packages feed

futhark 0.16.4 → 0.17.1

raw patch · 223 files changed

+61586/−49496 lines, 223 filesdep +sexp-grammardep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: sexp-grammar

Dependency ranges changed: base

API changes (from Hackage documentation)

- Futhark.Analysis.PrimExp: instance Text.PrettyPrint.Mainland.Class.Pretty v => Futhark.Util.IntegralExp.IntegralExp (Futhark.Analysis.PrimExp.PrimExp v)
- Futhark.Analysis.PrimExp: instance Text.PrettyPrint.Mainland.Class.Pretty v => GHC.Num.Num (Futhark.Analysis.PrimExp.PrimExp v)
- Futhark.Analysis.PrimExp: instance Text.PrettyPrint.Mainland.Class.Pretty v => GHC.Real.Fractional (Futhark.Analysis.PrimExp.PrimExp v)
- Futhark.CodeGen.ImpGen: dPrimVol_ :: VName -> PrimType -> ImpM lore r op ()
- Futhark.Construct: eNot :: MonadBinder m => m (Exp (Lore m)) -> m (Exp (Lore m))
- Futhark.IR.Prop.Constants: instance Futhark.IR.Prop.Constants.IsValue GHC.Types.Int
- Futhark.Internalise.Defunctorise: instance Control.Monad.Writer.Class.MonadWriter (Data.DList.DList Language.Futhark.Dec) Futhark.Internalise.Defunctorise.TransformM
- Futhark.Util.IntegralExp: fromInt16 :: IntegralExp e => Int16 -> e
- Futhark.Util.IntegralExp: fromInt32 :: IntegralExp e => Int32 -> e
- Futhark.Util.IntegralExp: fromInt64 :: IntegralExp e => Int64 -> e
- Futhark.Util.IntegralExp: fromInt8 :: IntegralExp e => Int8 -> e
- Language.Futhark.Pretty: instance Text.PrettyPrint.Mainland.Class.Pretty (Language.Futhark.Syntax.ShapeDecl GHC.Int.Int32)
- Language.Futhark.TypeChecker.Monad: anyIntType :: [PrimType]
+ Futhark.Actions: sexpAction :: ASTLore lore => Action lore
+ Futhark.Analysis.PrimExp: TPrimExp :: PrimExp v -> TPrimExp t v
+ Futhark.Analysis.PrimExp: [untyped] :: TPrimExp t v -> PrimExp v
+ Futhark.Analysis.PrimExp: bNot :: TPrimExp Bool v -> TPrimExp Bool v
+ Futhark.Analysis.PrimExp: class NumExp t => FloatExp t
+ Futhark.Analysis.PrimExp: class NumExp t => IntExp t
+ Futhark.Analysis.PrimExp: class NumExp t
+ Futhark.Analysis.PrimExp: fMax64 :: TPrimExp Double v -> TPrimExp Double v -> TPrimExp Double v
+ Futhark.Analysis.PrimExp: fMin64 :: TPrimExp Double v -> TPrimExp Double v -> TPrimExp Double v
+ Futhark.Analysis.PrimExp: fromInteger' :: NumExp t => Integer -> TPrimExp t v
+ Futhark.Analysis.PrimExp: fromRational' :: FloatExp t => Rational -> TPrimExp t v
+ Futhark.Analysis.PrimExp: instance (Futhark.Analysis.PrimExp.FloatExp t, Text.PrettyPrint.Mainland.Class.Pretty v) => GHC.Real.Fractional (Futhark.Analysis.PrimExp.TPrimExp t v)
+ Futhark.Analysis.PrimExp: instance (Futhark.Analysis.PrimExp.IntExp t, Text.PrettyPrint.Mainland.Class.Pretty v) => Futhark.Util.IntegralExp.IntegralExp (Futhark.Analysis.PrimExp.TPrimExp t v)
+ Futhark.Analysis.PrimExp: instance (Futhark.Analysis.PrimExp.NumExp t, Text.PrettyPrint.Mainland.Class.Pretty v) => GHC.Num.Num (Futhark.Analysis.PrimExp.TPrimExp t v)
+ Futhark.Analysis.PrimExp: instance Data.Foldable.Foldable (Futhark.Analysis.PrimExp.TPrimExp t)
+ Futhark.Analysis.PrimExp: instance Data.Traversable.Traversable (Futhark.Analysis.PrimExp.TPrimExp t)
+ Futhark.Analysis.PrimExp: instance Futhark.Analysis.PrimExp.FloatExp GHC.Types.Double
+ Futhark.Analysis.PrimExp: instance Futhark.Analysis.PrimExp.FloatExp GHC.Types.Float
+ Futhark.Analysis.PrimExp: instance Futhark.Analysis.PrimExp.IntExp GHC.Int.Int16
+ Futhark.Analysis.PrimExp: instance Futhark.Analysis.PrimExp.IntExp GHC.Int.Int32
+ Futhark.Analysis.PrimExp: instance Futhark.Analysis.PrimExp.IntExp GHC.Int.Int64
+ Futhark.Analysis.PrimExp: instance Futhark.Analysis.PrimExp.IntExp GHC.Int.Int8
+ Futhark.Analysis.PrimExp: instance Futhark.Analysis.PrimExp.NumExp GHC.Int.Int16
+ Futhark.Analysis.PrimExp: instance Futhark.Analysis.PrimExp.NumExp GHC.Int.Int32
+ Futhark.Analysis.PrimExp: instance Futhark.Analysis.PrimExp.NumExp GHC.Int.Int64
+ Futhark.Analysis.PrimExp: instance Futhark.Analysis.PrimExp.NumExp GHC.Int.Int8
+ Futhark.Analysis.PrimExp: instance Futhark.Analysis.PrimExp.NumExp GHC.Types.Double
+ Futhark.Analysis.PrimExp: instance Futhark.Analysis.PrimExp.NumExp GHC.Types.Float
+ Futhark.Analysis.PrimExp: instance Futhark.IR.Prop.Names.FreeIn v => Futhark.IR.Prop.Names.FreeIn (Futhark.Analysis.PrimExp.TPrimExp t v)
+ Futhark.Analysis.PrimExp: instance GHC.Base.Functor (Futhark.Analysis.PrimExp.TPrimExp t)
+ Futhark.Analysis.PrimExp: instance GHC.Classes.Eq v => GHC.Classes.Eq (Futhark.Analysis.PrimExp.TPrimExp t v)
+ Futhark.Analysis.PrimExp: instance GHC.Classes.Ord v => GHC.Classes.Ord (Futhark.Analysis.PrimExp.TPrimExp t v)
+ Futhark.Analysis.PrimExp: instance GHC.Generics.Generic (Futhark.Analysis.PrimExp.PrimExp v)
+ Futhark.Analysis.PrimExp: instance GHC.Generics.Generic (Futhark.Analysis.PrimExp.TPrimExp t v)
+ Futhark.Analysis.PrimExp: instance GHC.Show.Show v => GHC.Show.Show (Futhark.Analysis.PrimExp.TPrimExp t v)
+ Futhark.Analysis.PrimExp: instance Language.SexpGrammar.Class.SexpIso v => Language.SexpGrammar.Class.SexpIso (Futhark.Analysis.PrimExp.PrimExp v)
+ Futhark.Analysis.PrimExp: instance Language.SexpGrammar.Class.SexpIso v => Language.SexpGrammar.Class.SexpIso (Futhark.Analysis.PrimExp.TPrimExp t v)
+ Futhark.Analysis.PrimExp: instance Text.PrettyPrint.Mainland.Class.Pretty v => Text.PrettyPrint.Mainland.Class.Pretty (Futhark.Analysis.PrimExp.TPrimExp t v)
+ Futhark.Analysis.PrimExp: isBool :: PrimExp v -> TPrimExp Bool v
+ Futhark.Analysis.PrimExp: isF32 :: PrimExp v -> TPrimExp Float v
+ Futhark.Analysis.PrimExp: isF64 :: PrimExp v -> TPrimExp Double v
+ Futhark.Analysis.PrimExp: isInt16 :: PrimExp v -> TPrimExp Int16 v
+ Futhark.Analysis.PrimExp: isInt32 :: PrimExp v -> TPrimExp Int32 v
+ Futhark.Analysis.PrimExp: isInt64 :: PrimExp v -> TPrimExp Int64 v
+ Futhark.Analysis.PrimExp: isInt8 :: PrimExp v -> TPrimExp Int8 v
+ Futhark.Analysis.PrimExp: newtype TPrimExp t v
+ Futhark.Analysis.PrimExp: sExt32 :: IntExp t => TPrimExp t v -> TPrimExp Int32 v
+ Futhark.Analysis.PrimExp: sExt64 :: IntExp t => TPrimExp t v -> TPrimExp Int64 v
+ Futhark.Analysis.PrimExp: sMax32 :: TPrimExp Int32 v -> TPrimExp Int32 v -> TPrimExp Int32 v
+ Futhark.Analysis.PrimExp: sMax64 :: TPrimExp Int64 v -> TPrimExp Int64 v -> TPrimExp Int64 v
+ Futhark.Analysis.PrimExp: sMin32 :: TPrimExp Int32 v -> TPrimExp Int32 v -> TPrimExp Int32 v
+ Futhark.Analysis.PrimExp: sMin64 :: TPrimExp Int64 v -> TPrimExp Int64 v -> TPrimExp Int64 v
+ Futhark.Analysis.PrimExp: zExt32 :: IntExp t => TPrimExp t v -> TPrimExp Int32 v
+ Futhark.Analysis.PrimExp: zExt64 :: IntExp t => TPrimExp t v -> TPrimExp Int64 v
+ Futhark.Analysis.PrimExp.Convert: instance Futhark.Construct.ToExp v => Futhark.Construct.ToExp (Futhark.Analysis.PrimExp.TPrimExp t v)
+ Futhark.Analysis.PrimExp.Convert: le32 :: a -> TPrimExp Int32 a
+ Futhark.Analysis.PrimExp.Convert: le64 :: a -> TPrimExp Int64 a
+ Futhark.Analysis.PrimExp.Convert: pe32 :: SubExp -> TPrimExp Int32 VName
+ Futhark.Analysis.PrimExp.Convert: pe64 :: SubExp -> TPrimExp Int64 VName
+ Futhark.Analysis.SymbolTable: insertLoopMerge :: ASTLore lore => [(FParam lore, SubExp, SubExp)] -> SymbolTable lore -> SymbolTable lore
+ Futhark.Analysis.SymbolTable: lookupLoopParam :: VName -> SymbolTable lore -> Maybe (SubExp, SubExp)
+ Futhark.CodeGen.Backends.GenericC: [opsCritical] :: Operations op s -> ([BlockItem], [BlockItem])
+ Futhark.CodeGen.Backends.GenericC.Options: [optionDescription] :: Option -> String
+ Futhark.CodeGen.ImpCode: ErrorInt64 :: a -> ErrorMsgPart a
+ Futhark.CodeGen.ImpCode: type TExp t = TPrimExp t ExpLeaf
+ Futhark.CodeGen.ImpCode: vi64 :: VName -> TExp Int64
+ Futhark.CodeGen.ImpCode.Kernels: ErrorInt64 :: a -> ErrorMsgPart a
+ Futhark.CodeGen.ImpCode.Kernels: TPrimExp :: PrimExp v -> TPrimExp t v
+ Futhark.CodeGen.ImpCode.Kernels: [untyped] :: TPrimExp t v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Kernels: bNot :: TPrimExp Bool v -> TPrimExp Bool v
+ Futhark.CodeGen.ImpCode.Kernels: class NumExp t => FloatExp t
+ Futhark.CodeGen.ImpCode.Kernels: class NumExp t => IntExp t
+ Futhark.CodeGen.ImpCode.Kernels: class NumExp t
+ Futhark.CodeGen.ImpCode.Kernels: fMax64 :: TPrimExp Double v -> TPrimExp Double v -> TPrimExp Double v
+ Futhark.CodeGen.ImpCode.Kernels: fMin64 :: TPrimExp Double v -> TPrimExp Double v -> TPrimExp Double v
+ Futhark.CodeGen.ImpCode.Kernels: fromInteger' :: NumExp t => Integer -> TPrimExp t v
+ Futhark.CodeGen.ImpCode.Kernels: fromRational' :: FloatExp t => Rational -> TPrimExp t v
+ Futhark.CodeGen.ImpCode.Kernels: isBool :: PrimExp v -> TPrimExp Bool v
+ Futhark.CodeGen.ImpCode.Kernels: isF32 :: PrimExp v -> TPrimExp Float v
+ Futhark.CodeGen.ImpCode.Kernels: isF64 :: PrimExp v -> TPrimExp Double v
+ Futhark.CodeGen.ImpCode.Kernels: isInt16 :: PrimExp v -> TPrimExp Int16 v
+ Futhark.CodeGen.ImpCode.Kernels: isInt32 :: PrimExp v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.Kernels: isInt64 :: PrimExp v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpCode.Kernels: isInt8 :: PrimExp v -> TPrimExp Int8 v
+ Futhark.CodeGen.ImpCode.Kernels: newtype TPrimExp t v
+ Futhark.CodeGen.ImpCode.Kernels: pattern If :: () => TExp Bool -> Code a -> Code a -> Code a
+ Futhark.CodeGen.ImpCode.Kernels: sExt32 :: IntExp t => TPrimExp t v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.Kernels: sExt64 :: IntExp t => TPrimExp t v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpCode.Kernels: sMax32 :: TPrimExp Int32 v -> TPrimExp Int32 v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.Kernels: sMax64 :: TPrimExp Int64 v -> TPrimExp Int64 v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpCode.Kernels: sMin32 :: TPrimExp Int32 v -> TPrimExp Int32 v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.Kernels: sMin64 :: TPrimExp Int64 v -> TPrimExp Int64 v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpCode.Kernels: type TExp t = TPrimExp t ExpLeaf
+ Futhark.CodeGen.ImpCode.Kernels: vi64 :: VName -> TExp Int64
+ Futhark.CodeGen.ImpCode.Kernels: zExt32 :: IntExp t => TPrimExp t v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.Kernels: zExt64 :: IntExp t => TPrimExp t v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpCode.OpenCL: ErrorInt64 :: a -> ErrorMsgPart a
+ Futhark.CodeGen.ImpCode.OpenCL: TPrimExp :: PrimExp v -> TPrimExp t v
+ Futhark.CodeGen.ImpCode.OpenCL: [untyped] :: TPrimExp t v -> PrimExp v
+ Futhark.CodeGen.ImpCode.OpenCL: bNot :: TPrimExp Bool v -> TPrimExp Bool v
+ Futhark.CodeGen.ImpCode.OpenCL: class NumExp t => FloatExp t
+ Futhark.CodeGen.ImpCode.OpenCL: class NumExp t => IntExp t
+ Futhark.CodeGen.ImpCode.OpenCL: class NumExp t
+ Futhark.CodeGen.ImpCode.OpenCL: fMax64 :: TPrimExp Double v -> TPrimExp Double v -> TPrimExp Double v
+ Futhark.CodeGen.ImpCode.OpenCL: fMin64 :: TPrimExp Double v -> TPrimExp Double v -> TPrimExp Double v
+ Futhark.CodeGen.ImpCode.OpenCL: fromInteger' :: NumExp t => Integer -> TPrimExp t v
+ Futhark.CodeGen.ImpCode.OpenCL: fromRational' :: FloatExp t => Rational -> TPrimExp t v
+ Futhark.CodeGen.ImpCode.OpenCL: isBool :: PrimExp v -> TPrimExp Bool v
+ Futhark.CodeGen.ImpCode.OpenCL: isF32 :: PrimExp v -> TPrimExp Float v
+ Futhark.CodeGen.ImpCode.OpenCL: isF64 :: PrimExp v -> TPrimExp Double v
+ Futhark.CodeGen.ImpCode.OpenCL: isInt16 :: PrimExp v -> TPrimExp Int16 v
+ Futhark.CodeGen.ImpCode.OpenCL: isInt32 :: PrimExp v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.OpenCL: isInt64 :: PrimExp v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpCode.OpenCL: isInt8 :: PrimExp v -> TPrimExp Int8 v
+ Futhark.CodeGen.ImpCode.OpenCL: newtype TPrimExp t v
+ Futhark.CodeGen.ImpCode.OpenCL: pattern If :: () => TExp Bool -> Code a -> Code a -> Code a
+ Futhark.CodeGen.ImpCode.OpenCL: sExt32 :: IntExp t => TPrimExp t v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.OpenCL: sExt64 :: IntExp t => TPrimExp t v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpCode.OpenCL: sMax32 :: TPrimExp Int32 v -> TPrimExp Int32 v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.OpenCL: sMax64 :: TPrimExp Int64 v -> TPrimExp Int64 v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpCode.OpenCL: sMin32 :: TPrimExp Int32 v -> TPrimExp Int32 v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.OpenCL: sMin64 :: TPrimExp Int64 v -> TPrimExp Int64 v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpCode.OpenCL: type TExp t = TPrimExp t ExpLeaf
+ Futhark.CodeGen.ImpCode.OpenCL: vi64 :: VName -> TExp Int64
+ Futhark.CodeGen.ImpCode.OpenCL: zExt32 :: IntExp t => TPrimExp t v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.OpenCL: zExt64 :: IntExp t => TPrimExp t v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpCode.Sequential: ErrorInt64 :: a -> ErrorMsgPart a
+ Futhark.CodeGen.ImpCode.Sequential: TPrimExp :: PrimExp v -> TPrimExp t v
+ Futhark.CodeGen.ImpCode.Sequential: [untyped] :: TPrimExp t v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Sequential: bNot :: TPrimExp Bool v -> TPrimExp Bool v
+ Futhark.CodeGen.ImpCode.Sequential: class NumExp t => FloatExp t
+ Futhark.CodeGen.ImpCode.Sequential: class NumExp t => IntExp t
+ Futhark.CodeGen.ImpCode.Sequential: class NumExp t
+ Futhark.CodeGen.ImpCode.Sequential: fMax64 :: TPrimExp Double v -> TPrimExp Double v -> TPrimExp Double v
+ Futhark.CodeGen.ImpCode.Sequential: fMin64 :: TPrimExp Double v -> TPrimExp Double v -> TPrimExp Double v
+ Futhark.CodeGen.ImpCode.Sequential: fromInteger' :: NumExp t => Integer -> TPrimExp t v
+ Futhark.CodeGen.ImpCode.Sequential: fromRational' :: FloatExp t => Rational -> TPrimExp t v
+ Futhark.CodeGen.ImpCode.Sequential: isBool :: PrimExp v -> TPrimExp Bool v
+ Futhark.CodeGen.ImpCode.Sequential: isF32 :: PrimExp v -> TPrimExp Float v
+ Futhark.CodeGen.ImpCode.Sequential: isF64 :: PrimExp v -> TPrimExp Double v
+ Futhark.CodeGen.ImpCode.Sequential: isInt16 :: PrimExp v -> TPrimExp Int16 v
+ Futhark.CodeGen.ImpCode.Sequential: isInt32 :: PrimExp v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.Sequential: isInt64 :: PrimExp v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpCode.Sequential: isInt8 :: PrimExp v -> TPrimExp Int8 v
+ Futhark.CodeGen.ImpCode.Sequential: newtype TPrimExp t v
+ Futhark.CodeGen.ImpCode.Sequential: pattern If :: () => TExp Bool -> Code a -> Code a -> Code a
+ Futhark.CodeGen.ImpCode.Sequential: sExt32 :: IntExp t => TPrimExp t v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.Sequential: sExt64 :: IntExp t => TPrimExp t v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpCode.Sequential: sMax32 :: TPrimExp Int32 v -> TPrimExp Int32 v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.Sequential: sMax64 :: TPrimExp Int64 v -> TPrimExp Int64 v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpCode.Sequential: sMin32 :: TPrimExp Int32 v -> TPrimExp Int32 v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.Sequential: sMin64 :: TPrimExp Int64 v -> TPrimExp Int64 v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpCode.Sequential: type TExp t = TPrimExp t ExpLeaf
+ Futhark.CodeGen.ImpCode.Sequential: vi64 :: VName -> TExp Int64
+ Futhark.CodeGen.ImpCode.Sequential: zExt32 :: IntExp t => TPrimExp t v -> TPrimExp Int32 v
+ Futhark.CodeGen.ImpCode.Sequential: zExt64 :: IntExp t => TPrimExp t v -> TPrimExp Int64 v
+ Futhark.CodeGen.ImpGen: (<~~) :: VName -> Exp -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: dPrimVol :: String -> PrimType -> TExp t -> ImpM lore r op (TV t)
+ Futhark.CodeGen.ImpGen: data TV t
+ Futhark.CodeGen.ImpGen: isMapTransposeCopy :: PrimType -> MemLocation -> Slice (TExp Int64) -> MemLocation -> Slice (TExp Int64) -> Maybe (TExp Int64, TExp Int64, TExp Int64, TExp Int64, TExp Int64)
+ Futhark.CodeGen.ImpGen: mkTV :: VName -> PrimType -> TV t
+ Futhark.CodeGen.ImpGen: toBoolExp :: ToExp a => a -> TExp Bool
+ Futhark.CodeGen.ImpGen: toInt32Exp :: ToExp a => a -> TExp Int32
+ Futhark.CodeGen.ImpGen: toInt64Exp :: ToExp a => a -> TExp Int64
+ Futhark.CodeGen.ImpGen: tvExp :: TV t -> TExp t
+ Futhark.CodeGen.ImpGen: tvSize :: TV t -> DimSize
+ Futhark.CodeGen.ImpGen: tvVar :: TV t -> VName
+ Futhark.CodeGen.ImpGen.Transpose: mapTransposeFunction :: Name -> PrimType -> Function op
+ Futhark.CodeGen.ImpGen.Transpose: transposeArgs :: PrimType -> VName -> Count Bytes (TExp Int64) -> VName -> Count Bytes (TExp Int64) -> TExp Int64 -> TExp Int64 -> TExp Int64 -> [Arg]
+ Futhark.IR.Aliases: instance GHC.Generics.Generic Futhark.IR.Aliases.AliasDec
+ Futhark.IR.Aliases: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Aliases.AliasDec
+ Futhark.IR.Kernels.Kernel: instance (Language.SexpGrammar.Class.SexpIso op, Futhark.IR.Decorations.Decorations lore) => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Kernels.Kernel.HostOp lore op)
+ Futhark.IR.Kernels.Kernel: instance GHC.Generics.Generic (Futhark.IR.Kernels.Kernel.HostOp lore op)
+ Futhark.IR.Kernels.Kernel: instance GHC.Generics.Generic Futhark.IR.Kernels.Kernel.SegLevel
+ Futhark.IR.Kernels.Kernel: instance GHC.Generics.Generic Futhark.IR.Kernels.Kernel.SizeOp
+ Futhark.IR.Kernels.Kernel: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Kernels.Kernel.SegLevel
+ Futhark.IR.Kernels.Kernel: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Kernels.Kernel.SizeOp
+ Futhark.IR.Kernels.Sizes: instance GHC.Generics.Generic (Futhark.IR.Kernels.Sizes.Count u e)
+ Futhark.IR.Kernels.Sizes: instance GHC.Generics.Generic Futhark.IR.Kernels.Sizes.SizeClass
+ Futhark.IR.Kernels.Sizes: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Kernels.Sizes.SizeClass
+ Futhark.IR.Kernels.Sizes: instance Language.SexpGrammar.Class.SexpIso e => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Kernels.Sizes.Count u e)
+ Futhark.IR.Mem: instance (Language.SexpGrammar.Class.SexpIso d, Language.SexpGrammar.Class.SexpIso u, Language.SexpGrammar.Class.SexpIso ret) => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Mem.MemInfo d u ret)
+ Futhark.IR.Mem: instance GHC.Generics.Generic (Futhark.IR.Mem.MemInfo d u ret)
+ Futhark.IR.Mem: instance GHC.Generics.Generic (Futhark.IR.Mem.MemOp inner)
+ Futhark.IR.Mem: instance GHC.Generics.Generic Futhark.IR.Mem.MemBind
+ Futhark.IR.Mem: instance GHC.Generics.Generic Futhark.IR.Mem.MemReturn
+ Futhark.IR.Mem: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Mem.MemBind
+ Futhark.IR.Mem: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Mem.MemReturn
+ Futhark.IR.Mem: instance Language.SexpGrammar.Class.SexpIso inner => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Mem.MemOp inner)
+ Futhark.IR.Mem.IxFun: instance GHC.Generics.Generic (Futhark.IR.Mem.IxFun.IxFun num)
+ Futhark.IR.Mem.IxFun: instance GHC.Generics.Generic (Futhark.IR.Mem.IxFun.LMAD num)
+ Futhark.IR.Mem.IxFun: instance GHC.Generics.Generic (Futhark.IR.Mem.IxFun.LMADDim num)
+ Futhark.IR.Mem.IxFun: instance GHC.Generics.Generic Futhark.IR.Mem.IxFun.Monotonicity
+ Futhark.IR.Mem.IxFun: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Mem.IxFun.Monotonicity
+ Futhark.IR.Mem.IxFun: instance Language.SexpGrammar.Class.SexpIso num => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Mem.IxFun.IxFun num)
+ Futhark.IR.Mem.IxFun: instance Language.SexpGrammar.Class.SexpIso num => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Mem.IxFun.LMAD num)
+ Futhark.IR.Mem.IxFun: instance Language.SexpGrammar.Class.SexpIso num => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Mem.IxFun.LMADDim num)
+ Futhark.IR.Primitive: data Int16
+ Futhark.IR.Primitive: data Int32
+ Futhark.IR.Primitive: data Int64
+ Futhark.IR.Primitive: data Int8
+ Futhark.IR.Primitive: instance GHC.Generics.Generic Futhark.IR.Primitive.BinOp
+ Futhark.IR.Primitive: instance GHC.Generics.Generic Futhark.IR.Primitive.CmpOp
+ Futhark.IR.Primitive: instance GHC.Generics.Generic Futhark.IR.Primitive.ConvOp
+ Futhark.IR.Primitive: instance GHC.Generics.Generic Futhark.IR.Primitive.FloatType
+ Futhark.IR.Primitive: instance GHC.Generics.Generic Futhark.IR.Primitive.FloatValue
+ Futhark.IR.Primitive: instance GHC.Generics.Generic Futhark.IR.Primitive.IntType
+ Futhark.IR.Primitive: instance GHC.Generics.Generic Futhark.IR.Primitive.IntValue
+ Futhark.IR.Primitive: instance GHC.Generics.Generic Futhark.IR.Primitive.Overflow
+ Futhark.IR.Primitive: instance GHC.Generics.Generic Futhark.IR.Primitive.PrimType
+ Futhark.IR.Primitive: instance GHC.Generics.Generic Futhark.IR.Primitive.PrimValue
+ Futhark.IR.Primitive: instance GHC.Generics.Generic Futhark.IR.Primitive.Safety
+ Futhark.IR.Primitive: instance GHC.Generics.Generic Futhark.IR.Primitive.UnOp
+ Futhark.IR.Primitive: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Primitive.BinOp
+ Futhark.IR.Primitive: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Primitive.CmpOp
+ Futhark.IR.Primitive: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Primitive.ConvOp
+ Futhark.IR.Primitive: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Primitive.FloatType
+ Futhark.IR.Primitive: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Primitive.FloatValue
+ Futhark.IR.Primitive: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Primitive.IntType
+ Futhark.IR.Primitive: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Primitive.IntValue
+ Futhark.IR.Primitive: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Primitive.Overflow
+ Futhark.IR.Primitive: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Primitive.PrimType
+ Futhark.IR.Primitive: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Primitive.PrimValue
+ Futhark.IR.Primitive: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Primitive.Safety
+ Futhark.IR.Primitive: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Primitive.UnOp
+ Futhark.IR.Prop.Names: instance GHC.Generics.Generic Futhark.IR.Prop.Names.Names
+ Futhark.IR.Prop.Names: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Prop.Names.Names
+ Futhark.IR.SOACS: ErrorInt64 :: a -> ErrorMsgPart a
+ Futhark.IR.SOACS.SOAC: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.SOACS.SOAC.HistOp lore)
+ Futhark.IR.SOACS.SOAC: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.SOACS.SOAC.Reduce lore)
+ Futhark.IR.SOACS.SOAC: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.SOACS.SOAC.SOAC lore)
+ Futhark.IR.SOACS.SOAC: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.SOACS.SOAC.Scan lore)
+ Futhark.IR.SOACS.SOAC: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.SOACS.SOAC.ScremaForm lore)
+ Futhark.IR.SOACS.SOAC: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.SOACS.SOAC.StreamForm lore)
+ Futhark.IR.SOACS.SOAC: instance GHC.Generics.Generic (Futhark.IR.SOACS.SOAC.HistOp lore)
+ Futhark.IR.SOACS.SOAC: instance GHC.Generics.Generic (Futhark.IR.SOACS.SOAC.Reduce lore)
+ Futhark.IR.SOACS.SOAC: instance GHC.Generics.Generic (Futhark.IR.SOACS.SOAC.SOAC lore)
+ Futhark.IR.SOACS.SOAC: instance GHC.Generics.Generic (Futhark.IR.SOACS.SOAC.Scan lore)
+ Futhark.IR.SOACS.SOAC: instance GHC.Generics.Generic (Futhark.IR.SOACS.SOAC.ScremaForm lore)
+ Futhark.IR.SOACS.SOAC: instance GHC.Generics.Generic (Futhark.IR.SOACS.SOAC.StreamForm lore)
+ Futhark.IR.SOACS.SOAC: instance GHC.Generics.Generic Futhark.IR.SOACS.SOAC.StreamOrd
+ Futhark.IR.SOACS.SOAC: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.SOACS.SOAC.StreamOrd
+ Futhark.IR.SegOp: instance (Language.SexpGrammar.Class.SexpIso lvl, Futhark.IR.Decorations.Decorations lore) => Language.SexpGrammar.Class.SexpIso (Futhark.IR.SegOp.SegOp lvl lore)
+ Futhark.IR.SegOp: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.SegOp.HistOp lore)
+ Futhark.IR.SegOp: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.SegOp.KernelBody lore)
+ Futhark.IR.SegOp: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.SegOp.SegBinOp lore)
+ Futhark.IR.SegOp: instance GHC.Generics.Generic (Futhark.IR.SegOp.HistOp lore)
+ Futhark.IR.SegOp: instance GHC.Generics.Generic (Futhark.IR.SegOp.KernelBody lore)
+ Futhark.IR.SegOp: instance GHC.Generics.Generic (Futhark.IR.SegOp.SegBinOp lore)
+ Futhark.IR.SegOp: instance GHC.Generics.Generic (Futhark.IR.SegOp.SegOp lvl lore)
+ Futhark.IR.SegOp: instance GHC.Generics.Generic Futhark.IR.SegOp.KernelResult
+ Futhark.IR.SegOp: instance GHC.Generics.Generic Futhark.IR.SegOp.ResultManifest
+ Futhark.IR.SegOp: instance GHC.Generics.Generic Futhark.IR.SegOp.SegSpace
+ Futhark.IR.SegOp: instance GHC.Generics.Generic Futhark.IR.SegOp.SegVirt
+ Futhark.IR.SegOp: instance GHC.Generics.Generic Futhark.IR.SegOp.SplitOrdering
+ Futhark.IR.SegOp: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.SegOp.KernelResult
+ Futhark.IR.SegOp: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.SegOp.ResultManifest
+ Futhark.IR.SegOp: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.SegOp.SegSpace
+ Futhark.IR.SegOp: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.SegOp.SegVirt
+ Futhark.IR.SegOp: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.SegOp.SplitOrdering
+ Futhark.IR.Syntax: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.BodyT lore)
+ Futhark.IR.Syntax: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.ExpT lore)
+ Futhark.IR.Syntax: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.FunDef lore)
+ Futhark.IR.Syntax: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.LambdaT lore)
+ Futhark.IR.Syntax: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.LoopForm lore)
+ Futhark.IR.Syntax: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.Prog lore)
+ Futhark.IR.Syntax: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.Stm lore)
+ Futhark.IR.Syntax: instance Futhark.IR.Decorations.Decorations lore => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.Stms lore)
+ Futhark.IR.Syntax: instance GHC.Generics.Generic (Futhark.IR.Syntax.BodyT lore)
+ Futhark.IR.Syntax: instance GHC.Generics.Generic (Futhark.IR.Syntax.DimChange d)
+ Futhark.IR.Syntax: instance GHC.Generics.Generic (Futhark.IR.Syntax.ExpT lore)
+ Futhark.IR.Syntax: instance GHC.Generics.Generic (Futhark.IR.Syntax.FunDef lore)
+ Futhark.IR.Syntax: instance GHC.Generics.Generic (Futhark.IR.Syntax.IfDec rt)
+ Futhark.IR.Syntax: instance GHC.Generics.Generic (Futhark.IR.Syntax.LambdaT lore)
+ Futhark.IR.Syntax: instance GHC.Generics.Generic (Futhark.IR.Syntax.LoopForm lore)
+ Futhark.IR.Syntax: instance GHC.Generics.Generic (Futhark.IR.Syntax.PatternT dec)
+ Futhark.IR.Syntax: instance GHC.Generics.Generic (Futhark.IR.Syntax.Prog lore)
+ Futhark.IR.Syntax: instance GHC.Generics.Generic (Futhark.IR.Syntax.Stm lore)
+ Futhark.IR.Syntax: instance GHC.Generics.Generic (Futhark.IR.Syntax.StmAux dec)
+ Futhark.IR.Syntax: instance GHC.Generics.Generic Futhark.IR.Syntax.Attr
+ Futhark.IR.Syntax: instance GHC.Generics.Generic Futhark.IR.Syntax.Attrs
+ Futhark.IR.Syntax: instance GHC.Generics.Generic Futhark.IR.Syntax.BasicOp
+ Futhark.IR.Syntax: instance GHC.Generics.Generic Futhark.IR.Syntax.EntryPointType
+ Futhark.IR.Syntax: instance GHC.Generics.Generic Futhark.IR.Syntax.IfSort
+ Futhark.IR.Syntax: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Syntax.Attr
+ Futhark.IR.Syntax: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Syntax.Attrs
+ Futhark.IR.Syntax: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Syntax.BasicOp
+ Futhark.IR.Syntax: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Syntax.EntryPointType
+ Futhark.IR.Syntax: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Syntax.IfSort
+ Futhark.IR.Syntax: instance Language.SexpGrammar.Class.SexpIso d => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.DimChange d)
+ Futhark.IR.Syntax: instance Language.SexpGrammar.Class.SexpIso dec => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.PatternT dec)
+ Futhark.IR.Syntax: instance Language.SexpGrammar.Class.SexpIso dec => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.StmAux dec)
+ Futhark.IR.Syntax: instance Language.SexpGrammar.Class.SexpIso rt => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.IfDec rt)
+ Futhark.IR.Syntax.Core: ErrorInt64 :: a -> ErrorMsgPart a
+ Futhark.IR.Syntax.Core: instance (Language.SexpGrammar.Class.SexpIso shape, Language.SexpGrammar.Class.SexpIso u) => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.Core.TypeBase shape u)
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic (Futhark.IR.Syntax.Core.DimIndex d)
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic (Futhark.IR.Syntax.Core.ErrorMsg a)
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic (Futhark.IR.Syntax.Core.ErrorMsgPart a)
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic (Futhark.IR.Syntax.Core.Ext a)
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic (Futhark.IR.Syntax.Core.Param dec)
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic (Futhark.IR.Syntax.Core.PatElemT dec)
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic (Futhark.IR.Syntax.Core.ShapeBase d)
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic (Futhark.IR.Syntax.Core.TypeBase shape u)
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic Futhark.IR.Syntax.Core.Certificates
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic Futhark.IR.Syntax.Core.Diet
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic Futhark.IR.Syntax.Core.Ident
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic Futhark.IR.Syntax.Core.NoUniqueness
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic Futhark.IR.Syntax.Core.Rank
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic Futhark.IR.Syntax.Core.Space
+ Futhark.IR.Syntax.Core: instance GHC.Generics.Generic Futhark.IR.Syntax.Core.SubExp
+ Futhark.IR.Syntax.Core: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Syntax.Core.Certificates
+ Futhark.IR.Syntax.Core: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Syntax.Core.Diet
+ Futhark.IR.Syntax.Core: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Syntax.Core.Ident
+ Futhark.IR.Syntax.Core: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Syntax.Core.NoUniqueness
+ Futhark.IR.Syntax.Core: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Syntax.Core.Rank
+ Futhark.IR.Syntax.Core: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Syntax.Core.Space
+ Futhark.IR.Syntax.Core: instance Language.SexpGrammar.Class.SexpIso Futhark.IR.Syntax.Core.SubExp
+ Futhark.IR.Syntax.Core: instance Language.SexpGrammar.Class.SexpIso a => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.Core.ErrorMsg a)
+ Futhark.IR.Syntax.Core: instance Language.SexpGrammar.Class.SexpIso a => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.Core.ErrorMsgPart a)
+ Futhark.IR.Syntax.Core: instance Language.SexpGrammar.Class.SexpIso a => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.Core.Ext a)
+ Futhark.IR.Syntax.Core: instance Language.SexpGrammar.Class.SexpIso d => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.Core.DimIndex d)
+ Futhark.IR.Syntax.Core: instance Language.SexpGrammar.Class.SexpIso d => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.Core.ShapeBase d)
+ Futhark.IR.Syntax.Core: instance Language.SexpGrammar.Class.SexpIso dec => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.Core.Param dec)
+ Futhark.IR.Syntax.Core: instance Language.SexpGrammar.Class.SexpIso dec => Language.SexpGrammar.Class.SexpIso (Futhark.IR.Syntax.Core.PatElemT dec)
+ Futhark.Internalise.Defunctorise: instance Control.Monad.Writer.Class.MonadWriter (Data.DList.Internal.DList Language.Futhark.Dec) Futhark.Internalise.Defunctorise.TransformM
+ Futhark.Optimise.Simplify.Lore: instance GHC.Generics.Generic Futhark.Optimise.Simplify.Lore.BodyWisdom
+ Futhark.Optimise.Simplify.Lore: instance GHC.Generics.Generic Futhark.Optimise.Simplify.Lore.ExpWisdom
+ Futhark.Optimise.Simplify.Lore: instance GHC.Generics.Generic Futhark.Optimise.Simplify.Lore.VarWisdom
+ Futhark.Optimise.Simplify.Lore: instance Language.SexpGrammar.Class.SexpIso Futhark.Optimise.Simplify.Lore.BodyWisdom
+ Futhark.Optimise.Simplify.Lore: instance Language.SexpGrammar.Class.SexpIso Futhark.Optimise.Simplify.Lore.ExpWisdom
+ Futhark.Optimise.Simplify.Lore: instance Language.SexpGrammar.Class.SexpIso Futhark.Optimise.Simplify.Lore.VarWisdom
+ Futhark.Transform.Substitute: instance Futhark.Transform.Substitute.Substitute v => Futhark.Transform.Substitute.Substitute (Futhark.Analysis.PrimExp.TPrimExp t v)
+ Language.Futhark.Core: instance GHC.Generics.Generic Language.Futhark.Core.Commutativity
+ Language.Futhark.Core: instance GHC.Generics.Generic Language.Futhark.Core.Name
+ Language.Futhark.Core: instance GHC.Generics.Generic Language.Futhark.Core.Uniqueness
+ Language.Futhark.Core: instance GHC.Generics.Generic Language.Futhark.Core.VName
+ Language.Futhark.Core: instance Language.SexpGrammar.Class.SexpIso Language.Futhark.Core.Commutativity
+ Language.Futhark.Core: instance Language.SexpGrammar.Class.SexpIso Language.Futhark.Core.Name
+ Language.Futhark.Core: instance Language.SexpGrammar.Class.SexpIso Language.Futhark.Core.Uniqueness
+ Language.Futhark.Core: instance Language.SexpGrammar.Class.SexpIso Language.Futhark.Core.VName
+ Language.Futhark.Pretty: instance Text.PrettyPrint.Mainland.Class.Pretty (Language.Futhark.Syntax.ShapeDecl GHC.Int.Int64)
- Futhark.Analysis.PrimExp: (.&&.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.Analysis.PrimExp: (.&&.) :: TPrimExp Bool v -> TPrimExp Bool v -> TPrimExp Bool v
- Futhark.Analysis.PrimExp: (.&.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.Analysis.PrimExp: (.&.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp t v
- Futhark.Analysis.PrimExp: (.<.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.Analysis.PrimExp: (.<.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.Analysis.PrimExp: (.<=.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.Analysis.PrimExp: (.<=.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.Analysis.PrimExp: (.==.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.Analysis.PrimExp: (.==.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.Analysis.PrimExp: (.>.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.Analysis.PrimExp: (.>.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.Analysis.PrimExp: (.>=.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.Analysis.PrimExp: (.>=.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.Analysis.PrimExp: (.^.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.Analysis.PrimExp: (.^.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp t v
- Futhark.Analysis.PrimExp: (.|.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.Analysis.PrimExp: (.|.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp t v
- Futhark.Analysis.PrimExp: (.||.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.Analysis.PrimExp: (.||.) :: TPrimExp Bool v -> TPrimExp Bool v -> TPrimExp Bool v
- Futhark.Analysis.PrimExp: false :: PrimExp v
+ Futhark.Analysis.PrimExp: false :: TPrimExp Bool v
- Futhark.Analysis.PrimExp: true :: PrimExp v
+ Futhark.Analysis.PrimExp: true :: TPrimExp Bool v
- Futhark.Analysis.PrimExp.Convert: primExpSlice :: Slice SubExp -> Slice (PrimExp VName)
+ Futhark.Analysis.PrimExp.Convert: primExpSlice :: Slice SubExp -> Slice (TPrimExp Int64 VName)
- Futhark.Analysis.PrimExp.Convert: subExpSlice :: MonadBinder m => Slice (PrimExp VName) -> m (Slice SubExp)
+ Futhark.Analysis.PrimExp.Convert: subExpSlice :: MonadBinder m => Slice (TPrimExp Int64 VName) -> m (Slice SubExp)
- Futhark.Analysis.SymbolTable: IndexedArray :: Certificates -> VName -> [PrimExp VName] -> Indexed
+ Futhark.Analysis.SymbolTable: IndexedArray :: Certificates -> VName -> [TPrimExp Int64 VName] -> Indexed
- Futhark.Analysis.SymbolTable: index' :: VName -> [PrimExp VName] -> SymbolTable lore -> Maybe Indexed
+ Futhark.Analysis.SymbolTable: index' :: VName -> [TPrimExp Int64 VName] -> SymbolTable lore -> Maybe Indexed
- Futhark.Analysis.SymbolTable: indexOp :: (IndexOp op, ASTLore lore, IndexOp (Op lore)) => SymbolTable lore -> Int -> op -> [PrimExp VName] -> Maybe Indexed
+ Futhark.Analysis.SymbolTable: indexOp :: (IndexOp op, ASTLore lore, IndexOp (Op lore)) => SymbolTable lore -> Int -> op -> [TPrimExp Int64 VName] -> Maybe Indexed
- Futhark.CodeGen.Backends.GenericC: Operations :: WriteScalar op s -> ReadScalar op s -> Allocate op s -> Deallocate op s -> Copy op s -> StaticArray op s -> MemoryType op s -> OpCompiler op s -> ErrorCompiler op s -> CallCompiler op s -> Bool -> Operations op s
+ Futhark.CodeGen.Backends.GenericC: Operations :: WriteScalar op s -> ReadScalar op s -> Allocate op s -> Deallocate op s -> Copy op s -> StaticArray op s -> MemoryType op s -> OpCompiler op s -> ErrorCompiler op s -> CallCompiler op s -> Bool -> ([BlockItem], [BlockItem]) -> Operations op s
- Futhark.CodeGen.Backends.GenericC.Options: Option :: String -> Maybe Char -> OptionArgument -> Stm -> Option
+ Futhark.CodeGen.Backends.GenericC.Options: Option :: String -> Maybe Char -> OptionArgument -> String -> Stm -> Option
- Futhark.CodeGen.ImpCode: Allocate :: VName -> Count Bytes Exp -> Space -> Code a
+ Futhark.CodeGen.ImpCode: Allocate :: VName -> Count Bytes (TExp Int64) -> Space -> Code a
- Futhark.CodeGen.ImpCode: Copy :: VName -> Count Bytes Exp -> Space -> VName -> Count Bytes Exp -> Space -> Count Bytes Exp -> Code a
+ Futhark.CodeGen.ImpCode: Copy :: VName -> Count Bytes (TExp Int64) -> Space -> VName -> Count Bytes (TExp Int64) -> Space -> Count Bytes (TExp Int64) -> Code a
- Futhark.CodeGen.ImpCode: For :: VName -> IntType -> Exp -> Code a -> Code a
+ Futhark.CodeGen.ImpCode: For :: VName -> Exp -> Code a -> Code a
- Futhark.CodeGen.ImpCode: If :: Exp -> Code a -> Code a -> Code a
+ Futhark.CodeGen.ImpCode: If :: TExp Bool -> Code a -> Code a -> Code a
- Futhark.CodeGen.ImpCode: Index :: VName -> Count Elements Exp -> PrimType -> Space -> Volatility -> ExpLeaf
+ Futhark.CodeGen.ImpCode: Index :: VName -> Count Elements (TExp Int64) -> PrimType -> Space -> Volatility -> ExpLeaf
- Futhark.CodeGen.ImpCode: While :: Exp -> Code a -> Code a
+ Futhark.CodeGen.ImpCode: While :: TExp Bool -> Code a -> Code a
- Futhark.CodeGen.ImpCode: Write :: VName -> Count Elements Exp -> PrimType -> Space -> Volatility -> Exp -> Code a
+ Futhark.CodeGen.ImpCode: Write :: VName -> Count Elements (TExp Int64) -> PrimType -> Space -> Volatility -> Exp -> Code a
- Futhark.CodeGen.ImpCode: bytes :: Exp -> Count Bytes Exp
+ Futhark.CodeGen.ImpCode: bytes :: a -> Count Bytes a
- Futhark.CodeGen.ImpCode: elements :: Exp -> Count Elements Exp
+ Futhark.CodeGen.ImpCode: elements :: a -> Count Elements a
- Futhark.CodeGen.ImpCode: index :: VName -> Count Elements Exp -> PrimType -> Space -> Volatility -> Exp
+ Futhark.CodeGen.ImpCode: index :: VName -> Count Elements (TExp Int64) -> PrimType -> Space -> Volatility -> Exp
- Futhark.CodeGen.ImpCode: vi32 :: VName -> Exp
+ Futhark.CodeGen.ImpCode: vi32 :: VName -> TExp Int32
- Futhark.CodeGen.ImpCode: withElemType :: Count Elements Exp -> PrimType -> Count Bytes Exp
+ Futhark.CodeGen.ImpCode: withElemType :: Count Elements (TExp Int64) -> PrimType -> Count Bytes (TExp Int64)
- Futhark.CodeGen.ImpCode.Kernels: (.&&.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Kernels: (.&&.) :: TPrimExp Bool v -> TPrimExp Bool v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Kernels: (.&.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Kernels: (.&.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp t v
- Futhark.CodeGen.ImpCode.Kernels: (.<.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Kernels: (.<.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Kernels: (.<=.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Kernels: (.<=.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Kernels: (.==.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Kernels: (.==.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Kernels: (.>.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Kernels: (.>.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Kernels: (.>=.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Kernels: (.>=.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Kernels: (.^.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Kernels: (.^.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp t v
- Futhark.CodeGen.ImpCode.Kernels: (.|.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Kernels: (.|.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp t v
- Futhark.CodeGen.ImpCode.Kernels: (.||.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Kernels: (.||.) :: TPrimExp Bool v -> TPrimExp Bool v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Kernels: AtomicAdd :: IntType -> VName -> VName -> Count Elements Exp -> Exp -> AtomicOp
+ Futhark.CodeGen.ImpCode.Kernels: AtomicAdd :: IntType -> VName -> VName -> Count Elements (TExp Int64) -> Exp -> AtomicOp
- Futhark.CodeGen.ImpCode.Kernels: AtomicAnd :: IntType -> VName -> VName -> Count Elements Exp -> Exp -> AtomicOp
+ Futhark.CodeGen.ImpCode.Kernels: AtomicAnd :: IntType -> VName -> VName -> Count Elements (TExp Int64) -> Exp -> AtomicOp
- Futhark.CodeGen.ImpCode.Kernels: AtomicCmpXchg :: PrimType -> VName -> VName -> Count Elements Exp -> Exp -> Exp -> AtomicOp
+ Futhark.CodeGen.ImpCode.Kernels: AtomicCmpXchg :: PrimType -> VName -> VName -> Count Elements (TExp Int64) -> Exp -> Exp -> AtomicOp
- Futhark.CodeGen.ImpCode.Kernels: AtomicFAdd :: FloatType -> VName -> VName -> Count Elements Exp -> Exp -> AtomicOp
+ Futhark.CodeGen.ImpCode.Kernels: AtomicFAdd :: FloatType -> VName -> VName -> Count Elements (TExp Int64) -> Exp -> AtomicOp
- Futhark.CodeGen.ImpCode.Kernels: AtomicOr :: IntType -> VName -> VName -> Count Elements Exp -> Exp -> AtomicOp
+ Futhark.CodeGen.ImpCode.Kernels: AtomicOr :: IntType -> VName -> VName -> Count Elements (TExp Int64) -> Exp -> AtomicOp
- Futhark.CodeGen.ImpCode.Kernels: AtomicSMax :: IntType -> VName -> VName -> Count Elements Exp -> Exp -> AtomicOp
+ Futhark.CodeGen.ImpCode.Kernels: AtomicSMax :: IntType -> VName -> VName -> Count Elements (TExp Int64) -> Exp -> AtomicOp
- Futhark.CodeGen.ImpCode.Kernels: AtomicSMin :: IntType -> VName -> VName -> Count Elements Exp -> Exp -> AtomicOp
+ Futhark.CodeGen.ImpCode.Kernels: AtomicSMin :: IntType -> VName -> VName -> Count Elements (TExp Int64) -> Exp -> AtomicOp
- Futhark.CodeGen.ImpCode.Kernels: AtomicUMax :: IntType -> VName -> VName -> Count Elements Exp -> Exp -> AtomicOp
+ Futhark.CodeGen.ImpCode.Kernels: AtomicUMax :: IntType -> VName -> VName -> Count Elements (TExp Int64) -> Exp -> AtomicOp
- Futhark.CodeGen.ImpCode.Kernels: AtomicUMin :: IntType -> VName -> VName -> Count Elements Exp -> Exp -> AtomicOp
+ Futhark.CodeGen.ImpCode.Kernels: AtomicUMin :: IntType -> VName -> VName -> Count Elements (TExp Int64) -> Exp -> AtomicOp
- Futhark.CodeGen.ImpCode.Kernels: AtomicXchg :: PrimType -> VName -> VName -> Count Elements Exp -> Exp -> AtomicOp
+ Futhark.CodeGen.ImpCode.Kernels: AtomicXchg :: PrimType -> VName -> VName -> Count Elements (TExp Int64) -> Exp -> AtomicOp
- Futhark.CodeGen.ImpCode.Kernels: AtomicXor :: IntType -> VName -> VName -> Count Elements Exp -> Exp -> AtomicOp
+ Futhark.CodeGen.ImpCode.Kernels: AtomicXor :: IntType -> VName -> VName -> Count Elements (TExp Int64) -> Exp -> AtomicOp
- Futhark.CodeGen.ImpCode.Kernels: Index :: VName -> Count Elements Exp -> PrimType -> Space -> Volatility -> ExpLeaf
+ Futhark.CodeGen.ImpCode.Kernels: Index :: VName -> Count Elements (TExp Int64) -> PrimType -> Space -> Volatility -> ExpLeaf
- Futhark.CodeGen.ImpCode.Kernels: LocalAlloc :: VName -> Count Bytes Exp -> KernelOp
+ Futhark.CodeGen.ImpCode.Kernels: LocalAlloc :: VName -> Count Bytes (TExp Int64) -> KernelOp
- Futhark.CodeGen.ImpCode.Kernels: bytes :: Exp -> Count Bytes Exp
+ Futhark.CodeGen.ImpCode.Kernels: bytes :: a -> Count Bytes a
- Futhark.CodeGen.ImpCode.Kernels: elements :: Exp -> Count Elements Exp
+ Futhark.CodeGen.ImpCode.Kernels: elements :: a -> Count Elements a
- Futhark.CodeGen.ImpCode.Kernels: false :: PrimExp v
+ Futhark.CodeGen.ImpCode.Kernels: false :: TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Kernels: index :: VName -> Count Elements Exp -> PrimType -> Space -> Volatility -> Exp
+ Futhark.CodeGen.ImpCode.Kernels: index :: VName -> Count Elements (TExp Int64) -> PrimType -> Space -> Volatility -> Exp
- Futhark.CodeGen.ImpCode.Kernels: pattern If :: () => Exp -> Code a -> Code a -> Code a
+ Futhark.CodeGen.ImpCode.Kernels: pattern Assert :: () => Exp -> ErrorMsg Exp -> (SrcLoc, [SrcLoc]) -> Code a
- Futhark.CodeGen.ImpCode.Kernels: pattern Write :: () => VName -> Count Elements Exp -> PrimType -> Space -> Volatility -> Exp -> Code a
+ Futhark.CodeGen.ImpCode.Kernels: pattern Write :: () => VName -> Count Elements (TExp Int64) -> PrimType -> Space -> Volatility -> Exp -> Code a
- Futhark.CodeGen.ImpCode.Kernels: true :: PrimExp v
+ Futhark.CodeGen.ImpCode.Kernels: true :: TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Kernels: vi32 :: VName -> Exp
+ Futhark.CodeGen.ImpCode.Kernels: vi32 :: VName -> TExp Int32
- Futhark.CodeGen.ImpCode.Kernels: withElemType :: Count Elements Exp -> PrimType -> Count Bytes Exp
+ Futhark.CodeGen.ImpCode.Kernels: withElemType :: Count Elements (TExp Int64) -> PrimType -> Count Bytes (TExp Int64)
- Futhark.CodeGen.ImpCode.OpenCL: (.&&.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.OpenCL: (.&&.) :: TPrimExp Bool v -> TPrimExp Bool v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.OpenCL: (.&.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.OpenCL: (.&.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp t v
- Futhark.CodeGen.ImpCode.OpenCL: (.<.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.OpenCL: (.<.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.OpenCL: (.<=.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.OpenCL: (.<=.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.OpenCL: (.==.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.OpenCL: (.==.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.OpenCL: (.>.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.OpenCL: (.>.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.OpenCL: (.>=.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.OpenCL: (.>=.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.OpenCL: (.^.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.OpenCL: (.^.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp t v
- Futhark.CodeGen.ImpCode.OpenCL: (.|.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.OpenCL: (.|.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp t v
- Futhark.CodeGen.ImpCode.OpenCL: (.||.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.OpenCL: (.||.) :: TPrimExp Bool v -> TPrimExp Bool v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.OpenCL: Index :: VName -> Count Elements Exp -> PrimType -> Space -> Volatility -> ExpLeaf
+ Futhark.CodeGen.ImpCode.OpenCL: Index :: VName -> Count Elements (TExp Int64) -> PrimType -> Space -> Volatility -> ExpLeaf
- Futhark.CodeGen.ImpCode.OpenCL: bytes :: Exp -> Count Bytes Exp
+ Futhark.CodeGen.ImpCode.OpenCL: bytes :: a -> Count Bytes a
- Futhark.CodeGen.ImpCode.OpenCL: elements :: Exp -> Count Elements Exp
+ Futhark.CodeGen.ImpCode.OpenCL: elements :: a -> Count Elements a
- Futhark.CodeGen.ImpCode.OpenCL: false :: PrimExp v
+ Futhark.CodeGen.ImpCode.OpenCL: false :: TPrimExp Bool v
- Futhark.CodeGen.ImpCode.OpenCL: index :: VName -> Count Elements Exp -> PrimType -> Space -> Volatility -> Exp
+ Futhark.CodeGen.ImpCode.OpenCL: index :: VName -> Count Elements (TExp Int64) -> PrimType -> Space -> Volatility -> Exp
- Futhark.CodeGen.ImpCode.OpenCL: pattern If :: () => Exp -> Code a -> Code a -> Code a
+ Futhark.CodeGen.ImpCode.OpenCL: pattern Assert :: () => Exp -> ErrorMsg Exp -> (SrcLoc, [SrcLoc]) -> Code a
- Futhark.CodeGen.ImpCode.OpenCL: pattern Write :: () => VName -> Count Elements Exp -> PrimType -> Space -> Volatility -> Exp -> Code a
+ Futhark.CodeGen.ImpCode.OpenCL: pattern Write :: () => VName -> Count Elements (TExp Int64) -> PrimType -> Space -> Volatility -> Exp -> Code a
- Futhark.CodeGen.ImpCode.OpenCL: true :: PrimExp v
+ Futhark.CodeGen.ImpCode.OpenCL: true :: TPrimExp Bool v
- Futhark.CodeGen.ImpCode.OpenCL: vi32 :: VName -> Exp
+ Futhark.CodeGen.ImpCode.OpenCL: vi32 :: VName -> TExp Int32
- Futhark.CodeGen.ImpCode.OpenCL: withElemType :: Count Elements Exp -> PrimType -> Count Bytes Exp
+ Futhark.CodeGen.ImpCode.OpenCL: withElemType :: Count Elements (TExp Int64) -> PrimType -> Count Bytes (TExp Int64)
- Futhark.CodeGen.ImpCode.Sequential: (.&&.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Sequential: (.&&.) :: TPrimExp Bool v -> TPrimExp Bool v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Sequential: (.&.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Sequential: (.&.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp t v
- Futhark.CodeGen.ImpCode.Sequential: (.<.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Sequential: (.<.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Sequential: (.<=.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Sequential: (.<=.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Sequential: (.==.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Sequential: (.==.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Sequential: (.>.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Sequential: (.>.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Sequential: (.>=.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Sequential: (.>=.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Sequential: (.^.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Sequential: (.^.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp t v
- Futhark.CodeGen.ImpCode.Sequential: (.|.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Sequential: (.|.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp t v
- Futhark.CodeGen.ImpCode.Sequential: (.||.) :: PrimExp v -> PrimExp v -> PrimExp v
+ Futhark.CodeGen.ImpCode.Sequential: (.||.) :: TPrimExp Bool v -> TPrimExp Bool v -> TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Sequential: Index :: VName -> Count Elements Exp -> PrimType -> Space -> Volatility -> ExpLeaf
+ Futhark.CodeGen.ImpCode.Sequential: Index :: VName -> Count Elements (TExp Int64) -> PrimType -> Space -> Volatility -> ExpLeaf
- Futhark.CodeGen.ImpCode.Sequential: bytes :: Exp -> Count Bytes Exp
+ Futhark.CodeGen.ImpCode.Sequential: bytes :: a -> Count Bytes a
- Futhark.CodeGen.ImpCode.Sequential: elements :: Exp -> Count Elements Exp
+ Futhark.CodeGen.ImpCode.Sequential: elements :: a -> Count Elements a
- Futhark.CodeGen.ImpCode.Sequential: false :: PrimExp v
+ Futhark.CodeGen.ImpCode.Sequential: false :: TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Sequential: index :: VName -> Count Elements Exp -> PrimType -> Space -> Volatility -> Exp
+ Futhark.CodeGen.ImpCode.Sequential: index :: VName -> Count Elements (TExp Int64) -> PrimType -> Space -> Volatility -> Exp
- Futhark.CodeGen.ImpCode.Sequential: pattern If :: () => Exp -> Code a -> Code a -> Code a
+ Futhark.CodeGen.ImpCode.Sequential: pattern Assert :: () => Exp -> ErrorMsg Exp -> (SrcLoc, [SrcLoc]) -> Code a
- Futhark.CodeGen.ImpCode.Sequential: pattern Write :: () => VName -> Count Elements Exp -> PrimType -> Space -> Volatility -> Exp -> Code a
+ Futhark.CodeGen.ImpCode.Sequential: pattern Write :: () => VName -> Count Elements (TExp Int64) -> PrimType -> Space -> Volatility -> Exp -> Code a
- Futhark.CodeGen.ImpCode.Sequential: true :: PrimExp v
+ Futhark.CodeGen.ImpCode.Sequential: true :: TPrimExp Bool v
- Futhark.CodeGen.ImpCode.Sequential: vi32 :: VName -> Exp
+ Futhark.CodeGen.ImpCode.Sequential: vi32 :: VName -> TExp Int32
- Futhark.CodeGen.ImpCode.Sequential: withElemType :: Count Elements Exp -> PrimType -> Count Bytes Exp
+ Futhark.CodeGen.ImpCode.Sequential: withElemType :: Count Elements (TExp Int64) -> PrimType -> Count Bytes (TExp Int64)
- Futhark.CodeGen.ImpGen: (<--) :: VName -> Exp -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: (<--) :: TV t -> TExp t -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: MemLocation :: VName -> [DimSize] -> IxFun Exp -> MemLocation
+ Futhark.CodeGen.ImpGen: MemLocation :: VName -> [DimSize] -> IxFun (TExp Int64) -> MemLocation
- Futhark.CodeGen.ImpGen: [memLocationIxFun] :: MemLocation -> IxFun Exp
+ Futhark.CodeGen.ImpGen: [memLocationIxFun] :: MemLocation -> IxFun (TExp Int64)
- Futhark.CodeGen.ImpGen: copyDWIM :: VName -> [DimIndex Exp] -> SubExp -> [DimIndex Exp] -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: copyDWIM :: VName -> [DimIndex (TExp Int64)] -> SubExp -> [DimIndex (TExp Int64)] -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: copyDWIMFix :: VName -> [Exp] -> SubExp -> [Exp] -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: copyDWIMFix :: VName -> [TExp Int64] -> SubExp -> [TExp Int64] -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: dPrim :: String -> PrimType -> ImpM lore r op VName
+ Futhark.CodeGen.ImpGen: dPrim :: String -> PrimType -> ImpM lore r op (TV t)
- Futhark.CodeGen.ImpGen: dPrimV :: String -> Exp -> ImpM lore r op VName
+ Futhark.CodeGen.ImpGen: dPrimV :: String -> TExp t -> ImpM lore r op (TV t)
- Futhark.CodeGen.ImpGen: dPrimVE :: String -> Exp -> ImpM lore r op Exp
+ Futhark.CodeGen.ImpGen: dPrimVE :: String -> TExp t -> ImpM lore r op (TExp t)
- Futhark.CodeGen.ImpGen: dPrimV_ :: VName -> Exp -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: dPrimV_ :: VName -> TExp t -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: fullyIndexArray :: VName -> [Exp] -> ImpM lore r op (VName, Space, Count Elements Exp)
+ Futhark.CodeGen.ImpGen: fullyIndexArray :: VName -> [TExp Int64] -> ImpM lore r op (VName, Space, Count Elements (TExp Int64))
- Futhark.CodeGen.ImpGen: fullyIndexArray' :: MemLocation -> [Exp] -> ImpM lore r op (VName, Space, Count Elements Exp)
+ Futhark.CodeGen.ImpGen: fullyIndexArray' :: MemLocation -> [TExp Int64] -> ImpM lore r op (VName, Space, Count Elements (TExp Int64))
- Futhark.CodeGen.ImpGen: infixl 3 <--
+ Futhark.CodeGen.ImpGen: infixl 3 <~~
- Futhark.CodeGen.ImpGen: sAlloc :: String -> Count Bytes Exp -> Space -> ImpM lore r op VName
+ Futhark.CodeGen.ImpGen: sAlloc :: String -> Count Bytes (TExp Int64) -> Space -> ImpM lore r op VName
- Futhark.CodeGen.ImpGen: sAlloc_ :: VName -> Count Bytes Exp -> Space -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: sAlloc_ :: VName -> Count Bytes (TExp Int64) -> Space -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: sFor :: String -> Exp -> (Exp -> ImpM lore r op ()) -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: sFor :: String -> TExp t -> (TExp t -> ImpM lore r op ()) -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: sIf :: Exp -> ImpM lore r op () -> ImpM lore r op () -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: sIf :: TExp Bool -> ImpM lore r op () -> ImpM lore r op () -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: sLoopNest :: Shape -> ([Exp] -> ImpM lore r op ()) -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: sLoopNest :: Shape -> ([TExp Int64] -> ImpM lore r op ()) -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: sUnless :: Exp -> ImpM lore r op () -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: sUnless :: TExp Bool -> ImpM lore r op () -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: sUpdate :: VName -> Slice Exp -> SubExp -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: sUpdate :: VName -> Slice (TExp Int64) -> SubExp -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: sWhen :: Exp -> ImpM lore r op () -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: sWhen :: TExp Bool -> ImpM lore r op () -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: sWhile :: Exp -> ImpM lore r op () -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: sWhile :: TExp Bool -> ImpM lore r op () -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: sWrite :: VName -> [Exp] -> PrimExp ExpLeaf -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: sWrite :: VName -> [TExp Int64] -> Exp -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: type AllocCompiler lore r op = VName -> Count Bytes Exp -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: type AllocCompiler lore r op = VName -> Count Bytes (TExp Int64) -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: type CopyCompiler lore r op = PrimType -> MemLocation -> Slice Exp -> MemLocation -> Slice Exp -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen: type CopyCompiler lore r op = PrimType -> MemLocation -> Slice (TExp Int64) -> MemLocation -> Slice (TExp Int64) -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen: typeSize :: Type -> Count Bytes Exp
+ Futhark.CodeGen.ImpGen: typeSize :: Type -> Count Bytes (TExp Int64)
- Futhark.CodeGen.ImpGen.Kernels.Base: KernelConstants :: Exp -> Exp -> Exp -> VName -> VName -> VName -> Exp -> Exp -> Exp -> Exp -> Exp -> Map [SubExp] [Exp] -> KernelConstants
+ Futhark.CodeGen.ImpGen.Kernels.Base: KernelConstants :: TExp Int32 -> TExp Int32 -> TExp Int32 -> VName -> VName -> VName -> TExp Int64 -> TExp Int64 -> TExp Int32 -> TExp Int32 -> TExp Bool -> Map [SubExp] [TExp Int32] -> KernelConstants
- Futhark.CodeGen.ImpGen.Kernels.Base: Locking :: VName -> Exp -> Exp -> Exp -> ([Exp] -> [Exp]) -> Locking
+ Futhark.CodeGen.ImpGen.Kernels.Base: Locking :: VName -> TExp Int32 -> TExp Int32 -> TExp Int32 -> ([TExp Int64] -> [TExp Int64]) -> Locking
- Futhark.CodeGen.ImpGen.Kernels.Base: [kernelGlobalThreadId] :: KernelConstants -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: [kernelGlobalThreadId] :: KernelConstants -> TExp Int32
- Futhark.CodeGen.ImpGen.Kernels.Base: [kernelGroupId] :: KernelConstants -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: [kernelGroupId] :: KernelConstants -> TExp Int32
- Futhark.CodeGen.ImpGen.Kernels.Base: [kernelGroupSize] :: KernelConstants -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: [kernelGroupSize] :: KernelConstants -> TExp Int64
- Futhark.CodeGen.ImpGen.Kernels.Base: [kernelLocalIdMap] :: KernelConstants -> Map [SubExp] [Exp]
+ Futhark.CodeGen.ImpGen.Kernels.Base: [kernelLocalIdMap] :: KernelConstants -> Map [SubExp] [TExp Int32]
- Futhark.CodeGen.ImpGen.Kernels.Base: [kernelLocalThreadId] :: KernelConstants -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: [kernelLocalThreadId] :: KernelConstants -> TExp Int32
- Futhark.CodeGen.ImpGen.Kernels.Base: [kernelNumGroups] :: KernelConstants -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: [kernelNumGroups] :: KernelConstants -> TExp Int64
- Futhark.CodeGen.ImpGen.Kernels.Base: [kernelNumThreads] :: KernelConstants -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: [kernelNumThreads] :: KernelConstants -> TExp Int32
- Futhark.CodeGen.ImpGen.Kernels.Base: [kernelThreadActive] :: KernelConstants -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: [kernelThreadActive] :: KernelConstants -> TExp Bool
- Futhark.CodeGen.ImpGen.Kernels.Base: [kernelWaveSize] :: KernelConstants -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: [kernelWaveSize] :: KernelConstants -> TExp Int32
- Futhark.CodeGen.ImpGen.Kernels.Base: [lockingIsUnlocked] :: Locking -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: [lockingIsUnlocked] :: Locking -> TExp Int32
- Futhark.CodeGen.ImpGen.Kernels.Base: [lockingMapping] :: Locking -> [Exp] -> [Exp]
+ Futhark.CodeGen.ImpGen.Kernels.Base: [lockingMapping] :: Locking -> [TExp Int64] -> [TExp Int64]
- Futhark.CodeGen.ImpGen.Kernels.Base: [lockingToLock] :: Locking -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: [lockingToLock] :: Locking -> TExp Int32
- Futhark.CodeGen.ImpGen.Kernels.Base: [lockingToUnlock] :: Locking -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: [lockingToUnlock] :: Locking -> TExp Int32
- Futhark.CodeGen.ImpGen.Kernels.Base: computeThreadChunkSize :: SplitOrdering -> Exp -> Count Elements Exp -> Count Elements Exp -> VName -> ImpM lore r op ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: computeThreadChunkSize :: SplitOrdering -> TExp Int64 -> Count Elements (TExp Int64) -> Count Elements (TExp Int64) -> TV Int64 -> ImpM lore r op ()
- Futhark.CodeGen.ImpGen.Kernels.Base: groupCoverSpace :: [Exp] -> ([Exp] -> InKernelGen ()) -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: groupCoverSpace :: [TExp Int64] -> ([TExp Int64] -> InKernelGen ()) -> InKernelGen ()
- Futhark.CodeGen.ImpGen.Kernels.Base: groupLoop :: Exp -> (Exp -> InKernelGen ()) -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: groupLoop :: TExp Int64 -> (TExp Int64 -> InKernelGen ()) -> InKernelGen ()
- Futhark.CodeGen.ImpGen.Kernels.Base: groupReduce :: Exp -> Lambda KernelsMem -> [VName] -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: groupReduce :: TExp Int32 -> Lambda KernelsMem -> [VName] -> InKernelGen ()
- Futhark.CodeGen.ImpGen.Kernels.Base: groupScan :: Maybe (Exp -> Exp -> Exp) -> Exp -> Exp -> Lambda KernelsMem -> [VName] -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: groupScan :: Maybe (TExp Int32 -> TExp Int32 -> TExp Bool) -> TExp Int64 -> TExp Int64 -> Lambda KernelsMem -> [VName] -> InKernelGen ()
- Futhark.CodeGen.ImpGen.Kernels.Base: isActive :: [(VName, SubExp)] -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: isActive :: [(VName, SubExp)] -> TExp Bool
- Futhark.CodeGen.ImpGen.Kernels.Base: kernelLoop :: Exp -> Exp -> Exp -> (Exp -> InKernelGen ()) -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: kernelLoop :: IntExp t => TExp t -> TExp t -> TExp t -> (TExp t -> InKernelGen ()) -> InKernelGen ()
- Futhark.CodeGen.ImpGen.Kernels.Base: sIota :: VName -> Exp -> Exp -> Exp -> IntType -> CallKernelGen ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: sIota :: VName -> TExp Int64 -> Exp -> Exp -> IntType -> CallKernelGen ()
- Futhark.CodeGen.ImpGen.Kernels.Base: sKernelGroup :: String -> Count NumGroups Exp -> Count GroupSize Exp -> VName -> InKernelGen () -> CallKernelGen ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: sKernelGroup :: String -> Count NumGroups (TExp Int64) -> Count GroupSize (TExp Int64) -> VName -> InKernelGen () -> CallKernelGen ()
- Futhark.CodeGen.ImpGen.Kernels.Base: sKernelThread :: String -> Count NumGroups Exp -> Count GroupSize Exp -> VName -> InKernelGen () -> CallKernelGen ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: sKernelThread :: String -> Count NumGroups (TExp Int64) -> Count GroupSize (TExp Int64) -> VName -> InKernelGen () -> CallKernelGen ()
- Futhark.CodeGen.ImpGen.Kernels.Base: type AtomicBinOp = BinOp -> Maybe (VName -> VName -> Count Elements Exp -> Exp -> AtomicOp)
+ Futhark.CodeGen.ImpGen.Kernels.Base: type AtomicBinOp = BinOp -> Maybe (VName -> VName -> Count Elements (TExp Int64) -> Exp -> AtomicOp)
- Futhark.CodeGen.ImpGen.Kernels.Base: type DoAtomicUpdate lore r = Space -> [VName] -> [Exp] -> ImpM lore r KernelOp ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: type DoAtomicUpdate lore r = Space -> [VName] -> [TExp Int64] -> ImpM lore r KernelOp ()
- Futhark.CodeGen.ImpGen.Kernels.Base: virtualiseGroups :: SegVirt -> Exp -> (VName -> InKernelGen ()) -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: virtualiseGroups :: SegVirt -> TExp Int32 -> (TExp Int32 -> InKernelGen ()) -> InKernelGen ()
- Futhark.CodeGen.ImpGen.Kernels.SegRed: type DoSegBody = ([(SubExp, [Exp])] -> InKernelGen ()) -> InKernelGen ()
+ Futhark.CodeGen.ImpGen.Kernels.SegRed: type DoSegBody = ([(SubExp, [TExp Int64])] -> InKernelGen ()) -> InKernelGen ()
- Futhark.CodeGen.ImpGen.Kernels.Transpose: type TransposeArgs = (VName, Exp, VName, Exp, Exp, Exp, Exp, Exp, Exp, VName)
+ Futhark.CodeGen.ImpGen.Kernels.Transpose: type TransposeArgs = (VName, TExp Int32, VName, TExp Int32, TExp Int32, TExp Int32, TExp Int32, TExp Int32, TExp Int32, VName)
- Futhark.CodeGen.OpenCL.Heuristics: SizeHeuristic :: String -> DeviceType -> WhichSize -> PrimExp DeviceInfo -> SizeHeuristic
+ Futhark.CodeGen.OpenCL.Heuristics: SizeHeuristic :: String -> DeviceType -> WhichSize -> TPrimExp Int32 DeviceInfo -> SizeHeuristic
- Futhark.CodeGen.OpenCL.Heuristics: [heuristicValue] :: SizeHeuristic -> PrimExp DeviceInfo
+ Futhark.CodeGen.OpenCL.Heuristics: [heuristicValue] :: SizeHeuristic -> TPrimExp Int32 DeviceInfo
- Futhark.IR.Decorations: class (Show (LetDec l), Show (ExpDec l), Show (BodyDec l), Show (FParamInfo l), Show (LParamInfo l), Show (RetType l), Show (BranchType l), Show (Op l), Eq (LetDec l), Eq (ExpDec l), Eq (BodyDec l), Eq (FParamInfo l), Eq (LParamInfo l), Eq (RetType l), Eq (BranchType l), Eq (Op l), Ord (LetDec l), Ord (ExpDec l), Ord (BodyDec l), Ord (FParamInfo l), Ord (LParamInfo l), Ord (RetType l), Ord (BranchType l), Ord (Op l), IsRetType (RetType l), IsBodyType (BranchType l), Typed (FParamInfo l), Typed (LParamInfo l), Typed (LetDec l), DeclTyped (FParamInfo l)) => Decorations l where {
+ Futhark.IR.Decorations: class (Show (LetDec l), Show (ExpDec l), Show (BodyDec l), Show (FParamInfo l), Show (LParamInfo l), Show (RetType l), Show (BranchType l), Show (Op l), Eq (LetDec l), Eq (ExpDec l), Eq (BodyDec l), Eq (FParamInfo l), Eq (LParamInfo l), Eq (RetType l), Eq (BranchType l), Eq (Op l), Ord (LetDec l), Ord (ExpDec l), Ord (BodyDec l), Ord (FParamInfo l), Ord (LParamInfo l), Ord (RetType l), Ord (BranchType l), Ord (Op l), IsRetType (RetType l), IsBodyType (BranchType l), Typed (FParamInfo l), Typed (LParamInfo l), Typed (LetDec l), DeclTyped (FParamInfo l), SexpIso (LetDec l), SexpIso (ExpDec l), SexpIso (BodyDec l), SexpIso (FParamInfo l), SexpIso (LParamInfo l), SexpIso (RetType l), SexpIso (BranchType l), SexpIso (Op l)) => Decorations l where {
- Futhark.IR.Kernels.Sizes: SizeBespoke :: Name -> Int32 -> SizeClass
+ Futhark.IR.Kernels.Sizes: SizeBespoke :: Name -> Int64 -> SizeClass
- Futhark.IR.Kernels.Sizes: SizeThreshold :: KernelPath -> Maybe Int32 -> SizeClass
+ Futhark.IR.Kernels.Sizes: SizeThreshold :: KernelPath -> Maybe Int64 -> SizeClass
- Futhark.IR.Kernels.Sizes: sizeDefault :: SizeClass -> Maybe Int32
+ Futhark.IR.Kernels.Sizes: sizeDefault :: SizeClass -> Maybe Int64
- Futhark.IR.Mem: lookupArraySummary :: (Mem lore, HasScope lore m, Monad m) => VName -> m (VName, IxFun (PrimExp VName))
+ Futhark.IR.Mem: lookupArraySummary :: (Mem lore, HasScope lore m, Monad m) => VName -> m (VName, IxFun (TPrimExp Int64 VName))
- Futhark.IR.Mem: matchBranchReturnType :: Mem lore => [BodyReturns] -> Body (Aliases lore) -> TypeM lore ()
+ Futhark.IR.Mem: matchBranchReturnType :: (Mem lore, Checkable lore) => [BodyReturns] -> Body (Aliases lore) -> TypeM lore ()
- Futhark.IR.Mem: matchFunctionReturnType :: Mem lore => [FunReturns] -> Result -> TypeM lore ()
+ Futhark.IR.Mem: matchFunctionReturnType :: (Mem lore, Checkable lore) => [FunReturns] -> Result -> TypeM lore ()
- Futhark.IR.Mem: matchLoopResultMem :: Mem lore => [FParam (Aliases lore)] -> [FParam (Aliases lore)] -> [SubExp] -> TypeM lore ()
+ Futhark.IR.Mem: matchLoopResultMem :: (Mem lore, Checkable lore) => [FParam (Aliases lore)] -> [FParam (Aliases lore)] -> [SubExp] -> TypeM lore ()
- Futhark.IR.Mem: matchPatternToExp :: Mem lore => Pattern (Aliases lore) -> Exp (Aliases lore) -> TypeM lore ()
+ Futhark.IR.Mem: matchPatternToExp :: (Mem lore, Checkable lore) => Pattern (Aliases lore) -> Exp (Aliases lore) -> TypeM lore ()
- Futhark.IR.Mem: type ExtIxFun = IxFun (PrimExp (Ext VName))
+ Futhark.IR.Mem: type ExtIxFun = IxFun (TPrimExp Int64 (Ext VName))
- Futhark.IR.Mem: type IxFun = IxFun (PrimExp VName)
+ Futhark.IR.Mem: type IxFun = IxFun (TPrimExp Int64 VName)
- Futhark.IR.Mem: type Mem lore = (AllocOp (Op lore), FParamInfo lore ~ FParamMem, LParamInfo lore ~ LParamMem, LetDec lore ~ LetDecMem, RetType lore ~ RetTypeMem, BranchType lore ~ BranchTypeMem, CanBeAliased (Op lore), ASTLore lore, Decorations lore, Checkable lore, OpReturns lore)
+ Futhark.IR.Mem: type Mem lore = (AllocOp (Op lore), FParamInfo lore ~ FParamMem, LParamInfo lore ~ LParamMem, LetDec lore ~ LetDecMem, RetType lore ~ RetTypeMem, BranchType lore ~ BranchTypeMem, ASTLore lore, Decorations lore, OpReturns lore)
- Futhark.IR.Mem.IxFun: existentialize :: (Eq v, Pretty v) => IxFun (PrimExp v) -> State [PrimExp v] (Maybe (IxFun (PrimExp (Ext v))))
+ Futhark.IR.Mem.IxFun: existentialize :: (IntExp t, Eq v, Pretty v) => IxFun (TPrimExp t v) -> State [TPrimExp t v] (Maybe (IxFun (TPrimExp t (Ext v))))
- Futhark.IR.Mem.IxFun: substituteInIxFun :: Ord a => Map a (PrimExp a) -> IxFun (PrimExp a) -> IxFun (PrimExp a)
+ Futhark.IR.Mem.IxFun: substituteInIxFun :: Ord a => Map a (TPrimExp t a) -> IxFun (TPrimExp t a) -> IxFun (TPrimExp t a)
- Futhark.IR.Prop: type ASTConstraints a = (Eq a, Ord a, Show a, Rename a, Substitute a, FreeIn a, Pretty a)
+ Futhark.IR.Prop: type ASTConstraints a = (Eq a, Ord a, Show a, Rename a, Substitute a, FreeIn a, Pretty a, SexpIso a)
- Futhark.IR.SOACS: class (Show (LetDec l), Show (ExpDec l), Show (BodyDec l), Show (FParamInfo l), Show (LParamInfo l), Show (RetType l), Show (BranchType l), Show (Op l), Eq (LetDec l), Eq (ExpDec l), Eq (BodyDec l), Eq (FParamInfo l), Eq (LParamInfo l), Eq (RetType l), Eq (BranchType l), Eq (Op l), Ord (LetDec l), Ord (ExpDec l), Ord (BodyDec l), Ord (FParamInfo l), Ord (LParamInfo l), Ord (RetType l), Ord (BranchType l), Ord (Op l), IsRetType (RetType l), IsBodyType (BranchType l), Typed (FParamInfo l), Typed (LParamInfo l), Typed (LetDec l), DeclTyped (FParamInfo l)) => Decorations l where {
+ Futhark.IR.SOACS: class (Show (LetDec l), Show (ExpDec l), Show (BodyDec l), Show (FParamInfo l), Show (LParamInfo l), Show (RetType l), Show (BranchType l), Show (Op l), Eq (LetDec l), Eq (ExpDec l), Eq (BodyDec l), Eq (FParamInfo l), Eq (LParamInfo l), Eq (RetType l), Eq (BranchType l), Eq (Op l), Ord (LetDec l), Ord (ExpDec l), Ord (BodyDec l), Ord (FParamInfo l), Ord (LParamInfo l), Ord (RetType l), Ord (BranchType l), Ord (Op l), IsRetType (RetType l), IsBodyType (BranchType l), Typed (FParamInfo l), Typed (LParamInfo l), Typed (LetDec l), DeclTyped (FParamInfo l), SexpIso (LetDec l), SexpIso (ExpDec l), SexpIso (BodyDec l), SexpIso (FParamInfo l), SexpIso (LParamInfo l), SexpIso (RetType l), SexpIso (BranchType l), SexpIso (Op l)) => Decorations l where {
- Futhark.Optimise.Simplify.ClosedForm: loopClosedForm :: (ASTLore lore, BinderOps lore) => Pattern lore -> [(FParam lore, SubExp)] -> Names -> SubExp -> Body lore -> RuleM lore ()
+ Futhark.Optimise.Simplify.ClosedForm: loopClosedForm :: (ASTLore lore, BinderOps lore) => Pattern lore -> [(FParam lore, SubExp)] -> Names -> IntType -> SubExp -> Body lore -> RuleM lore ()
- Language.Futhark.Syntax: type ValueType = TypeBase Int32 ()
+ Language.Futhark.Syntax: type ValueType = TypeBase Int64 ()

Files

docs/language-reference.rst view
@@ -34,12 +34,11 @@ Many things in Futhark are named. When we are defining something, we give it an unqualified name (`id`).  When referencing something inside a module, we use a qualified name (`qualid`).  The constructor names-of a sum type (:ref:`compounds`) are identifiers prefixed with ``#``,-with no space afterwards.  The fields of a record are named with-`fieldid`.  Note that a `fieldid` can be a decimal number.  Futhark-has three distinct name spaces: terms, module types, and types.-Modules (including parametric modules) and values both share the term-namespace.+of a sum type are identifiers prefixed with ``#``, with no space+afterwards.  The fields of a record are named with `fieldid`.  Note+that a `fieldid` can be a decimal number.  Futhark has three distinct+name spaces: terms, module types, and types.  Modules (including+parametric modules) and values both share the term namespace.  .. _primitives: @@ -94,8 +93,6 @@    hexdigit: `decdigit` | "a"..."f" | "A"..."F"    bindigit: "0" | "1" -.. _compounds:- Compound Types and Values ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -329,6 +326,9 @@ Any top-level function named ``main`` will always be considered an entry point, whether it is declared with ``entry`` or not. +The name of an entry point must not contain an apostrophe (``'``),+even though that is normally permitted in Futhark identifiers.+ Value Declarations ~~~~~~~~~~~~~~~~~~ @@ -594,7 +594,7 @@ Return the element at the given position in the array.  The index may be a comma-separated list of indexes instead of just a single index. If the number of indices given is less than the rank of the array, an-array is returned.+array is returned.  The index may be of any unsigned integer type.  The array ``a`` must be a variable name or a parenthesised expression. Furthermore, there *may not* be a space between ``a`` and the opening@@ -610,7 +610,8 @@ former inclusive and the latter exclusive, taking every ``s``-th element.  The ``s`` parameter may not be zero.  If ``s`` is negative, it means to start at ``i`` and descend by steps of size ``s`` to ``j``-(not inclusive).+(not inclusive).  Slicing can be done only with expressions of type+``i32``.  It is generally a bad idea for ``s`` to be non-constant. Slicing of multiple dimensions can be done by separating with commas,@@ -647,9 +648,9 @@ ``x..y...z`` ............ -Construct an integer array whose first element is ``x`` and which-proceeds stride of ``y-x`` until reaching ``z`` (inclusive).  The-``..y`` part can be elided in which case a stride of 1 is used.  A+Construct a signed integer array whose first element is ``x`` and+which proceeds stride of ``y-x`` until reaching ``z`` (inclusive).+The ``..y`` part can be elided in which case a stride of 1 is used.  A run-time error occurs if ``z`` is lesser than ``x`` or ``y``, or if ``x`` and ``y`` are the same value. @@ -666,8 +667,8 @@ ``x..y..<z`` ............ -Construct an integer array whose first elements is ``x``, and which-proceeds upwards with a stride of ``y`` until reaching ``z``+Construct a signed integer array whose first elements is ``x``, and+which proceeds upwards with a stride of ``y`` until reaching ``z`` (exclusive).  The ``..y`` part can be elided in which case a stride of 1 is used.  A run-time error occurs if ``z`` is lesser than ``x`` or ``y``, or if ``x`` and ``y`` are the same value.@@ -681,8 +682,8 @@ ``x..y..>z`` ............... -Construct an integer array whose first elements is ``x``, and which-proceeds downwards with a stride of ``y`` until reaching ``z``+Construct a signed integer array whose first elements is ``x``, and+which proceeds downwards with a stride of ``y`` until reaching ``z`` (exclusive).  The ``..y`` part can be elided in which case a stride of -1 is used.  A run-time error occurs if ``z`` is greater than ``x`` or ``y``, or if ``x`` and ``y`` are the same value.
docs/man/futhark-c.rst view
@@ -62,6 +62,10 @@  The following options are accepted by executables generated by ``futhark c``. +-h, --help++  Print help text to standard output and exit.+ -b, --binary-output    Print the program result in the binary output format.  The default
docs/man/futhark-cuda.rst view
@@ -23,10 +23,14 @@ resulting program will otherwise behave exactly as one compiled with ``futhark c``. -``futhark cuda`` uses ``-lcuda -lnvrtc`` to link.  If using+``futhark cuda`` uses ``-lcuda -lcudart -lnvrtc`` to link.  If using ``--library``, you will need to do the same when linking the final binary. +The generated CUDA code can be called from multiple CPU threads, as it+brackets every API operation with ``cuCtxPushCurrent()`` and+``cuCtxPopCurrent()``.+ OPTIONS ======= @@ -68,6 +72,10 @@ nomenclature ("group" instead of "thread block").  The following additional options are accepted.++-h, --help++  Print help text to standard output and exit.  --default-group-size=INT 
docs/man/futhark-opencl.rst view
@@ -73,6 +73,10 @@  The following additional options are accepted. +-h, --help++  Print help text to standard output and exit.+ --build-option=OPT    Add an additional build option to the string passed to@@ -154,6 +158,10 @@ --tuning=FILE    Read size=value assignments from the given file.++--list-devices++  List all OpenCL devices and platforms available on the system.  SEE ALSO ========
docs/usage.rst view
@@ -75,6 +75,10 @@  All generated executables support the following options. +  ``-h/--help``++    Print help text to standard output and exit.+   ``-t FILE``      Print the time taken to execute the program to the indicated file,@@ -205,6 +209,10 @@     ``clBuildProgram()``.  Refer to the OpenCL documentation for which     options are supported.  Be careful - some options can easily     result in invalid results.++  ``--list-devices``++    List all OpenCL devices and platforms available on the system.  There is rarely a need to use both ``-p`` and ``-d``.  For example, to run on the first available NVIDIA GPU, ``-p NVIDIA`` is sufficient, as
futhark.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4  name:           futhark-version:        0.16.4+version:        0.17.1 synopsis:       An optimising compiler for a functional, array-oriented language.  description:    Futhark is a small programming language designed to be compiled to@@ -118,6 +118,7 @@       Futhark.CodeGen.ImpGen.Kernels.Transpose       Futhark.CodeGen.ImpGen.OpenCL       Futhark.CodeGen.ImpGen.Sequential+      Futhark.CodeGen.ImpGen.Transpose       Futhark.CodeGen.OpenCL.Heuristics       Futhark.CodeGen.SetDefaultSpace       Futhark.Compiler@@ -286,6 +287,7 @@     , process >=1.4.3.0     , process-extras >=0.7.2     , regex-tdfa >=1.2+    , sexp-grammar >= 2.2.1     , srcloc >=0.4     , template-haskell >=2.11.1     , temporary@@ -342,6 +344,7 @@     , megaparsec     , mtl     , parser-combinators+    , sexp-grammar     , tasty     , tasty-hunit     , tasty-quickcheck
prelude/array.fut view
@@ -24,13 +24,13 @@ let init [n] 't (x: [n]t) = x[0:n-1]  -- | Take some number of elements from the head of the array.-let take [n] 't (i: i32) (x: [n]t): [i]t = x[0:i]+let take [n] 't (i: i64) (x: [n]t): [i]t = x[0:i]  -- | Remove some number of elements from the head of the array.-let drop [n] 't (i: i32) (x: [n]t) = x[i:]+let drop [n] 't (i: i64) (x: [n]t) = x[i:]  -- | Split an array at a given position.-let split [n] 't (i: i32) (xs: [n]t): ([i]t, []t) =+let split [n] 't (i: i64) (xs: [n]t): ([i]t, []t) =   (xs[:i] :> [i]t, xs[i:])  -- | Return the elements of the array in reverse order.@@ -46,28 +46,28 @@ -- | Concatenation where the result has a predetermined size.  If the -- provided size is wrong, the function will fail with a run-time -- error.-let concat_to [n] [m] 't (k: i32) (xs: [n]t) (ys: [m]t): *[k]t = xs ++ ys :> [k]t+let concat_to [n] [m] 't (k: i64) (xs: [n]t) (ys: [m]t): *[k]t = xs ++ ys :> [k]t  -- | Rotate an array some number of elements to the left.  A negative -- rotation amount is also supported. -- -- For example, if `b==rotate r a`, then `b[x+r] = a[x]`.-let rotate [n] 't (r: i32) (xs: [n]t): [n]t = intrinsics.rotate (r, xs) :> [n]t+let rotate [n] 't (r: i64) (xs: [n]t): [n]t = intrinsics.rotate (r, xs) :> [n]t  -- | Construct an array of consecutive integers of the given length, -- starting at 0.-let iota (n: i32): *[n]i32 =-  i32.iota n :> [n]i32+let iota (n: i64): *[n]i64 =+  0..1..<n  -- | Construct an array comprising valid indexes into some other -- array, starting at 0.-let indices [n] 't (_: [n]t) : *[n]i32 =+let indices [n] 't (_: [n]t) : *[n]i64 =   iota n  -- | Construct an array of the given length containing the given -- value.-let replicate 't (n: i32) (x: t): *[n]t =-  i32.replicate n x :> [n]t+let replicate 't (n: i64) (x: t): *[n]t =+  map (const x) (iota n)  -- | Copy a value.  The result will not alias anything. let copy 't (a: t): *t =@@ -79,7 +79,7 @@  -- | Like `flatten`@term, but where the final size is known.  Fails at -- runtime if the provided size is wrong.-let flatten_to [n][m] 't (l: i32) (xs: [n][m]t): [l]t =+let flatten_to [n][m] 't (l: i64) (xs: [n][m]t): [l]t =   flatten xs :> [l]t  -- | Combines the outer three dimensions of an array.@@ -91,15 +91,15 @@   flatten (flatten_3d xs)  -- | Splits the outer dimension of an array in two.-let unflatten [p] 't (n: i32) (m: i32) (xs: [p]t): [n][m]t =+let unflatten [p] 't (n: i64) (m: i64) (xs: [p]t): [n][m]t =   intrinsics.unflatten (n, m, xs) :> [n][m]t  -- | Splits the outer dimension of an array in three.-let unflatten_3d [p] 't (n: i32) (m: i32) (l: i32) (xs: [p]t): [n][m][l]t =+let unflatten_3d [p] 't (n: i64) (m: i64) (l: i64) (xs: [p]t): [n][m][l]t =   unflatten n m (unflatten (n*m) l xs)  -- | Splits the outer dimension of an array in four.-let unflatten_4d [p] 't (n: i32) (m: i32) (l: i32) (k: i32) (xs: [p]t): [n][m][l][k]t =+let unflatten_4d [p] 't (n: i64) (m: i64) (l: i64) (k: i64) (xs: [p]t): [n][m][l][k]t =   unflatten n m (unflatten_3d (n*m) l k xs)  let transpose [n] [m] 't (a: [n][m]t): [m][n]t =@@ -122,13 +122,13 @@   foldl (flip f) acc (reverse bs)  -- | Create a value for each point in a one-dimensional index space.-let tabulate 'a (n: i32) (f: i32 -> a): *[n]a =+let tabulate 'a (n: i64) (f: i64 -> a): *[n]a =   map1 f (iota n)  -- | Create a value for each point in a two-dimensional index space.-let tabulate_2d 'a (n: i32) (m: i32) (f: i32 -> i32 -> a): *[n][m]a =+let tabulate_2d 'a (n: i64) (m: i64) (f: i64 -> i64 -> a): *[n][m]a =   map1 (f >-> tabulate m) (iota n)  -- | Create a value for each point in a three-dimensional index space.-let tabulate_3d 'a (n: i32) (m: i32) (o: i32) (f: i32 -> i32 -> i32 -> a): *[n][m][o]a =+let tabulate_3d 'a (n: i64) (m: i64) (o: i64) (f: i64 -> i64 -> i64 -> a): *[n][m][o]a =   map1 (f >-> tabulate_2d m o) (iota n)
prelude/math.fut view
@@ -2,8 +2,6 @@  import "soacs" -local let const 'a 'b (x: a) (_: b): a = x- -- | Describes types of values that can be created from the primitive -- numeric types (and bool). module type from_prim = {@@ -118,22 +116,11 @@   val ctz: t -> i32 } --- | An extension of `size`@mtype that further includes facilities for--- constructing arrays where the size is provided as a value of the--- given integral type.-module type size = {-  include integral--  val iota: t -> *[]t-  val replicate 'v: t -> v -> *[]v-}- -- | Numbers that model real numbers to some degree. module type real = {   include numeric -  val from_fraction: i32 -> i32 -> t-  val to_i32: t -> i32+  val from_fraction: i64 -> i64 -> t   val to_i64: t -> i64   val to_f64: t -> f64 @@ -239,7 +226,7 @@   let bool (x: bool) = x } -module i8: (size with t = i8) = {+module i8: (integral with t = i8) = {   type t = i8    let (x: i8) + (y: i8) = intrinsics.add8 (x, y)@@ -305,16 +292,13 @@   let clz = intrinsics.clz8   let ctz = intrinsics.ctz8 -  let iota (n: i8) = 0i8..1i8..<n-  let replicate 'v (n: i8) (x: v) = map (const x) (iota n)-   let sum = reduce (+) (i32 0)   let product = reduce (*) (i32 1)   let maximum = reduce max lowest   let minimum = reduce min highest } -module i16: (size with t = i16) = {+module i16: (integral with t = i16) = {   type t = i16    let (x: i16) + (y: i16) = intrinsics.add16 (x, y)@@ -380,16 +364,13 @@   let clz = intrinsics.clz16   let ctz = intrinsics.ctz16 -  let iota (n: i16) = 0i16..1i16..<n-  let replicate 'v (n: i16) (x: v) = map (const x) (iota n)-   let sum = reduce (+) (i32 0)   let product = reduce (*) (i32 1)   let maximum = reduce max lowest   let minimum = reduce min highest } -module i32: (size with t = i32) = {+module i32: (integral with t = i32) = {   type t = i32    let sign (x: u32) = intrinsics.sign_i32 x@@ -458,16 +439,13 @@   let clz = intrinsics.clz32   let ctz = intrinsics.ctz32 -  let iota (n: i32) = 0..1..<n-  let replicate 'v (n: i32) (x: v) = map (const x) (iota n)-   let sum = reduce (+) (i32 0)   let product = reduce (*) (i32 1)   let maximum = reduce max lowest   let minimum = reduce min highest } -module i64: (size with t = i64) = {+module i64: (integral with t = i64) = {   type t = i64    let sign (x: u64) = intrinsics.sign_i64 x@@ -536,16 +514,13 @@   let clz = intrinsics.clz64   let ctz = intrinsics.ctz64 -  let iota (n: i64) = 0i64..1i64..<n-  let replicate 'v (n: i64) (x: v) = map (const x) (iota n)-   let sum = reduce (+) (i32 0)   let product = reduce (*) (i32 1)   let maximum = reduce max lowest   let minimum = reduce min highest } -module u8: (size with t = u8) = {+module u8: (integral with t = u8) = {   type t = u8    let sign (x: u8) = intrinsics.sign_i8 x@@ -614,16 +589,13 @@   let clz x = intrinsics.clz8 (sign x)   let ctz x = intrinsics.ctz8 (sign x) -  let iota (n: u8) = 0u8..1u8..<n-  let replicate 'v (n: u8) (x: v) = map (const x) (iota n)-   let sum = reduce (+) (i32 0)   let product = reduce (*) (i32 1)   let maximum = reduce max lowest   let minimum = reduce min highest } -module u16: (size with t = u16) = {+module u16: (integral with t = u16) = {   type t = u16    let sign (x: u16) = intrinsics.sign_i16 x@@ -692,16 +664,13 @@   let clz x = intrinsics.clz16 (sign x)   let ctz x = intrinsics.ctz16 (sign x) -  let iota (n: u16) = 0u16..1u16..<n-  let replicate 'v (n: u16) (x: v) = map (const x) (iota n)-   let sum = reduce (+) (i32 0)   let product = reduce (*) (i32 1)   let maximum = reduce max lowest   let minimum = reduce min highest } -module u32: (size with t = u32) = {+module u32: (integral with t = u32) = {   type t = u32    let sign (x: u32) = intrinsics.sign_i32 x@@ -770,16 +739,13 @@   let clz x = intrinsics.clz32 (sign x)   let ctz x = intrinsics.ctz32 (sign x) -  let iota (n: u32) = 0u32..1u32..<n-  let replicate 'v (n: u32) (x: v) = map (const x) (iota n)-   let sum = reduce (+) (i32 0)   let product = reduce (*) (i32 1)   let maximum = reduce max lowest   let minimum = reduce min highest } -module u64: (size with t = u64) = {+module u64: (integral with t = u64) = {   type t = u64    let sign (x: u64) = intrinsics.sign_i64 x@@ -848,9 +814,6 @@   let clz x = intrinsics.clz64 (sign x)   let ctz x = intrinsics.ctz64 (sign x) -  let iota (n: u64) = 0u64..1u64..<n-  let replicate 'v (n: u64) (x: v) = map (const x) (iota n)-   let sum = reduce (+) (i32 0)   let product = reduce (*) (i32 1)   let maximum = reduce max lowest@@ -886,8 +849,7 @@    let bool (x: bool) = if x then 1f64 else 0f64 -  let from_fraction (x: i32) (y: i32) = i32 x / i32 y-  let to_i32 (x: f64) = intrinsics.fptosi_f64_i32 x+  let from_fraction (x: i64) (y: i64) = i64 x / i64 y   let to_i64 (x: f64) = intrinsics.fptosi_f64_i64 x   let to_f64 (x: f64) = x @@ -994,8 +956,7 @@    let bool (x: bool) = if x then 1f32 else 0f32 -  let from_fraction (x: i32) (y: i32) = i32 x / i32 y-  let to_i32 (x: f32) = intrinsics.fptosi_f32_i32 x+  let from_fraction (x: i64) (y: i64) = i64 x / i64 y   let to_i64 (x: f32) = intrinsics.fptosi_f32_i64 x   let to_f64 (x: f32) = intrinsics.fpconv_f32_f64 x 
prelude/soacs.fut view
@@ -118,7 +118,7 @@ -- -- In practice, the *O(n)* behaviour only occurs if *m* is also very -- large.-let reduce_by_index 'a [m] [n] (dest : *[m]a) (f : a -> a -> a) (ne : a) (is : [n]i32) (as : [n]a) : *[m]a =+let reduce_by_index 'a [m] [n] (dest : *[m]a) (f : a -> a -> a) (ne : a) (is : [n]i64) (as : [n]a) : *[m]a =   intrinsics.hist (1, dest, f, ne, is, as) :> *[m]a  -- | Inclusive prefix scan.  Has the same caveats with respect to@@ -163,7 +163,7 @@  -- | `reduce_stream op f as` splits `as` into chunks, applies `f` to each -- of these in parallel, and uses `op` (which must be associative) to--- combine the per-chunk results into a final result.  The `i32`+-- combine the per-chunk results into a final result.  The `i64` -- passed to `f` is the size of the chunk.  This SOAC is useful when -- `f` can be given a particularly work-efficient sequential -- implementation.  Operationally, we can imagine that `as` is divided@@ -176,7 +176,7 @@ -- **Work:** *O(n)* -- -- **Span:** *O(log(n))*-let reduce_stream [n] 'a 'b (op: b -> b -> b) (f: (k: i32) -> [k]a -> b) (as: [n]a): b =+let reduce_stream [n] 'a 'b (op: b -> b -> b) (f: (k: i64) -> [k]a -> b) (as: [n]a): b =   intrinsics.reduce_stream (op, f, as)  -- | As `reduce_stream`@term, but the chunks do not necessarily@@ -186,7 +186,7 @@ -- **Work:** *O(n)* -- -- **Span:** *O(log(n))*-let reduce_stream_per [n] 'a 'b (op: b -> b -> b) (f: (k: i32) -> [k]a -> b) (as: [n]a): b =+let reduce_stream_per [n] 'a 'b (op: b -> b -> b) (f: (k: i64) -> [k]a -> b) (as: [n]a): b =   intrinsics.reduce_stream_per (op, f, as)  -- | Similar to `reduce_stream`@term, except that each chunk must produce@@ -196,7 +196,7 @@ -- **Work:** *O(n)* -- -- **Span:** *O(1)*-let map_stream [n] 'a 'b (f: (k: i32) -> [k]a -> [k]b) (as: [n]a): *[n]b =+let map_stream [n] 'a 'b (f: (k: i64) -> [k]a -> [k]b) (as: [n]a): *[n]b =   intrinsics.map_stream (f, as) :> *[n]b  -- | Similar to `map_stream`@term, but the chunks do not necessarily@@ -206,7 +206,7 @@ -- **Work:** *O(n)* -- -- **Span:** *O(1)*-let map_stream_per [n] 'a 'b (f: (k: i32) -> [k]a -> [k]b) (as: [n]a): *[n]b =+let map_stream_per [n] 'a 'b (f: (k: i64) -> [k]a -> [k]b) (as: [n]a): *[n]b =   intrinsics.map_stream_per (f, as) :> *[n]b  -- | Return `true` if the given function returns `true` for all@@ -252,5 +252,5 @@ -- **Work:** *O(n)* -- -- **Span:** *O(1)*-let scatter 't [m] [n] (dest: *[m]t) (is: [n]i32) (vs: [n]t): *[m]t =+let scatter 't [m] [n] (dest: *[m]t) (is: [n]i64) (vs: [n]t): *[m]t =   intrinsics.scatter (dest, is, vs) :> *[m]t
rts/c/lock.h view
@@ -11,7 +11,7 @@  typedef HANDLE lock_t; -static lock_t create_lock(lock_t *lock) {+static void create_lock(lock_t *lock) {   *lock = CreateMutex(NULL,  // Default security attributes.                       FALSE, // Initially unlocked.                       NULL); // Unnamed.
rts/c/opencl.h view
@@ -342,6 +342,33 @@ }  // Returns 0 on success.+static int list_devices(struct opencl_config *cfg) {+  struct opencl_device_option *devices;+  size_t num_devices;++  opencl_all_device_options(&devices, &num_devices);++  const char *cur_platform = "";+  for (size_t i = 0; i < num_devices; i++) {+    struct opencl_device_option device = devices[i];+    if (strcmp(cur_platform, device.platform_name) != 0) {+      printf("Platform: %s\n", device.platform_name);+      cur_platform = device.platform_name;+    }+    printf("[%d]: %s\n", (int)i, device.device_name);+  }++  // Free all the platform and device names.+  for (size_t j = 0; j < num_devices; j++) {+    free(devices[j].platform_name);+    free(devices[j].device_name);+  }+  free(devices);++  return 0;+}++// Returns 0 on success. static int select_device_interactively(struct opencl_config *cfg) {   struct opencl_device_option *devices;   size_t num_devices;@@ -529,11 +556,18 @@   // Futhark reserves 4 bytes for bookkeeping information.   max_local_memory -= 4; -  // NVIDIA reserves some more bytes for who-knows-what.  The number-  // of bytes here has been experimentally determined, but the-  // overhead seems to vary a bit depending on what the kernel does.+  // The OpenCL implementation may reserve some local memory bytes for+  // various purposes.  In principle, we should use+  // clGetKernelWorkGroupInfo() to figure out for each kernel how much+  // is actually available, but our current code generator design+  // makes this infeasible.  Instead, we have this nasty hack where we+  // arbitrarily subtract some bytes, based on empirical measurements+  // (but which might be arbitrarily wrong).  Fortunately, we rarely+  // try to really push the local memory usage.   if (strstr(device_option.platform_name, "NVIDIA CUDA") != NULL) {     max_local_memory -= 12;+  } else if (strstr(device_option.platform_name, "AMD") != NULL) {+    max_local_memory -= 16;   }    // Make sure this function is defined.@@ -843,6 +877,7 @@ }  static int opencl_alloc(struct opencl_context *ctx, size_t min_size, const char *tag, cl_mem *mem_out) {+  (void)tag;   if (min_size < sizeof(int)) {     min_size = sizeof(int);   }
rts/python/opencl.py view
@@ -115,12 +115,14 @@     # See comment in rts/c/opencl.h.     if self.platform.name.find('NVIDIA CUDA') >= 0:         self.max_local_memory -= 12+    elif self.platform.name.find('AMD') >= 0:+        self.max_local_memory -= 16      self.free_list = {}      self.global_failure = self.pool.allocate(np.int32().itemsize)     cl.enqueue_fill_buffer(self.queue, self.global_failure, np.int32(-1), 0, np.int32().itemsize)-    self.global_failure_args = self.pool.allocate(np.int32().itemsize *+    self.global_failure_args = self.pool.allocate(np.int64().itemsize *                                                   (self.global_failure_args_max+1))     self.failure_is_an_option = np.int32(0) @@ -223,7 +225,7 @@         cl.enqueue_fill_buffer(self.queue, self.global_failure, np.int32(-1), 0, np.int32().itemsize)          # Read failure args.-        failure_args = np.empty(self.global_failure_args_max+1, dtype=np.int32)+        failure_args = np.empty(self.global_failure_args_max+1, dtype=np.int64)         cl.enqueue_copy(self.queue, failure_args, self.global_failure_args, is_blocking=True)          raise Exception(self.failure_msgs[failure[0]].format(*failure_args))
src/Futhark/Actions.hs view
@@ -1,75 +1,106 @@ {-# LANGUAGE FlexibleContexts #-}+ -- | All (almost) compiler pipelines end with an 'Action', which does -- something with the result of the pipeline. module Futhark.Actions-  ( printAction-  , impCodeGenAction-  , kernelImpCodeGenAction-  , metricsAction-  , compileCAction-  , compileOpenCLAction-  , compileCUDAAction+  ( printAction,+    impCodeGenAction,+    kernelImpCodeGenAction,+    metricsAction,+    compileCAction,+    compileOpenCLAction,+    compileCUDAAction,+    sexpAction,   ) where  import Control.Monad import Control.Monad.IO.Class-import System.Exit-import System.FilePath-import qualified System.Info--import Futhark.Compiler.CLI+import qualified Data.ByteString.Lazy.Char8 as ByteString import Futhark.Analysis.Alias+import Futhark.Analysis.Metrics+import qualified Futhark.CodeGen.Backends.CCUDA as CCUDA+import qualified Futhark.CodeGen.Backends.COpenCL as COpenCL+import qualified Futhark.CodeGen.Backends.SequentialC as SequentialC+import qualified Futhark.CodeGen.ImpGen.Kernels as ImpGenKernels+import qualified Futhark.CodeGen.ImpGen.Sequential as ImpGenSequential+import Futhark.Compiler.CLI import Futhark.IR-import Futhark.IR.Prop.Aliases import Futhark.IR.KernelsMem (KernelsMem)+import Futhark.IR.Prop.Aliases import Futhark.IR.SeqMem (SeqMem)-import qualified Futhark.CodeGen.ImpGen.Sequential as ImpGenSequential-import qualified Futhark.CodeGen.ImpGen.Kernels as ImpGenKernels-import qualified Futhark.CodeGen.Backends.SequentialC as SequentialC-import qualified Futhark.CodeGen.Backends.CCUDA as CCUDA-import qualified Futhark.CodeGen.Backends.COpenCL as COpenCL-import Futhark.Analysis.Metrics import Futhark.Util (runProgramWithExitCode)+import Language.SexpGrammar as Sexp+import System.Exit+import System.FilePath+import qualified System.Info  -- | Print the result to stdout, with alias annotations. printAction :: (ASTLore lore, CanBeAliased (Op lore)) => Action lore printAction =-  Action { actionName = "Prettyprint"-         , actionDescription = "Prettyprint the resulting internal representation on standard output."-         , actionProcedure = liftIO . putStrLn . pretty . aliasAnalysis-         }+  Action+    { actionName = "Prettyprint",+      actionDescription = "Prettyprint the resulting internal representation on standard output.",+      actionProcedure = liftIO . putStrLn . pretty . aliasAnalysis+    }  -- | Print metrics about AST node counts to stdout. metricsAction :: OpMetrics (Op lore) => Action lore metricsAction =-  Action { actionName = "Compute metrics"-         , actionDescription = "Print metrics on the final AST."-         , actionProcedure = liftIO . putStr . show . progMetrics-         }+  Action+    { actionName = "Compute metrics",+      actionDescription = "Print metrics on the final AST.",+      actionProcedure = liftIO . putStr . show . progMetrics+    }  -- | Convert the program to sequential ImpCode and print it to stdout. impCodeGenAction :: Action SeqMem impCodeGenAction =-  Action { actionName = "Compile imperative"-         , actionDescription = "Translate program into imperative IL and write it on standard output."-         , actionProcedure = liftIO . putStrLn . pretty . snd <=< ImpGenSequential.compileProg-         }+  Action+    { actionName = "Compile imperative",+      actionDescription = "Translate program into imperative IL and write it on standard output.",+      actionProcedure = liftIO . putStrLn . pretty . snd <=< ImpGenSequential.compileProg+    }  -- | Convert the program to GPU ImpCode and print it to stdout. kernelImpCodeGenAction :: Action KernelsMem kernelImpCodeGenAction =-  Action { actionName = "Compile imperative kernels"-         , actionDescription = "Translate program into imperative IL with kernels and write it on standard output."-         , actionProcedure = liftIO . putStrLn . pretty . snd <=< ImpGenKernels.compileProgOpenCL-         }+  Action+    { actionName = "Compile imperative kernels",+      actionDescription = "Translate program into imperative IL with kernels and write it on standard output.",+      actionProcedure = liftIO . putStrLn . pretty . snd <=< ImpGenKernels.compileProgOpenCL+    } +-- | Print metrics about AST node counts to stdout.+sexpAction :: ASTLore lore => Action lore+sexpAction =+  Action+    { actionName = "Print sexps",+      actionDescription = "Print sexps on the final IR.",+      actionProcedure = liftIO . helper+    }+  where+    helper :: ASTLore lore => Prog lore -> IO ()+    helper prog =+      case encodePretty prog of+        Right prog' -> do+          ByteString.putStrLn prog'+          let prog'' = decode prog'+          unless (prog'' == Right prog) $+            error $+              "S-exp not isomorph!\n"+                ++ either show pretty prog''+        Left s ->+          error $ "Couldn't encode program: " ++ s+ -- | The @futhark c@ action. compileCAction :: FutharkConfig -> CompilerMode -> FilePath -> Action SeqMem compileCAction fcfg mode outpath =-  Action { actionName = "Compile to OpenCL"-         , actionDescription = "Compile to OpenCL"-         , actionProcedure = helper }+  Action+    { actionName = "Compile to OpenCL",+      actionDescription = "Compile to OpenCL",+      actionProcedure = helper+    }   where     helper prog = do       cprog <- handleWarnings fcfg $ SequentialC.compileProg prog@@ -83,23 +114,32 @@           liftIO $ writeFile cpath impl         ToExecutable -> do           liftIO $ writeFile cpath $ SequentialC.asExecutable cprog-          ret <- liftIO $ runProgramWithExitCode "gcc"-                 [cpath, "-O3", "-std=c99", "-lm", "-o", outpath] mempty+          ret <-+            liftIO $+              runProgramWithExitCode+                "gcc"+                [cpath, "-O3", "-std=c99", "-lm", "-o", outpath]+                mempty           case ret of             Left err ->               externalErrorS $ "Failed to run gcc: " ++ show err             Right (ExitFailure code, _, gccerr) ->-              externalErrorS $ "gcc failed with code " ++-              show code ++ ":\n" ++ gccerr+              externalErrorS $+                "gcc failed with code "+                  ++ show code+                  ++ ":\n"+                  ++ gccerr             Right (ExitSuccess, _, _) ->               return ()  -- | The @futhark opencl@ action. compileOpenCLAction :: FutharkConfig -> CompilerMode -> FilePath -> Action KernelsMem compileOpenCLAction fcfg mode outpath =-  Action { actionName = "Compile to OpenCL"-         , actionDescription = "Compile to OpenCL"-         , actionProcedure = helper }+  Action+    { actionName = "Compile to OpenCL",+      actionDescription = "Compile to OpenCL",+      actionProcedure = helper+    }   where     helper prog = do       cprog <- handleWarnings fcfg $ COpenCL.compileProg prog@@ -107,11 +147,11 @@           hpath = outpath `addExtension` "h"           extra_options             | System.Info.os == "darwin" =-                ["-framework", "OpenCL"]+              ["-framework", "OpenCL"]             | System.Info.os == "mingw32" =-                ["-lOpenCL64"]+              ["-lOpenCL64"]             | otherwise =-                ["-lOpenCL"]+              ["-lOpenCL"]        case mode of         ToLibrary -> do@@ -120,32 +160,42 @@           liftIO $ writeFile cpath impl         ToExecutable -> do           liftIO $ writeFile cpath $ COpenCL.asExecutable cprog-          ret <- liftIO $ runProgramWithExitCode "gcc"-                 ([cpath, "-O", "-std=c99", "-lm", "-o", outpath] ++ extra_options) mempty+          ret <-+            liftIO $+              runProgramWithExitCode+                "gcc"+                ([cpath, "-O", "-std=c99", "-lm", "-o", outpath] ++ extra_options)+                mempty           case ret of             Left err ->               externalErrorS $ "Failed to run gcc: " ++ show err             Right (ExitFailure code, _, gccerr) ->-              externalErrorS $ "gcc failed with code " ++-              show code ++ ":\n" ++ gccerr+              externalErrorS $+                "gcc failed with code "+                  ++ show code+                  ++ ":\n"+                  ++ gccerr             Right (ExitSuccess, _, _) ->               return ()  -- | The @futhark cuda@ action. compileCUDAAction :: FutharkConfig -> CompilerMode -> FilePath -> Action KernelsMem compileCUDAAction fcfg mode outpath =-  Action { actionName = "Compile to CUDA"-         , actionDescription = "Compile to CUDA"-         , actionProcedure = helper }+  Action+    { actionName = "Compile to CUDA",+      actionDescription = "Compile to CUDA",+      actionProcedure = helper+    }   where     helper prog = do       cprog <- handleWarnings fcfg $ CCUDA.compileProg prog       let cpath = outpath `addExtension` "c"           hpath = outpath `addExtension` "h"-          extra_options = [ "-lcuda"-                          , "-lcudart"-                          , "-lnvrtc"-                          ]+          extra_options =+            [ "-lcuda",+              "-lcudart",+              "-lnvrtc"+            ]       case mode of         ToLibrary -> do           let (header, impl) = CCUDA.asLibrary cprog@@ -153,14 +203,18 @@           liftIO $ writeFile cpath impl         ToExecutable -> do           liftIO $ writeFile cpath $ CCUDA.asExecutable cprog-          let args = [cpath, "-O", "-std=c99", "-lm", "-o", outpath]-                     ++ extra_options+          let args =+                [cpath, "-O", "-std=c99", "-lm", "-o", outpath]+                  ++ extra_options           ret <- liftIO $ runProgramWithExitCode "gcc" args mempty           case ret of             Left err ->               externalErrorS $ "Failed to run gcc: " ++ show err             Right (ExitFailure code, _, gccerr) ->-              externalErrorS $ "gcc failed with code " ++-              show code ++ ":\n" ++ gccerr+              externalErrorS $+                "gcc failed with code "+                  ++ show code+                  ++ ":\n"+                  ++ gccerr             Right (ExitSuccess, _, _) ->               return ()
src/Futhark/Analysis/Alias.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-}+ -- | Alias analysis of a full Futhark program.  Takes as input a -- program with an arbitrary lore and produces one with aliases.  This -- module does not implement the aliasing logic itself, and derives@@ -8,102 +9,126 @@ -- here will include transitive aliases (note that this is not what -- the building blocks do). module Futhark.Analysis.Alias-       ( aliasAnalysis-         -- * Ad-hoc utilities-       , AliasTable-       , analyseFun-       , analyseStms-       , analyseExp-       , analyseBody-       , analyseLambda-       )-       where+  ( aliasAnalysis, +    -- * Ad-hoc utilities+    AliasTable,+    analyseFun,+    analyseStms,+    analyseExp,+    analyseBody,+    analyseLambda,+  )+where+ import Data.List (foldl') import qualified Data.Map as M--import Futhark.IR.Syntax import Futhark.IR.Aliases  -- | Perform alias analysis on a Futhark program.-aliasAnalysis :: (ASTLore lore, CanBeAliased (Op lore)) =>-                 Prog lore -> Prog (Aliases lore)+aliasAnalysis ::+  (ASTLore lore, CanBeAliased (Op lore)) =>+  Prog lore ->+  Prog (Aliases lore) aliasAnalysis (Prog consts funs) =   Prog (fst (analyseStms mempty consts)) (map analyseFun funs) -analyseFun :: (ASTLore lore, CanBeAliased (Op lore)) =>-              FunDef lore -> FunDef (Aliases lore)+analyseFun ::+  (ASTLore lore, CanBeAliased (Op lore)) =>+  FunDef lore ->+  FunDef (Aliases lore) analyseFun (FunDef entry attrs fname restype params body) =   FunDef entry attrs fname restype params body'-  where body' = analyseBody mempty body+  where+    body' = analyseBody mempty body  -- | Pre-existing aliases for variables.  Used to add transitive -- aliases. type AliasTable = M.Map VName Names -analyseBody :: (ASTLore lore,-                CanBeAliased (Op lore)) =>-               AliasTable -> Body lore -> Body (Aliases lore)+analyseBody ::+  ( ASTLore lore,+    CanBeAliased (Op lore)+  ) =>+  AliasTable ->+  Body lore ->+  Body (Aliases lore) analyseBody atable (Body lore stms result) =   let (stms', _atable') = analyseStms atable stms-  in mkAliasedBody lore stms' result+   in mkAliasedBody lore stms' result -analyseStms :: (ASTLore lore, CanBeAliased (Op lore)) =>-               AliasTable -> Stms lore -> (Stms (Aliases lore), AliasesAndConsumed)+analyseStms ::+  (ASTLore lore, CanBeAliased (Op lore)) =>+  AliasTable ->+  Stms lore ->+  (Stms (Aliases lore), AliasesAndConsumed) analyseStms orig_aliases =   foldl' f (mempty, (orig_aliases, mempty)) . stmsToList-  where f (stms, aliases) stm =-          let stm' = analyseStm (fst aliases) stm-              atable' = trackAliases aliases stm'-          in (stms<>oneStm stm', atable')+  where+    f (stms, aliases) stm =+      let stm' = analyseStm (fst aliases) stm+          atable' = trackAliases aliases stm'+       in (stms <> oneStm stm', atable') -analyseStm :: (ASTLore lore, CanBeAliased (Op lore)) =>-              AliasTable -> Stm lore -> Stm (Aliases lore)+analyseStm ::+  (ASTLore lore, CanBeAliased (Op lore)) =>+  AliasTable ->+  Stm lore ->+  Stm (Aliases lore) analyseStm aliases (Let pat (StmAux cs attrs dec) e) =   let e' = analyseExp aliases e       pat' = addAliasesToPattern pat e'       lore' = (AliasDec $ consumedInExp e', dec)-  in Let pat' (StmAux cs attrs lore') e'--analyseExp :: (ASTLore lore, CanBeAliased (Op lore)) =>-              AliasTable -> Exp lore -> Exp (Aliases lore)+   in Let pat' (StmAux cs attrs lore') e' +analyseExp ::+  (ASTLore lore, CanBeAliased (Op lore)) =>+  AliasTable ->+  Exp lore ->+  Exp (Aliases lore) -- Would be better to put this in a BranchType annotation, but that -- requires a lot of other work. analyseExp aliases (If cond tb fb dec) =   let Body ((tb_als, tb_cons), tb_dec) tb_stms tb_res = analyseBody aliases tb       Body ((fb_als, fb_cons), fb_dec) fb_stms fb_res = analyseBody aliases fb       cons = tb_cons <> fb_cons-      isConsumed v = any (`nameIn` unAliases cons) $-                     v : namesToList (M.findWithDefault mempty v aliases)-      notConsumed = AliasDec . namesFromList .-                    filter (not . isConsumed) .-                    namesToList . unAliases+      isConsumed v =+        any (`nameIn` unAliases cons) $+          v : namesToList (M.findWithDefault mempty v aliases)+      notConsumed =+        AliasDec . namesFromList+          . filter (not . isConsumed)+          . namesToList+          . unAliases       tb_als' = map notConsumed tb_als       fb_als' = map notConsumed fb_als       tb' = Body ((tb_als', tb_cons), tb_dec) tb_stms tb_res       fb' = Body ((fb_als', fb_cons), fb_dec) fb_stms fb_res-  in If cond tb' fb' dec-+   in If cond tb' fb' dec analyseExp aliases e = mapExp analyse e-  where analyse =-          Mapper { mapOnSubExp = return-                 , mapOnVName = return-                 , mapOnBody = const $ return . analyseBody aliases-                 , mapOnRetType = return-                 , mapOnBranchType = return-                 , mapOnFParam = return-                 , mapOnLParam = return-                 , mapOnOp = return . addOpAliases-                 }+  where+    analyse =+      Mapper+        { mapOnSubExp = return,+          mapOnVName = return,+          mapOnBody = const $ return . analyseBody aliases,+          mapOnRetType = return,+          mapOnBranchType = return,+          mapOnFParam = return,+          mapOnLParam = return,+          mapOnOp = return . addOpAliases+        } -analyseLambda :: (ASTLore lore, CanBeAliased (Op lore)) =>-                 Lambda lore -> Lambda (Aliases lore)+analyseLambda ::+  (ASTLore lore, CanBeAliased (Op lore)) =>+  Lambda lore ->+  Lambda (Aliases lore) analyseLambda lam =   -- XXX: it may cause trouble that we pass mempty to analyseBody   -- here.  However, fixing this generally involves adding an   -- AliasTable argument to addOpAliases.   let body = analyseBody mempty $ lambdaBody lam-  in lam { lambdaBody = body-         , lambdaParams = lambdaParams lam-         }+   in lam+        { lambdaBody = body,+          lambdaParams = lambdaParams lam+        }
src/Futhark/Analysis/CallGraph.hs view
@@ -1,30 +1,31 @@ {-# LANGUAGE OverloadedStrings #-}+ -- | This module exports functionality for generating a call graph of -- an Futhark program. module Futhark.Analysis.CallGraph-  ( CallGraph-  , buildCallGraph-  , isFunInCallGraph-  , calls-  , calledByConsts-  , allCalledBy-  , findNoninlined+  ( CallGraph,+    buildCallGraph,+    isFunInCallGraph,+    calls,+    calledByConsts,+    allCalledBy,+    findNoninlined,   )-  where+where  import Control.Monad.Writer.Strict+import Data.List (foldl') import qualified Data.Map.Strict as M-import qualified Data.Set as S import Data.Maybe (fromMaybe)-import Data.List (foldl')-+import qualified Data.Set as S import Futhark.IR.SOACS  type FunctionTable = M.Map Name (FunDef SOACS)  buildFunctionTable :: Prog SOACS -> FunctionTable buildFunctionTable = foldl expand M.empty . progFuns-  where expand ftab f = M.insert (funDefName f) f ftab+  where+    expand ftab f = M.insert (funDefName f) f ftab  type FunGraph = M.Map Name (S.Set Name) @@ -33,9 +34,10 @@ -- transitively!) by the function. -- -- We keep track separately of the functions called by constants.-data CallGraph = CallGraph { calledByFuns :: M.Map Name (S.Set Name)-                           , calledInConsts :: S.Set Name-                           }+data CallGraph = CallGraph+  { calledByFuns :: M.Map Name (S.Set Name),+    calledInConsts :: S.Set Name+  }  -- | Is the given function known to the call graph? isFunInCallGraph :: Name -> CallGraph -> Bool@@ -58,23 +60,24 @@ buildCallGraph :: Prog SOACS -> CallGraph buildCallGraph prog =   CallGraph fg $ buildFGStms $ progConsts prog-  where fg = foldl' (buildFGfun ftable) M.empty entry_points+  where+    fg = foldl' (buildFGfun ftable) M.empty entry_points -        entry_points = map funDefName $ progFuns prog-        ftable = buildFunctionTable prog+    entry_points = map funDefName $ progFuns prog+    ftable = buildFunctionTable prog  -- | @buildCallGraph ftable fg fname@ updates @fg@ with the -- contributions of function @fname@. buildFGfun :: FunctionTable -> FunGraph -> Name -> FunGraph-buildFGfun ftable fg fname  =+buildFGfun ftable fg fname =   -- Check if function is a non-builtin that we have not already   -- processed.   case M.lookup fname ftable of     Just f | Nothing <- M.lookup fname fg -> do-               let callees = buildFGBody $ funDefBody f-                   fg' = M.insert fname callees fg-               -- recursively build the callees-               foldl' (buildFGfun ftable) fg' callees+      let callees = buildFGBody $ funDefBody f+          fg' = M.insert fname callees fg+      -- recursively build the callees+      foldl' (buildFGfun ftable) fg' callees     _ -> fg  buildFGStms :: Stms SOACS -> S.Set Name@@ -86,41 +89,51 @@ buildFGexp :: Exp -> S.Set Name buildFGexp (Apply fname _ _ _) = S.singleton fname buildFGexp (Op op) = execWriter $ mapSOACM folder op-  where folder = identitySOACMapper {-          mapOnSOACLambda = \lam -> do tell $ buildFGBody $ lambdaBody lam-                                       return lam-          }+  where+    folder =+      identitySOACMapper+        { mapOnSOACLambda = \lam -> do+            tell $ buildFGBody $ lambdaBody lam+            return lam+        } buildFGexp e = execWriter $ mapExpM folder e-  where folder = identityMapper {-          mapOnBody = \_ body -> do tell $ buildFGBody body-                                    return body-          }+  where+    folder =+      identityMapper+        { mapOnBody = \_ body -> do+            tell $ buildFGBody body+            return body+        }  -- | The set of all functions that are called noinline somewhere, or -- have a noinline attribute on their definition. findNoninlined :: Prog SOACS -> S.Set Name findNoninlined prog =-  foldMap noinlineDef (progFuns prog) <>-  foldMap onStm (foldMap (bodyStms . funDefBody) (progFuns prog) <> progConsts prog)-  where onStm :: Stm -> S.Set Name-        onStm (Let _ aux (Apply fname _ _ _))-          | "noinline" `inAttrs` stmAuxAttrs aux =-              S.singleton fname-        onStm (Let _ _ e) = execWriter $ mapExpM folder e-          where folder =-                  identityMapper-                  { mapOnBody = \_ body -> do tell $ foldMap onStm $ bodyStms body-                                              return body-                  , mapOnOp = mapSOACM-                              identitySOACMapper-                              { mapOnSOACLambda = \lam -> do-                                  tell $ foldMap onStm $ bodyStms $ lambdaBody lam-                                  return lam-                              }-                  }+  foldMap noinlineDef (progFuns prog)+    <> foldMap onStm (foldMap (bodyStms . funDefBody) (progFuns prog) <> progConsts prog)+  where+    onStm :: Stm -> S.Set Name+    onStm (Let _ aux (Apply fname _ _ _))+      | "noinline" `inAttrs` stmAuxAttrs aux =+        S.singleton fname+    onStm (Let _ _ e) = execWriter $ mapExpM folder e+      where+        folder =+          identityMapper+            { mapOnBody = \_ body -> do+                tell $ foldMap onStm $ bodyStms body+                return body,+              mapOnOp =+                mapSOACM+                  identitySOACMapper+                    { mapOnSOACLambda = \lam -> do+                        tell $ foldMap onStm $ bodyStms $ lambdaBody lam+                        return lam+                    }+            } -        noinlineDef fd-          | "noinline" `inAttrs` funDefAttrs fd =-              S.singleton $ funDefName fd-          | otherwise =-              mempty+    noinlineDef fd+      | "noinline" `inAttrs` funDefAttrs fd =+        S.singleton $ funDefName fd+      | otherwise =+        mempty
src/Futhark/Analysis/DataDependencies.hs view
@@ -1,14 +1,14 @@ {-# LANGUAGE FlexibleContexts #-}+ -- | Facilities for inspecting the data dependencies of a program. module Futhark.Analysis.DataDependencies-  ( Dependencies-  , dataDependencies-  , findNecessaryForReturned+  ( Dependencies,+    dataDependencies,+    findNecessaryForReturned,   )-  where+where  import qualified Data.Map.Strict as M- import Futhark.IR  -- | A mapping from a variable name @v@, to those variables on which@@ -21,31 +21,39 @@ dataDependencies :: ASTLore lore => Body lore -> Dependencies dataDependencies = dataDependencies' M.empty -dataDependencies' :: ASTLore lore =>-                     Dependencies -> Body lore -> Dependencies+dataDependencies' ::+  ASTLore lore =>+  Dependencies ->+  Body lore ->+  Dependencies dataDependencies' startdeps = foldl grow startdeps . bodyStms-  where grow deps (Let pat _ (If c tb fb _)) =-          let tdeps = dataDependencies' deps tb-              fdeps = dataDependencies' deps fb-              cdeps = depsOf deps c-              comb (pe, tres, fres) =-                (patElemName pe,-                 mconcat $ [freeIn pe, cdeps, depsOf tdeps tres, depsOf fdeps fres] ++-                 map (depsOfVar deps) (namesToList $ freeIn pe))-              branchdeps =-                M.fromList $ map comb $ zip3 (patternElements pat)-                (bodyResult tb)-                (bodyResult fb)-          in M.unions [branchdeps, deps, tdeps, fdeps]--        grow deps (Let pat _ e) =-          let free = freeIn pat <> freeIn e-              freeDeps = mconcat $ map (depsOfVar deps) $ namesToList free-          in M.fromList [ (name, freeDeps) | name <- patternNames pat ] `M.union` deps+  where+    grow deps (Let pat _ (If c tb fb _)) =+      let tdeps = dataDependencies' deps tb+          fdeps = dataDependencies' deps fb+          cdeps = depsOf deps c+          comb (pe, tres, fres) =+            ( patElemName pe,+              mconcat $+                [freeIn pe, cdeps, depsOf tdeps tres, depsOf fdeps fres]+                  ++ map (depsOfVar deps) (namesToList $ freeIn pe)+            )+          branchdeps =+            M.fromList $+              map comb $+                zip3+                  (patternElements pat)+                  (bodyResult tb)+                  (bodyResult fb)+       in M.unions [branchdeps, deps, tdeps, fdeps]+    grow deps (Let pat _ e) =+      let free = freeIn pat <> freeIn e+          freeDeps = mconcat $ map (depsOfVar deps) $ namesToList free+       in M.fromList [(name, freeDeps) | name <- patternNames pat] `M.union` deps  depsOf :: Dependencies -> SubExp -> Names depsOf _ (Constant _) = mempty-depsOf deps (Var v)   = depsOfVar deps v+depsOf deps (Var v) = depsOfVar deps v  depsOfVar :: Dependencies -> VName -> Names depsOfVar deps name = oneName name <> M.findWithDefault mempty name deps@@ -56,21 +64,25 @@ -- of that parameter is live after the loop.  @deps@ is the data -- dependencies of the loop body.  This is computed by straightforward -- fixpoint iteration.-findNecessaryForReturned :: (Param dec -> Bool) -> [(Param dec, SubExp)]-                         -> M.Map VName Names-                         -> Names+findNecessaryForReturned ::+  (Param dec -> Bool) ->+  [(Param dec, SubExp)] ->+  M.Map VName Names ->+  Names findNecessaryForReturned usedAfterLoop merge_and_res allDependencies =-  iterateNecessary mempty <>-  namesFromList (map paramName $ filter usedAfterLoop $ map fst merge_and_res)-  where iterateNecessary prev_necessary-          | necessary == prev_necessary = necessary-          | otherwise                   = iterateNecessary necessary-          where necessary = mconcat $ map dependencies returnedResultSubExps-                usedAfterLoopOrNecessary param =-                  usedAfterLoop param || paramName param `nameIn` prev_necessary-                returnedResultSubExps =-                  map snd $ filter (usedAfterLoopOrNecessary . fst) merge_and_res-                dependencies (Constant _) =-                  mempty-                dependencies (Var v)      =-                  M.findWithDefault (oneName v) v allDependencies+  iterateNecessary mempty+    <> namesFromList (map paramName $ filter usedAfterLoop $ map fst merge_and_res)+  where+    iterateNecessary prev_necessary+      | necessary == prev_necessary = necessary+      | otherwise = iterateNecessary necessary+      where+        necessary = mconcat $ map dependencies returnedResultSubExps+        usedAfterLoopOrNecessary param =+          usedAfterLoop param || paramName param `nameIn` prev_necessary+        returnedResultSubExps =+          map snd $ filter (usedAfterLoopOrNecessary . fst) merge_and_res+        dependencies (Constant _) =+          mempty+        dependencies (Var v) =+          M.findWithDefault (oneName v) v allDependencies
src/Futhark/Analysis/HORep/MapNest.hs view
@@ -1,50 +1,50 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}+ module Futhark.Analysis.HORep.MapNest-  ( Nesting (..)-  , MapNest (..)-  , typeOf-  , params-  , inputs-  , setInputs-  , fromSOAC-  , toSOAC+  ( Nesting (..),+    MapNest (..),+    typeOf,+    params,+    inputs,+    setInputs,+    fromSOAC,+    toSOAC,   ) where  import Data.List (find)-import Data.Maybe import qualified Data.Map.Strict as M--import qualified Futhark.Analysis.HORep.SOAC as SOAC+import Data.Maybe import Futhark.Analysis.HORep.SOAC (SOAC)+import qualified Futhark.Analysis.HORep.SOAC as SOAC+import Futhark.Construct+import Futhark.IR hiding (typeOf) import qualified Futhark.IR.SOACS.SOAC as Futhark import Futhark.Transform.Substitute-import Futhark.IR hiding (typeOf)-import Futhark.MonadFreshNames-import Futhark.Construct -data Nesting lore = Nesting {-    nestingParamNames   :: [VName]-  , nestingResult       :: [VName]-  , nestingReturnType   :: [Type]-  , nestingWidth        :: SubExp-  } deriving (Eq, Ord, Show)+data Nesting lore = Nesting+  { nestingParamNames :: [VName],+    nestingResult :: [VName],+    nestingReturnType :: [Type],+    nestingWidth :: SubExp+  }+  deriving (Eq, Ord, Show)  data MapNest lore = MapNest SubExp (Lambda lore) [Nesting lore] [SOAC.Input]-                  deriving (Show)+  deriving (Show)  typeOf :: MapNest lore -> [Type] typeOf (MapNest w lam [] _) =   map (`arrayOfRow` w) $ lambdaReturnType lam-typeOf (MapNest w _ (nest:_) _) =+typeOf (MapNest w _ (nest : _) _) =   map (`arrayOfRow` w) $ nestingReturnType nest  params :: MapNest lore -> [VName]-params (MapNest _ lam [] _)       =+params (MapNest _ lam [] _) =   map paramName $ lambdaParams lam-params (MapNest _ _ (nest:_) _) =+params (MapNest _ _ (nest : _) _) =   nestingParamNames nest  inputs :: MapNest lore -> [SOAC.Input]@@ -52,32 +52,41 @@  setInputs :: [SOAC.Input] -> MapNest lore -> MapNest lore setInputs [] (MapNest w body ns _) = MapNest w body ns []-setInputs (inp:inps) (MapNest _ body ns _) = MapNest w body ns' (inp:inps)-  where w = arraySize 0 $ SOAC.inputType inp-        ws = drop 1 $ arrayDims $ SOAC.inputType inp-        ns' = zipWith setDepth ns ws-        setDepth n nw = n { nestingWidth = nw }+setInputs (inp : inps) (MapNest _ body ns _) = MapNest w body ns' (inp : inps)+  where+    w = arraySize 0 $ SOAC.inputType inp+    ws = drop 1 $ arrayDims $ SOAC.inputType inp+    ns' = zipWith setDepth ns ws+    setDepth n nw = n {nestingWidth = nw} -fromSOAC :: (Bindable lore, MonadFreshNames m,-             LocalScope lore m,-             Op lore ~ Futhark.SOAC lore) =>-            SOAC lore -> m (Maybe (MapNest lore))+fromSOAC ::+  ( Bindable lore,+    MonadFreshNames m,+    LocalScope lore m,+    Op lore ~ Futhark.SOAC lore+  ) =>+  SOAC lore ->+  m (Maybe (MapNest lore)) fromSOAC = fromSOAC' mempty -fromSOAC' :: (Bindable lore, MonadFreshNames m,-              LocalScope lore m,-              Op lore ~ Futhark.SOAC lore) =>-             [Ident]-          -> SOAC lore-          -> m (Maybe (MapNest lore))-+fromSOAC' ::+  ( Bindable lore,+    MonadFreshNames m,+    LocalScope lore m,+    Op lore ~ Futhark.SOAC lore+  ) =>+  [Ident] ->+  SOAC lore ->+  m (Maybe (MapNest lore)) fromSOAC' bound (SOAC.Screma w (SOAC.ScremaForm [] [] lam) inps) = do-  maybenest <- case (stmsToList $ bodyStms $ lambdaBody lam,-                     bodyResult $ lambdaBody lam) of-    ([Let pat _ e], res) | res == map Var (patternNames pat) ->-      localScope (scopeOfLParams $ lambdaParams lam) $-      SOAC.fromExp e >>=-      either (return . Left) (fmap (Right . fmap (pat,)) . fromSOAC' bound')+  maybenest <- case ( stmsToList $ bodyStms $ lambdaBody lam,+                      bodyResult $ lambdaBody lam+                    ) of+    ([Let pat _ e], res)+      | res == map Var (patternNames pat) ->+        localScope (scopeOfLParams $ lambdaParams lam) $+          SOAC.fromExp e+            >>= either (return . Left) (fmap (Right . fmap (pat,)) . fromSOAC' bound')     _ ->       return $ Right Nothing @@ -85,72 +94,93 @@     -- Do we have a nested MapNest?     Right (Just (pat, mn@(MapNest inner_w body' ns' inps'))) -> do       (ps, inps'') <--        unzip <$>-        fixInputs w (zip (map paramName $ lambdaParams lam) inps)-        (zip (params mn) inps')-      let n' = Nesting { nestingParamNames = ps-                       , nestingResult     = patternNames pat-                       , nestingReturnType = typeOf mn-                       , nestingWidth      = inner_w-                       }-      return $ Just $ MapNest w body' (n':ns') inps''+        unzip+          <$> fixInputs+            w+            (zip (map paramName $ lambdaParams lam) inps)+            (zip (params mn) inps')+      let n' =+            Nesting+              { nestingParamNames = ps,+                nestingResult = patternNames pat,+                nestingReturnType = typeOf mn,+                nestingWidth = inner_w+              }+      return $ Just $ MapNest w body' (n' : ns') inps''     -- No nested MapNest it seems.     _ -> do       let isBound name-            | Just param <- find ((name==) . identName) bound =+            | Just param <- find ((name ==) . identName) bound =               Just param             | otherwise =               Nothing           boundUsedInBody =             mapMaybe isBound $ namesToList $ freeIn lam-      newParams <- mapM (newIdent' (++"_wasfree")) boundUsedInBody-      let subst = M.fromList $-                  zip (map identName boundUsedInBody) (map identName newParams)-          inps' = inps ++-                  map (SOAC.addTransform (SOAC.Replicate mempty $ Shape [w]) . SOAC.identInput)-                  boundUsedInBody+      newParams <- mapM (newIdent' (++ "_wasfree")) boundUsedInBody+      let subst =+            M.fromList $+              zip (map identName boundUsedInBody) (map identName newParams)+          inps' =+            inps+              ++ map+                (SOAC.addTransform (SOAC.Replicate mempty $ Shape [w]) . SOAC.identInput)+                boundUsedInBody           lam' =-            lam { lambdaBody =-                    substituteNames subst $ lambdaBody lam-                , lambdaParams =-                    lambdaParams lam ++ [ Param name t-                                        | Ident name t <- newParams ]-                }+            lam+              { lambdaBody =+                  substituteNames subst $ lambdaBody lam,+                lambdaParams =+                  lambdaParams lam+                    ++ [ Param name t+                         | Ident name t <- newParams+                       ]+              }       return $ Just $ MapNest w lam' [] inps'-  where bound' = bound <> map paramIdent (lambdaParams lam)-+  where+    bound' = bound <> map paramIdent (lambdaParams lam) fromSOAC' _ _ = return Nothing -toSOAC :: (MonadFreshNames m, HasScope lore m,-           Bindable lore, BinderOps lore, Op lore ~ Futhark.SOAC lore) =>-          MapNest lore -> m (SOAC lore)+toSOAC ::+  ( MonadFreshNames m,+    HasScope lore m,+    Bindable lore,+    BinderOps lore,+    Op lore ~ Futhark.SOAC lore+  ) =>+  MapNest lore ->+  m (SOAC lore) toSOAC (MapNest w lam [] inps) =   return $ SOAC.Screma w (Futhark.mapSOAC lam) inps-toSOAC (MapNest w lam (Nesting npnames nres nrettype nw:ns) inps) = do+toSOAC (MapNest w lam (Nesting npnames nres nrettype nw : ns) inps) = do   let nparams = zipWith Param npnames $ map SOAC.inputRowType inps-  body <- runBodyBinder $ localScope (scopeOfLParams nparams) $ do-    letBindNames nres =<< SOAC.toExp =<<-      toSOAC (MapNest nw lam ns $ map (SOAC.identInput . paramIdent) nparams)-    return $ resultBody $ map Var nres-  let outerlam = Lambda { lambdaParams = nparams-                        , lambdaBody = body-                        , lambdaReturnType = nrettype-                        }+  body <- runBodyBinder $+    localScope (scopeOfLParams nparams) $ do+      letBindNames nres =<< SOAC.toExp+        =<< toSOAC (MapNest nw lam ns $ map (SOAC.identInput . paramIdent) nparams)+      return $ resultBody $ map Var nres+  let outerlam =+        Lambda+          { lambdaParams = nparams,+            lambdaBody = body,+            lambdaReturnType = nrettype+          }   return $ SOAC.Screma w (Futhark.mapSOAC outerlam) inps -fixInputs :: MonadFreshNames m =>-             SubExp -> [(VName, SOAC.Input)] -> [(VName, SOAC.Input)]-          -> m [(VName, SOAC.Input)]+fixInputs ::+  MonadFreshNames m =>+  SubExp ->+  [(VName, SOAC.Input)] ->+  [(VName, SOAC.Input)] ->+  m [(VName, SOAC.Input)] fixInputs w ourInps = mapM inspect   where     isParam x (y, _) = x == y      inspect (_, SOAC.Input ts v _)-      | Just (p,pInp) <- find (isParam v) ourInps = do-          let pInp' = SOAC.transformRows ts pInp-          p' <- newNameFromString $ baseString p-          return (p', pInp')-+      | Just (p, pInp) <- find (isParam v) ourInps = do+        let pInp' = SOAC.transformRows ts pInp+        p' <- newNameFromString $ baseString p+        return (p', pInp')     inspect (param, SOAC.Input ts a t) = do       param' <- newNameFromString (baseString param ++ "_rep")       return (param', SOAC.Input (ts SOAC.|> SOAC.Replicate mempty (Shape [w])) a t)
src/Futhark/Analysis/HORep/SOAC.hs view
@@ -1,5 +1,6 @@-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeFamilies #-}+ -- | High-level representation of SOACs.  When performing -- SOAC-transformations, operating on normal 'Exp' values is somewhat -- of a nuisance, as they can represent terms that are not proper@@ -21,80 +22,94 @@ -- import qualified Futhark.Analysis.HORep.SOAC as SOAC -- @ module Futhark.Analysis.HORep.SOAC-  (-   -- * SOACs-    SOAC (..)-  , Futhark.ScremaForm(..)-  , inputs-  , setInputs-  , lambda-  , setLambda-  , typeOf-  , width-  -- ** Converting to and from expressions-  , NotSOAC (..)-  , fromExp-  , toExp-  , toSOAC-  -- * SOAC inputs-  , Input (..)-  , varInput-  , identInput-  , isVarInput-  , isVarishInput-  , addTransform-  , addInitialTransforms-  , inputArray-  , inputRank-  , inputType-  , inputRowType-  , transformRows-  , transposeInput-  -- ** Input transformations-  , ArrayTransforms-  , noTransforms-  , nullTransforms-  , (|>)-  , (<|)-  , viewf-  , ViewF(..)-  , viewl-  , ViewL(..)-  , ArrayTransform(..)-  , transformFromExp-  , soacToStream+  ( -- * SOACs+    SOAC (..),+    Futhark.ScremaForm (..),+    inputs,+    setInputs,+    lambda,+    setLambda,+    typeOf,+    width,++    -- ** Converting to and from expressions+    NotSOAC (..),+    fromExp,+    toExp,+    toSOAC,++    -- * SOAC inputs+    Input (..),+    varInput,+    identInput,+    isVarInput,+    isVarishInput,+    addTransform,+    addInitialTransforms,+    inputArray,+    inputRank,+    inputType,+    inputRowType,+    transformRows,+    transposeInput,++    -- ** Input transformations+    ArrayTransforms,+    noTransforms,+    nullTransforms,+    (|>),+    (<|),+    viewf,+    ViewF (..),+    viewl,+    ViewL (..),+    ArrayTransform (..),+    transformFromExp,+    soacToStream,   )-  where+where  import Data.Foldable as Foldable import Data.Maybe import qualified Data.Sequence as Seq-+import Futhark.Construct hiding (toExp)+import Futhark.IR hiding+  ( Iota,+    Rearrange,+    Replicate,+    Reshape,+    Var,+    typeOf,+  ) import qualified Futhark.IR as Futhark import Futhark.IR.SOACS.SOAC-  (StreamForm(..), ScremaForm(..), scremaType, getStreamAccums, HistOp(..), StreamOrd(..))+  ( HistOp (..),+    ScremaForm (..),+    StreamForm (..),+    StreamOrd (..),+    getStreamAccums,+    scremaType,+  ) import qualified Futhark.IR.SOACS.SOAC as Futhark-import Futhark.IR-  hiding (Var, Iota, Rearrange, Reshape, Replicate, typeOf)-import Futhark.Transform.Substitute-import Futhark.Construct hiding (toExp) import Futhark.Transform.Rename (renameLambda)-import qualified Futhark.Util.Pretty as PP+import Futhark.Transform.Substitute import Futhark.Util.Pretty (ppr, text)+import qualified Futhark.Util.Pretty as PP  -- | A single, simple transformation.  If you want several, don't just -- create a list, use 'ArrayTransforms' instead.-data ArrayTransform = Rearrange Certificates [Int]-                    -- ^ A permutation of an otherwise valid input.-                    | Reshape Certificates (ShapeChange SubExp)-                    -- ^ A reshaping of an otherwise valid input.-                    | ReshapeOuter Certificates (ShapeChange SubExp)-                    -- ^ A reshaping of the outer dimension.-                    | ReshapeInner Certificates (ShapeChange SubExp)-                    -- ^ A reshaping of everything but the outer dimension.-                    | Replicate Certificates Shape-                    -- ^ Replicate the rows of the array a number of times.-                      deriving (Show, Eq, Ord)+data ArrayTransform+  = -- | A permutation of an otherwise valid input.+    Rearrange Certificates [Int]+  | -- | A reshaping of an otherwise valid input.+    Reshape Certificates (ShapeChange SubExp)+  | -- | A reshaping of the outer dimension.+    ReshapeOuter Certificates (ShapeChange SubExp)+  | -- | A reshaping of everything but the outer dimension.+    ReshapeInner Certificates (ShapeChange SubExp)+  | -- | Replicate the rows of the array a number of times.+    Replicate Certificates Shape+  deriving (Show, Eq, Ord)  instance Substitute ArrayTransform where   substituteNames substs (Rearrange cs xs) =@@ -124,8 +139,8 @@  instance Semigroup ArrayTransforms where   ts1 <> ts2 = case viewf ts2 of-                 t :< ts2' -> (ts1 |> t) <> ts2'-                 EmptyF    -> ts1+    t :< ts2' -> (ts1 |> t) <> ts2'+    EmptyF -> ts1  instance Monoid ArrayTransforms where   mempty = noTransforms@@ -145,59 +160,67 @@ -- | Decompose the input-end of the transformation sequence. viewf :: ArrayTransforms -> ViewF viewf (ArrayTransforms s) = case Seq.viewl s of-                              t Seq.:< s' -> t :< ArrayTransforms s'-                              Seq.EmptyL  -> EmptyF+  t Seq.:< s' -> t :< ArrayTransforms s'+  Seq.EmptyL -> EmptyF  -- | A view of the first transformation to be applied.-data ViewF = EmptyF-           | ArrayTransform :< ArrayTransforms+data ViewF+  = EmptyF+  | ArrayTransform :< ArrayTransforms  -- | Decompose the output-end of the transformation sequence. viewl :: ArrayTransforms -> ViewL viewl (ArrayTransforms s) = case Seq.viewr s of-                              s' Seq.:> t -> ArrayTransforms s' :> t-                              Seq.EmptyR  -> EmptyL+  s' Seq.:> t -> ArrayTransforms s' :> t+  Seq.EmptyR -> EmptyL  -- | A view of the last transformation to be applied.-data ViewL = EmptyL-           | ArrayTransforms :> ArrayTransform+data ViewL+  = EmptyL+  | ArrayTransforms :> ArrayTransform  -- | Add a transform to the end of the transformation list. (|>) :: ArrayTransforms -> ArrayTransform -> ArrayTransforms (|>) = flip $ addTransform' extract add $ uncurry (flip (,))-   where extract ts' = case viewl ts' of-                         EmptyL     -> Nothing-                         ts'' :> t' -> Just (t', ts'')-         add t' (ArrayTransforms ts') = ArrayTransforms $ ts' Seq.|> t'+  where+    extract ts' = case viewl ts' of+      EmptyL -> Nothing+      ts'' :> t' -> Just (t', ts'')+    add t' (ArrayTransforms ts') = ArrayTransforms $ ts' Seq.|> t'  -- | Add a transform at the beginning of the transformation list. (<|) :: ArrayTransform -> ArrayTransforms -> ArrayTransforms (<|) = addTransform' extract add id-   where extract ts' = case viewf ts' of-                         EmptyF     -> Nothing-                         t' :< ts'' -> Just (t', ts'')-         add t' (ArrayTransforms ts') = ArrayTransforms $ t' Seq.<| ts'+  where+    extract ts' = case viewf ts' of+      EmptyF -> Nothing+      t' :< ts'' -> Just (t', ts'')+    add t' (ArrayTransforms ts') = ArrayTransforms $ t' Seq.<| ts' -addTransform' :: (ArrayTransforms -> Maybe (ArrayTransform, ArrayTransforms))-              -> (ArrayTransform -> ArrayTransforms -> ArrayTransforms)-              -> ((ArrayTransform,ArrayTransform) -> (ArrayTransform,ArrayTransform))-              -> ArrayTransform -> ArrayTransforms-              -> ArrayTransforms+addTransform' ::+  (ArrayTransforms -> Maybe (ArrayTransform, ArrayTransforms)) ->+  (ArrayTransform -> ArrayTransforms -> ArrayTransforms) ->+  ((ArrayTransform, ArrayTransform) -> (ArrayTransform, ArrayTransform)) ->+  ArrayTransform ->+  ArrayTransforms ->+  ArrayTransforms addTransform' extract add swap t ts =   fromMaybe (t `add` ts) $ do     (t', ts') <- extract ts     combined <- uncurry combineTransforms $ swap (t', t)-    Just $ if identityTransform combined then ts'-           else addTransform' extract add swap combined ts'+    Just $+      if identityTransform combined+        then ts'+        else addTransform' extract add swap combined ts'  identityTransform :: ArrayTransform -> Bool identityTransform (Rearrange _ perm) =-  Foldable.and $ zipWith (==) perm [0..]+  Foldable.and $ zipWith (==) perm [0 ..] identityTransform _ = False  combineTransforms :: ArrayTransform -> ArrayTransform -> Maybe ArrayTransform combineTransforms (Rearrange cs2 perm2) (Rearrange cs1 perm1) =-  Just $ Rearrange (cs1<>cs2) $ perm2 `rearrangeCompose` perm1+  Just $ Rearrange (cs1 <> cs2) $ perm2 `rearrangeCompose` perm1 combineTransforms _ _ = Nothing  -- | Given an expression, determine whether the expression represents@@ -219,17 +242,20 @@ -- plain expressions.  The transforms are done left-to-right, that is, -- the first element of the 'ArrayTransform' list is applied first. data Input = Input ArrayTransforms VName Type-             deriving (Show, Eq, Ord)+  deriving (Show, Eq, Ord)  instance Substitute Input where   substituteNames substs (Input ts v t) =-    Input (substituteNames substs ts)-    (substituteNames substs v) (substituteNames substs t)+    Input+      (substituteNames substs ts)+      (substituteNames substs v)+      (substituteNames substs t)  -- | Create a plain array variable input with no transformations. varInput :: HasScope t f => VName -> f Input varInput v = withType <$> lookupType v-  where withType = Input (ArrayTransforms Seq.empty) v+  where+    withType = Input (ArrayTransforms Seq.empty) v  -- | Create a plain array variable input with no transformations, from an 'Ident'. identInput :: Ident -> Input@@ -239,15 +265,16 @@ -- return the variable. isVarInput :: Input -> Maybe VName isVarInput (Input ts v _) | nullTransforms ts = Just v-isVarInput _                                  = Nothing+isVarInput _ = Nothing  -- | If the given input is a plain variable input, with no non-vacuous transforms, -- return the variable. isVarishInput :: Input -> Maybe VName isVarishInput (Input ts v t)   | nullTransforms ts = Just v-  | Reshape cs [DimCoercion _] :< ts' <- viewf ts, cs == mempty =-      isVarishInput $ Input ts' v t+  | Reshape cs [DimCoercion _] :< ts' <- viewf ts,+    cs == mempty =+    isVarishInput $ Input ts' v t isVarishInput _ = Nothing  -- | Add a transformation to the end of the transformation list.@@ -261,33 +288,32 @@ addInitialTransforms ts (Input ots a t) = Input (ts <> ots) a t  -- | Convert SOAC inputs to the corresponding expressions.-inputsToSubExps :: (MonadBinder m) =>-                   [Input] -> m [VName]+inputsToSubExps ::+  (MonadBinder m) =>+  [Input] ->+  m [VName] inputsToSubExps = mapM inputToExp'-  where inputToExp' (Input (ArrayTransforms ts) a _) =-          foldlM transform a ts--        transform ia (Replicate cs n) =-          certifying cs $-          letExp "repeat" $ BasicOp $ Futhark.Replicate n (Futhark.Var ia)--        transform ia (Rearrange cs perm) =-          certifying cs $-          letExp "rearrange" $ BasicOp $ Futhark.Rearrange perm ia--        transform ia (Reshape cs shape) =-          certifying cs $-          letExp "reshape" $ BasicOp $ Futhark.Reshape shape ia--        transform ia (ReshapeOuter cs shape) = do-          shape' <- reshapeOuter shape 1 . arrayShape <$> lookupType ia-          certifying cs $-            letExp "reshape_outer" $ BasicOp $ Futhark.Reshape shape' ia+  where+    inputToExp' (Input (ArrayTransforms ts) a _) =+      foldlM transform a ts -        transform ia (ReshapeInner cs shape) = do-          shape' <- reshapeInner shape 1 . arrayShape <$> lookupType ia-          certifying cs $-            letExp "reshape_inner" $ BasicOp $ Futhark.Reshape shape' ia+    transform ia (Replicate cs n) =+      certifying cs $+        letExp "repeat" $ BasicOp $ Futhark.Replicate n (Futhark.Var ia)+    transform ia (Rearrange cs perm) =+      certifying cs $+        letExp "rearrange" $ BasicOp $ Futhark.Rearrange perm ia+    transform ia (Reshape cs shape) =+      certifying cs $+        letExp "reshape" $ BasicOp $ Futhark.Reshape shape ia+    transform ia (ReshapeOuter cs shape) = do+      shape' <- reshapeOuter shape 1 . arrayShape <$> lookupType ia+      certifying cs $+        letExp "reshape_outer" $ BasicOp $ Futhark.Reshape shape' ia+    transform ia (ReshapeInner cs shape) = do+      shape' <- reshapeInner shape 1 . arrayShape <$> lookupType ia+      certifying cs $+        letExp "reshape_inner" $ BasicOp $ Futhark.Reshape shape' ia  -- | Return the array name of the input. inputArray :: Input -> VName@@ -297,18 +323,19 @@ inputType :: Input -> Type inputType (Input (ArrayTransforms ts) _ at) =   Foldable.foldl transformType at ts-  where transformType t (Replicate _ shape) =-          arrayOfShape t shape-        transformType t (Rearrange _ perm) =-          rearrangeType perm t-        transformType t (Reshape _ shape) =-          t `setArrayShape` newShape shape-        transformType t (ReshapeOuter _ shape) =-          let Shape oldshape = arrayShape t-          in t `setArrayShape` Shape (newDims shape ++ drop 1 oldshape)-        transformType t (ReshapeInner _ shape) =-          let Shape oldshape = arrayShape t-          in t `setArrayShape` Shape (take 1 oldshape ++ newDims shape)+  where+    transformType t (Replicate _ shape) =+      arrayOfShape t shape+    transformType t (Rearrange _ perm) =+      rearrangeType perm t+    transformType t (Reshape _ shape) =+      t `setArrayShape` newShape shape+    transformType t (ReshapeOuter _ shape) =+      let Shape oldshape = arrayShape t+       in t `setArrayShape` Shape (newDims shape ++ drop 1 oldshape)+    transformType t (ReshapeInner _ shape) =+      let Shape oldshape = arrayShape t+       in t `setArrayShape` Shape (take 1 oldshape ++ newDims shape)  -- | Return the row type of an input.  Just a convenient alias. inputRowType :: Input -> Type@@ -323,47 +350,51 @@ transformRows :: ArrayTransforms -> Input -> Input transformRows (ArrayTransforms ts) =   flip (Foldable.foldl transformRows') ts-  where transformRows' inp (Rearrange cs perm) =-          addTransform (Rearrange cs (0:map (+1) perm)) inp-        transformRows' inp (Reshape cs shape) =-          addTransform (ReshapeInner cs shape) inp-        transformRows' inp (Replicate cs n)-          | inputRank inp == 1 =-            Rearrange mempty [1,0] `addTransform`-            (Replicate cs n `addTransform` inp)-          | otherwise =-            Rearrange mempty (2:0:1:[3..inputRank inp]) `addTransform`-            (Replicate cs n `addTransform`-             (Rearrange mempty (1:0:[2..inputRank inp-1]) `addTransform` inp))-        transformRows' inp nts =-          error $ "transformRows: Cannot transform this yet:\n" ++ show nts ++ "\n" ++ show inp+  where+    transformRows' inp (Rearrange cs perm) =+      addTransform (Rearrange cs (0 : map (+ 1) perm)) inp+    transformRows' inp (Reshape cs shape) =+      addTransform (ReshapeInner cs shape) inp+    transformRows' inp (Replicate cs n)+      | inputRank inp == 1 =+        Rearrange mempty [1, 0]+          `addTransform` (Replicate cs n `addTransform` inp)+      | otherwise =+        Rearrange mempty (2 : 0 : 1 : [3 .. inputRank inp])+          `addTransform` ( Replicate cs n+                             `addTransform` (Rearrange mempty (1 : 0 : [2 .. inputRank inp -1]) `addTransform` inp)+                         )+    transformRows' inp nts =+      error $ "transformRows: Cannot transform this yet:\n" ++ show nts ++ "\n" ++ show inp  -- | Add to the input a 'Rearrange' transform that performs an @(k,n)@ -- transposition.  The new transform will be at the end of the current -- transformation list. transposeInput :: Int -> Int -> Input -> Input transposeInput k n inp =-  addTransform (Rearrange mempty $ transposeIndex k n [0..inputRank inp-1]) inp+  addTransform (Rearrange mempty $ transposeIndex k n [0 .. inputRank inp -1]) inp  -- | A definite representation of a SOAC expression.-data SOAC lore = Stream SubExp (StreamForm lore) (Lambda lore) [Input]-               | Scatter SubExp (Lambda lore) [Input] [(SubExp, Int, VName)]-               | Screma SubExp (ScremaForm lore) [Input]-               | Hist SubExp [HistOp lore] (Lambda lore) [Input]-            deriving (Eq, Show)+data SOAC lore+  = Stream SubExp (StreamForm lore) (Lambda lore) [Input]+  | Scatter SubExp (Lambda lore) [Input] [(SubExp, Int, VName)]+  | Screma SubExp (ScremaForm lore) [Input]+  | Hist SubExp [HistOp lore] (Lambda lore) [Input]+  deriving (Eq, Show)  instance PP.Pretty Input where   ppr (Input (ArrayTransforms ts) arr _) = foldl f (ppr arr) ts-    where f e (Rearrange cs perm) =-            text "rearrange" <> ppr cs <> PP.apply [PP.apply (map ppr perm), e]-          f e (Reshape cs shape) =-            text "reshape" <> ppr cs <> PP.apply [PP.apply (map ppr shape), e]-          f e (ReshapeOuter cs shape) =-            text "reshape_outer" <> ppr cs <> PP.apply [PP.apply (map ppr shape), e]-          f e (ReshapeInner cs shape) =-            text "reshape_inner" <> ppr cs <> PP.apply [PP.apply (map ppr shape), e]-          f e (Replicate cs ne) =-            text "replicate" <> ppr cs <> PP.apply [ppr ne, e]+    where+      f e (Rearrange cs perm) =+        text "rearrange" <> ppr cs <> PP.apply [PP.apply (map ppr perm), e]+      f e (Reshape cs shape) =+        text "reshape" <> ppr cs <> PP.apply [PP.apply (map ppr shape), e]+      f e (ReshapeOuter cs shape) =+        text "reshape_outer" <> ppr cs <> PP.apply [PP.apply (map ppr shape), e]+      f e (ReshapeInner cs shape) =+        text "reshape_inner" <> ppr cs <> PP.apply [PP.apply (map ppr shape), e]+      f e (Replicate cs ne) =+        text "replicate" <> ppr cs <> PP.apply [ppr ne, e]  instance PrettyLore lore => PP.Pretty (SOAC lore) where   ppr (Screma w form arrs) = Futhark.ppScrema w form arrs@@ -373,9 +404,9 @@  -- | Returns the inputs used in a SOAC. inputs :: SOAC lore -> [Input]-inputs (Stream   _ _ _     arrs) = arrs-inputs (Scatter  _len _lam ivs _as) = ivs-inputs (Screma _ _       arrs) = arrs+inputs (Stream _ _ _ arrs) = arrs+inputs (Scatter _len _lam ivs _as) = ivs+inputs (Screma _ _ arrs) = arrs inputs (Hist _ _ _ inps) = inps  -- | Set the inputs to a SOAC.@@ -391,11 +422,11 @@  newWidth :: [Input] -> SubExp -> SubExp newWidth [] w = w-newWidth (inp:_) _ = arraySize 0 $ inputType inp+newWidth (inp : _) _ = arraySize 0 $ inputType inp  -- | The lambda used in a given SOAC. lambda :: SOAC lore -> Lambda lore-lambda (Stream  _ _ lam      _) = lam+lambda (Stream _ _ lam _) = lam lambda (Scatter _len lam _ivs _as) = lam lambda (Screma _ (ScremaForm _ _ lam) _) = lam lambda (Hist _ _ lam _) = lam@@ -414,16 +445,19 @@ -- | The return type of a SOAC. typeOf :: SOAC lore -> [Type] typeOf (Stream w form lam _) =-  let nes     = getStreamAccums form+  let nes = getStreamAccums form       accrtps = take (length nes) $ lambdaReturnType lam-      arrtps  = [ arrayOf (stripArray 1 t) (Shape [w]) NoUniqueness-                  | t <- drop (length nes) (lambdaReturnType lam) ]-  in  accrtps ++ arrtps+      arrtps =+        [ arrayOf (stripArray 1 t) (Shape [w]) NoUniqueness+          | t <- drop (length nes) (lambdaReturnType lam)+        ]+   in accrtps ++ arrtps typeOf (Scatter _w lam _ivs dests) =   zipWith arrayOfRow (drop (n `div` 2) lam_ts) aws-  where lam_ts = lambdaReturnType lam-        n = length lam_ts-        (aws, _, _) = unzip3 dests+  where+    lam_ts = lambdaReturnType lam+    n = length lam_ts+    (aws, _, _) = unzip3 dests typeOf (Screma w form _) =   scremaType w form typeOf (Hist _ ops _ _) = do@@ -439,13 +473,17 @@ width (Hist w _ _ _) = w  -- | Convert a SOAC to the corresponding expression.-toExp :: (MonadBinder m, Op (Lore m) ~ Futhark.SOAC (Lore m)) =>-         SOAC (Lore m) -> m (Exp (Lore m))+toExp ::+  (MonadBinder m, Op (Lore m) ~ Futhark.SOAC (Lore m)) =>+  SOAC (Lore m) ->+  m (Exp (Lore m)) toExp soac = Op <$> toSOAC soac  -- | Convert a SOAC to a Futhark-level SOAC.-toSOAC :: MonadBinder m =>-          SOAC (Lore m) -> m (Futhark.SOAC (Lore m))+toSOAC ::+  MonadBinder m =>+  SOAC (Lore m) ->+  m (Futhark.SOAC (Lore m)) toSOAC (Stream w form lam inps) =   Futhark.Stream w form lam <$> inputsToSubExps inps toSOAC (Scatter len lam ivs dests) = do@@ -458,14 +496,18 @@  -- | The reason why some expression cannot be converted to a 'SOAC' -- value.-data NotSOAC = NotSOAC -- ^ The expression is not a (tuple-)SOAC at all.-               deriving (Show)+data NotSOAC+  = -- | The expression is not a (tuple-)SOAC at all.+    NotSOAC+  deriving (Show)  -- | Either convert an expression to the normalised SOAC -- representation, or a reason why the expression does not have the -- valid form.-fromExp :: (Op lore ~ Futhark.SOAC lore, HasScope lore m) =>-           Exp lore -> m (Either NotSOAC (SOAC lore))+fromExp ::+  (Op lore ~ Futhark.SOAC lore, HasScope lore m) =>+  Exp lore ->+  m (Either NotSOAC (SOAC lore)) fromExp (Op (Futhark.Stream w form lam as)) =   Right . Stream w form lam <$> traverse varInput as fromExp (Op (Futhark.Scatter len lam ivs as)) =@@ -479,159 +521,213 @@ -- | To-Stream translation of SOACs. --   Returns the Stream SOAC and the --   extra-accumulator body-result ident if any.-soacToStream :: (MonadFreshNames m, Bindable lore, Op lore ~ Futhark.SOAC lore) =>-                SOAC lore -> m (SOAC lore,[Ident])+soacToStream ::+  (MonadFreshNames m, Bindable lore, Op lore ~ Futhark.SOAC lore) =>+  SOAC lore ->+  m (SOAC lore, [Ident]) soacToStream soac = do-  chunk_param <- newParam "chunk" $ Prim int32-  let chvar= Futhark.Var $ paramName chunk_param+  chunk_param <- newParam "chunk" $ Prim int64+  let chvar = Futhark.Var $ paramName chunk_param       (lam, inps) = (lambda soac, inputs soac)       w = width soac-  lam'     <- renameLambda lam-  let arrrtps= mapType w lam+  lam' <- renameLambda lam+  let arrrtps = mapType w lam       -- the chunked-outersize of the array result and input types-      loutps = [ arrayOfRow t chvar | t <- map rowType   arrrtps ]-      lintps = [ arrayOfRow t chvar | t <- map inputRowType inps ]+      loutps = [arrayOfRow t chvar | t <- map rowType arrrtps]+      lintps = [arrayOfRow t chvar | t <- map inputRowType inps]    strm_inpids <- mapM (newParam "inp") lintps   -- Treat each SOAC case individually:   case soac of     Screma _ form _       | Just _ <- Futhark.isMapSOAC form -> do-      -- Map(f,a) => is translated in strem's body to:-      -- let strm_resids = map(f,a_ch) in strm_resids-      ---      -- array result and input IDs of the stream's lambda-      strm_resids <- mapM (newIdent "res") loutps-      let insoac = Futhark.Screma chvar (Futhark.mapSOAC lam') $ map paramName strm_inpids-          insbnd = mkLet [] strm_resids $ Op insoac-          strmbdy= mkBody (oneStm insbnd) $ map (Futhark.Var . identName) strm_resids-          strmpar= chunk_param:strm_inpids-          strmlam= Lambda strmpar strmbdy loutps-          empty_lam = Lambda [] (mkBody mempty []) []-      -- map(f,a) creates a stream with NO accumulators-      return (Stream w (Parallel Disorder Commutative empty_lam []) strmlam inps, [])-+        -- Map(f,a) => is translated in strem's body to:+        -- let strm_resids = map(f,a_ch) in strm_resids+        --+        -- array result and input IDs of the stream's lambda+        strm_resids <- mapM (newIdent "res") loutps+        let insoac = Futhark.Screma chvar (Futhark.mapSOAC lam') $ map paramName strm_inpids+            insbnd = mkLet [] strm_resids $ Op insoac+            strmbdy = mkBody (oneStm insbnd) $ map (Futhark.Var . identName) strm_resids+            strmpar = chunk_param : strm_inpids+            strmlam = Lambda strmpar strmbdy loutps+            empty_lam = Lambda [] (mkBody mempty []) []+        -- map(f,a) creates a stream with NO accumulators+        return (Stream w (Parallel Disorder Commutative empty_lam []) strmlam inps, [])       | Just (scans, _) <- Futhark.isScanomapSOAC form,         Futhark.Scan scan_lam nes <- Futhark.singleScan scans -> do-      -- scanomap(scan_lam,nes,map_lam,a) => is translated in strem's body to:-      -- 1. let (scan0_ids,map_resids)   = scanomap(scan_lam, nes, map_lam, a_ch)-      -- 2. let strm_resids = map (acc `+`,nes, scan0_ids)-      -- 3. let outerszm1id = sizeof(0,strm_resids) - 1-      -- 4. let lasteel_ids = if outerszm1id < 0-      --                      then nes-      --                      else strm_resids[outerszm1id]-      -- 5. let acc'        = acc + lasteel_ids-      --    {acc', strm_resids, map_resids}-      -- the array and accumulator result types-      let scan_arr_ts = map (`arrayOfRow` chvar) $ lambdaReturnType scan_lam-          map_arr_ts = drop (length nes) loutps-          accrtps = lambdaReturnType scan_lam--      -- array result and input IDs of the stream's lambda-      strm_resids <- mapM (newIdent "res") scan_arr_ts-      scan0_ids <- mapM (newIdent "resarr0") scan_arr_ts-      map_resids <- mapM (newIdent "map_res") map_arr_ts+        -- scanomap(scan_lam,nes,map_lam,a) => is translated in strem's body to:+        -- 1. let (scan0_ids,map_resids)   = scanomap(scan_lam, nes, map_lam, a_ch)+        -- 2. let strm_resids = map (acc `+`,nes, scan0_ids)+        -- 3. let outerszm1id = sizeof(0,strm_resids) - 1+        -- 4. let lasteel_ids = if outerszm1id < 0+        --                      then nes+        --                      else strm_resids[outerszm1id]+        -- 5. let acc'        = acc + lasteel_ids+        --    {acc', strm_resids, map_resids}+        -- the array and accumulator result types+        let scan_arr_ts = map (`arrayOfRow` chvar) $ lambdaReturnType scan_lam+            map_arr_ts = drop (length nes) loutps+            accrtps = lambdaReturnType scan_lam -      lastel_ids <- mapM (newIdent "lstel") accrtps-      lastel_tmp_ids <- mapM (newIdent "lstel_tmp") accrtps-      empty_arr <- newIdent "empty_arr" $ Prim Bool-      inpacc_ids <- mapM (newParam "inpacc") accrtps-      outszm1id  <- newIdent "szm1" $ Prim int32-      -- 1. let (scan0_ids,map_resids)  = scanomap(scan_lam,nes,map_lam,a_ch)-      let insbnd = mkLet [] (scan0_ids++map_resids) $ Op $-                   Futhark.Screma chvar (Futhark.scanomapSOAC [Futhark.Scan scan_lam nes] lam') $-                   map paramName strm_inpids-      -- 2. let outerszm1id = chunksize - 1-          outszm1bnd = mkLet [] [outszm1id] $ BasicOp $-                       BinOp (Sub Int32 OverflowUndef)-                       (Futhark.Var $ paramName chunk_param)-                       (constant (1::Int32))-      -- 3. let lasteel_ids = ...-          empty_arr_bnd = mkLet [] [empty_arr] $ BasicOp $ CmpOp (CmpSlt Int32)-                          (Futhark.Var $ identName outszm1id)-                          (constant (0::Int32))-          leltmpbnds= zipWith (\ lid arrid -> mkLet [] [lid] $ BasicOp $-                                              Index (identName arrid) $-                                              fullSlice (identType arrid)-                                              [DimFix $ Futhark.Var $ identName outszm1id]-                              ) lastel_tmp_ids scan0_ids-          lelbnd = mkLet [] lastel_ids $-                   If (Futhark.Var $ identName empty_arr)-                   (mkBody mempty nes)-                   (mkBody (stmsFromList leltmpbnds) $-                    map (Futhark.Var . identName) lastel_tmp_ids) $-                   ifCommon $ map identType lastel_tmp_ids-      -- 4. let strm_resids = map (acc `+`,nes, scan0_ids)-      maplam <- mkMapPlusAccLam (map (Futhark.Var . paramName) inpacc_ids) scan_lam-      let mapbnd = mkLet [] strm_resids $ Op $-                   Futhark.Screma chvar (Futhark.mapSOAC maplam) $-                   map identName scan0_ids-      -- 5. let acc'        = acc + lasteel_ids-      addlelbdy <- mkPlusBnds scan_lam $ map Futhark.Var $-                   map paramName inpacc_ids++map identName lastel_ids-      -- Finally, construct the stream-      let (addlelbnd,addlelres) = (bodyStms addlelbdy, bodyResult addlelbdy)-          strmbdy= mkBody (stmsFromList [insbnd,outszm1bnd,empty_arr_bnd,lelbnd,mapbnd]<>addlelbnd) $-                          addlelres ++ map (Futhark.Var . identName) (strm_resids ++ map_resids)-          strmpar= chunk_param:inpacc_ids++strm_inpids-          strmlam= Lambda strmpar strmbdy (accrtps++loutps)-      return (Stream w (Sequential nes) strmlam inps,-              map paramIdent inpacc_ids)+        -- array result and input IDs of the stream's lambda+        strm_resids <- mapM (newIdent "res") scan_arr_ts+        scan0_ids <- mapM (newIdent "resarr0") scan_arr_ts+        map_resids <- mapM (newIdent "map_res") map_arr_ts +        lastel_ids <- mapM (newIdent "lstel") accrtps+        lastel_tmp_ids <- mapM (newIdent "lstel_tmp") accrtps+        empty_arr <- newIdent "empty_arr" $ Prim Bool+        inpacc_ids <- mapM (newParam "inpacc") accrtps+        outszm1id <- newIdent "szm1" $ Prim int64+        -- 1. let (scan0_ids,map_resids)  = scanomap(scan_lam,nes,map_lam,a_ch)+        let insbnd =+              mkLet [] (scan0_ids ++ map_resids) $+                Op $+                  Futhark.Screma chvar (Futhark.scanomapSOAC [Futhark.Scan scan_lam nes] lam') $+                    map paramName strm_inpids+            -- 2. let outerszm1id = chunksize - 1+            outszm1bnd =+              mkLet [] [outszm1id] $+                BasicOp $+                  BinOp+                    (Sub Int64 OverflowUndef)+                    (Futhark.Var $ paramName chunk_param)+                    (constant (1 :: Int64))+            -- 3. let lasteel_ids = ...+            empty_arr_bnd =+              mkLet [] [empty_arr] $+                BasicOp $+                  CmpOp+                    (CmpSlt Int64)+                    (Futhark.Var $ identName outszm1id)+                    (constant (0 :: Int64))+            leltmpbnds =+              zipWith+                ( \lid arrid ->+                    mkLet [] [lid] $+                      BasicOp $+                        Index (identName arrid) $+                          fullSlice+                            (identType arrid)+                            [DimFix $ Futhark.Var $ identName outszm1id]+                )+                lastel_tmp_ids+                scan0_ids+            lelbnd =+              mkLet [] lastel_ids $+                If+                  (Futhark.Var $ identName empty_arr)+                  (mkBody mempty nes)+                  ( mkBody (stmsFromList leltmpbnds) $+                      map (Futhark.Var . identName) lastel_tmp_ids+                  )+                  $ ifCommon $ map identType lastel_tmp_ids+        -- 4. let strm_resids = map (acc `+`,nes, scan0_ids)+        maplam <- mkMapPlusAccLam (map (Futhark.Var . paramName) inpacc_ids) scan_lam+        let mapbnd =+              mkLet [] strm_resids $+                Op $+                  Futhark.Screma chvar (Futhark.mapSOAC maplam) $+                    map identName scan0_ids+        -- 5. let acc'        = acc + lasteel_ids+        addlelbdy <-+          mkPlusBnds scan_lam $+            map Futhark.Var $+              map paramName inpacc_ids ++ map identName lastel_ids+        -- Finally, construct the stream+        let (addlelbnd, addlelres) = (bodyStms addlelbdy, bodyResult addlelbdy)+            strmbdy =+              mkBody (stmsFromList [insbnd, outszm1bnd, empty_arr_bnd, lelbnd, mapbnd] <> addlelbnd) $+                addlelres ++ map (Futhark.Var . identName) (strm_resids ++ map_resids)+            strmpar = chunk_param : inpacc_ids ++ strm_inpids+            strmlam = Lambda strmpar strmbdy (accrtps ++ loutps)+        return+          ( Stream w (Sequential nes) strmlam inps,+            map paramIdent inpacc_ids+          )       | Just (reds, _) <- Futhark.isRedomapSOAC form,         Futhark.Reduce comm lamin nes <- Futhark.singleReduce reds -> do-      -- Redomap(+,lam,nes,a) => is translated in strem's body to:-      -- 1. let (acc0_ids,strm_resids) = redomap(+,lam,nes,a_ch) in-      -- 2. let acc'                   = acc + acc0_ids          in-      --    {acc', strm_resids}+        -- Redomap(+,lam,nes,a) => is translated in strem's body to:+        -- 1. let (acc0_ids,strm_resids) = redomap(+,lam,nes,a_ch) in+        -- 2. let acc'                   = acc + acc0_ids          in+        --    {acc', strm_resids} -      let accrtps= take (length nes) $ lambdaReturnType lam-          -- the chunked-outersize of the array result and input types-          loutps' = drop (length nes) loutps-          -- the lambda with proper index-          foldlam = lam'-      -- array result and input IDs of the stream's lambda-      strm_resids <- mapM (newIdent "res") loutps'-      inpacc_ids <- mapM (newParam "inpacc")  accrtps-      acc0_ids   <- mapM (newIdent "acc0"  )  accrtps-      -- 1. let (acc0_ids,strm_resids) = redomap(+,lam,nes,a_ch) in-      let insoac = Futhark.Screma chvar-                   (Futhark.redomapSOAC [Futhark.Reduce comm lamin nes] foldlam) $-                   map paramName strm_inpids-          insbnd = mkLet [] (acc0_ids++strm_resids) $ Op insoac-      -- 2. let acc'     = acc + acc0_ids    in-      addaccbdy <- mkPlusBnds lamin $ map Futhark.Var $-                   map paramName inpacc_ids++map identName acc0_ids-      -- Construct the stream-      let (addaccbnd,addaccres) = (bodyStms addaccbdy, bodyResult addaccbdy)-          strmbdy= mkBody (oneStm insbnd <> addaccbnd) $-                          addaccres ++ map (Futhark.Var . identName) strm_resids-          strmpar= chunk_param:inpacc_ids++strm_inpids-          strmlam= Lambda strmpar strmbdy (accrtps++loutps')-      lam0 <- renameLambda lamin-      return (Stream w (Parallel InOrder comm lam0 nes) strmlam inps, [])+        let accrtps = take (length nes) $ lambdaReturnType lam+            -- the chunked-outersize of the array result and input types+            loutps' = drop (length nes) loutps+            -- the lambda with proper index+            foldlam = lam'+        -- array result and input IDs of the stream's lambda+        strm_resids <- mapM (newIdent "res") loutps'+        inpacc_ids <- mapM (newParam "inpacc") accrtps+        acc0_ids <- mapM (newIdent "acc0") accrtps+        -- 1. let (acc0_ids,strm_resids) = redomap(+,lam,nes,a_ch) in+        let insoac =+              Futhark.Screma+                chvar+                (Futhark.redomapSOAC [Futhark.Reduce comm lamin nes] foldlam)+                $ map paramName strm_inpids+            insbnd = mkLet [] (acc0_ids ++ strm_resids) $ Op insoac+        -- 2. let acc'     = acc + acc0_ids    in+        addaccbdy <-+          mkPlusBnds lamin $+            map Futhark.Var $+              map paramName inpacc_ids ++ map identName acc0_ids+        -- Construct the stream+        let (addaccbnd, addaccres) = (bodyStms addaccbdy, bodyResult addaccbdy)+            strmbdy =+              mkBody (oneStm insbnd <> addaccbnd) $+                addaccres ++ map (Futhark.Var . identName) strm_resids+            strmpar = chunk_param : inpacc_ids ++ strm_inpids+            strmlam = Lambda strmpar strmbdy (accrtps ++ loutps')+        lam0 <- renameLambda lamin+        return (Stream w (Parallel InOrder comm lam0 nes) strmlam inps, [])      -- Otherwise it cannot become a stream.-    _ -> return (soac,[])-    where mkMapPlusAccLam :: (MonadFreshNames m, Bindable lore)-                          => [SubExp] -> Lambda lore -> m (Lambda lore)-          mkMapPlusAccLam accs plus = do-            let (accpars, rempars) = splitAt (length accs) $ lambdaParams plus-                parbnds = zipWith (\ par se -> mkLet [] [paramIdent par]-                                                        (BasicOp $ SubExp se)-                                  ) accpars accs-                plus_bdy = lambdaBody plus-                newlambdy = Body (bodyDec plus_bdy)-                                 (stmsFromList parbnds <> bodyStms plus_bdy)-                                 (bodyResult plus_bdy)-            renameLambda $ Lambda rempars newlambdy $ lambdaReturnType plus+    _ -> return (soac, [])+  where+    mkMapPlusAccLam ::+      (MonadFreshNames m, Bindable lore) =>+      [SubExp] ->+      Lambda lore ->+      m (Lambda lore)+    mkMapPlusAccLam accs plus = do+      let (accpars, rempars) = splitAt (length accs) $ lambdaParams plus+          parbnds =+            zipWith+              ( \par se ->+                  mkLet+                    []+                    [paramIdent par]+                    (BasicOp $ SubExp se)+              )+              accpars+              accs+          plus_bdy = lambdaBody plus+          newlambdy =+            Body+              (bodyDec plus_bdy)+              (stmsFromList parbnds <> bodyStms plus_bdy)+              (bodyResult plus_bdy)+      renameLambda $ Lambda rempars newlambdy $ lambdaReturnType plus -          mkPlusBnds :: (MonadFreshNames m, Bindable lore)-                     => Lambda lore -> [SubExp] -> m (Body lore)-          mkPlusBnds plus accels = do-            plus' <- renameLambda plus-            let parbnds = zipWith (\ par se -> mkLet [] [paramIdent par]-                                                        (BasicOp $ SubExp se)-                                  ) (lambdaParams plus') accels-                body = lambdaBody plus'-            return $ body { bodyStms = stmsFromList parbnds <> bodyStms body }+    mkPlusBnds ::+      (MonadFreshNames m, Bindable lore) =>+      Lambda lore ->+      [SubExp] ->+      m (Body lore)+    mkPlusBnds plus accels = do+      plus' <- renameLambda plus+      let parbnds =+            zipWith+              ( \par se ->+                  mkLet+                    []+                    [paramIdent par]+                    (BasicOp $ SubExp se)+              )+              (lambdaParams plus')+              accels+          body = lambdaBody plus'+      return $ body {bodyStms = stmsFromList parbnds <> bodyStms body}
src/Futhark/Analysis/Metrics.hs view
@@ -1,28 +1,29 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Trustworthy #-}+ -- | Abstract Syntax Tree metrics.  This is used in the @futhark test@ -- program, for the @structure@ stanzas. module Futhark.Analysis.Metrics-       ( AstMetrics(..)-       , progMetrics+  ( AstMetrics (..),+    progMetrics, -         -- * Extensibility-       , OpMetrics(..)-       , seen-       , inside-       , MetricsM-       , stmMetrics-       , lambdaMetrics-       ) where+    -- * Extensibility+    OpMetrics (..),+    seen,+    inside,+    MetricsM,+    stmMetrics,+    lambdaMetrics,+  )+where  import Control.Monad.Writer-import Data.Text (Text)-import qualified Data.Text as T import Data.List (tails) import qualified Data.Map.Strict as M-+import Data.Text (Text)+import qualified Data.Text as T import Futhark.IR  -- | AST metrics are simply a collection from identifiable node names@@ -31,15 +32,17 @@  instance Show AstMetrics where   show (AstMetrics m) = unlines $ map metric $ M.toList m-    where metric (k, v) = pretty k ++ " " ++ pretty v+    where+      metric (k, v) = pretty k ++ " " ++ pretty v  instance Read AstMetrics where   readsPrec _ s =     maybe [] success $ mapM onLine $ lines s-    where onLine l = case words l of-                       [k, x] | [(n, "")] <- reads x -> Just (T.pack k, n)-                       _ -> Nothing-          success m = [(AstMetrics $ M.fromList m, "")]+    where+      onLine l = case words l of+        [k, x] | [(n, "")] <- reads x -> Just (T.pack k, n)+        _ -> Nothing+      success m = [(AstMetrics $ M.fromList m, "")]  -- | Compute the metrics for some operation. class OpMetrics op where@@ -59,15 +62,21 @@ actualMetrics :: CountMetrics -> AstMetrics actualMetrics (CountMetrics metrics) =   AstMetrics $ M.fromListWith (+) $ concatMap expand metrics-  where expand (ctx, k) =-          [ (T.intercalate "/" (ctx' ++ [k]), 1)-          | ctx' <- tails $ "" : ctx ]+  where+    expand (ctx, k) =+      [ (T.intercalate "/" (ctx' ++ [k]), 1)+        | ctx' <- tails $ "" : ctx+      ]  -- | This monad is used for computing metrics.  It internally keeps -- track of what we've seen so far.  Use 'seen' to add more stuff.-newtype MetricsM a = MetricsM { runMetricsM :: Writer CountMetrics a }-                   deriving (Monad, Applicative, Functor,-                             MonadWriter CountMetrics)+newtype MetricsM a = MetricsM {runMetricsM :: Writer CountMetrics a}+  deriving+    ( Monad,+      Applicative,+      Functor,+      MonadWriter CountMetrics+    )  -- | Add this node to the current tally. seen :: Text -> MetricsM ()@@ -78,16 +87,19 @@ -- enclosed operation. inside :: Text -> MetricsM () -> MetricsM () inside what m = seen what >> censor addWhat m-  where addWhat (CountMetrics metrics) =-          CountMetrics (map addWhat' metrics)-        addWhat' (ctx, k) = (what : ctx, k)+  where+    addWhat (CountMetrics metrics) =+      CountMetrics (map addWhat' metrics)+    addWhat' (ctx, k) = (what : ctx, k)  -- | Compute the metrics for a program. progMetrics :: OpMetrics (Op lore) => Prog lore -> AstMetrics progMetrics prog =-  actualMetrics $ execWriter $ runMetricsM $ do-  mapM_ funDefMetrics $ progFuns prog-  mapM_ stmMetrics $ progConsts prog+  actualMetrics $+    execWriter $+      runMetricsM $ do+        mapM_ funDefMetrics $ progFuns prog+        mapM_ stmMetrics $ progConsts prog  funDefMetrics :: OpMetrics (Op lore) => FunDef lore -> MetricsM () funDefMetrics = bodyMetrics . funDefBody@@ -102,15 +114,15 @@ expMetrics :: OpMetrics (Op lore) => Exp lore -> MetricsM () expMetrics (BasicOp op) =   seen "BasicOp" >> primOpMetrics op-expMetrics (DoLoop _ _ ForLoop{} body) =+expMetrics (DoLoop _ _ ForLoop {} body) =   inside "DoLoop" $ seen "ForLoop" >> bodyMetrics body-expMetrics (DoLoop _ _ WhileLoop{} body) =+expMetrics (DoLoop _ _ WhileLoop {} body) =   inside "DoLoop" $ seen "WhileLoop" >> bodyMetrics body expMetrics (If _ tb fb _) =   inside "If" $ do     inside "True" $ bodyMetrics tb     inside "False" $ bodyMetrics fb-expMetrics Apply{} =+expMetrics Apply {} =   seen "Apply" expMetrics (Op op) =   opMetrics op@@ -118,23 +130,23 @@ primOpMetrics :: BasicOp -> MetricsM () primOpMetrics (SubExp _) = seen "SubExp" primOpMetrics (Opaque _) = seen "Opaque"-primOpMetrics ArrayLit{} = seen "ArrayLit"-primOpMetrics BinOp{} = seen "BinOp"-primOpMetrics UnOp{} = seen "UnOp"-primOpMetrics ConvOp{} = seen "ConvOp"-primOpMetrics CmpOp{} = seen "ConvOp"-primOpMetrics Assert{} = seen "Assert"-primOpMetrics Index{} = seen "Index"-primOpMetrics Update{} = seen "Update"-primOpMetrics Concat{} = seen "Concat"-primOpMetrics Copy{} = seen "Copy"-primOpMetrics Manifest{} = seen "Manifest"-primOpMetrics Iota{} = seen "Iota"-primOpMetrics Replicate{} = seen "Replicate"-primOpMetrics Scratch{} = seen "Scratch"-primOpMetrics Reshape{} = seen "Reshape"-primOpMetrics Rearrange{} = seen "Rearrange"-primOpMetrics Rotate{} = seen "Rotate"+primOpMetrics ArrayLit {} = seen "ArrayLit"+primOpMetrics BinOp {} = seen "BinOp"+primOpMetrics UnOp {} = seen "UnOp"+primOpMetrics ConvOp {} = seen "ConvOp"+primOpMetrics CmpOp {} = seen "ConvOp"+primOpMetrics Assert {} = seen "Assert"+primOpMetrics Index {} = seen "Index"+primOpMetrics Update {} = seen "Update"+primOpMetrics Concat {} = seen "Concat"+primOpMetrics Copy {} = seen "Copy"+primOpMetrics Manifest {} = seen "Manifest"+primOpMetrics Iota {} = seen "Iota"+primOpMetrics Replicate {} = seen "Replicate"+primOpMetrics Scratch {} = seen "Scratch"+primOpMetrics Reshape {} = seen "Reshape"+primOpMetrics Rearrange {} = seen "Rearrange"+primOpMetrics Rotate {} = seen "Rotate"  -- | Compute metrics for this lambda. lambdaMetrics :: OpMetrics (Op lore) => Lambda lore -> MetricsM ()
src/Futhark/Analysis/PrimExp.hs view
@@ -1,31 +1,74 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}+ -- | A primitive expression is an expression where the non-leaves are -- primitive operators.  Our representation does not guarantee that -- the expression is type-correct. module Futhark.Analysis.PrimExp-  ( PrimExp (..)-  , evalPrimExp-  , primExpType-  , primExpSizeAtLeast-  , coerceIntPrimExp-  , leafExpTypes-  , true-  , false-  , constFoldPrimExp+  ( PrimExp (..),+    TPrimExp (..),+    isInt8,+    isInt16,+    isInt32,+    isInt64,+    isBool,+    isF32,+    isF64,+    evalPrimExp,+    primExpType,+    primExpSizeAtLeast,+    coerceIntPrimExp,+    leafExpTypes,+    true,+    false,+    constFoldPrimExp, -  , module Futhark.IR.Primitive-  , sExt, zExt-  , (.&&.), (.||.), (.<.), (.<=.), (.>.), (.>=.), (.==.), (.&.), (.|.), (.^.)-  ) where+    -- * Construction+    module Futhark.IR.Primitive,+    NumExp (..),+    IntExp,+    FloatExp (..),+    sExt,+    zExt,+    (.&&.),+    (.||.),+    (.<.),+    (.<=.),+    (.>.),+    (.>=.),+    (.==.),+    (.&.),+    (.|.),+    (.^.),+    bNot,+    sMax32,+    sMin32,+    sMax64,+    sMin64,+    sExt32,+    sExt64,+    zExt32,+    zExt64,+    fMin64,+    fMax64,+  )+where -import           Control.Monad-import           Data.Traversable+import Control.Category+import Control.Monad import qualified Data.Map as M import qualified Data.Set as S--import           Futhark.IR.Prop.Names-import           Futhark.IR.Primitive-import           Futhark.Util.IntegralExp-import           Futhark.Util.Pretty+import qualified Data.Text as T+import Data.Traversable+import Futhark.IR.Primitive+import Futhark.IR.Prop.Names+import Futhark.Util.IntegralExp+import Futhark.Util.Pretty+import GHC.Generics (Generic)+import Language.SexpGrammar as Sexp+import Language.SexpGrammar.Generic+import Prelude hiding (id, (.))  -- | A primitive expression parametrised over the representation of -- free variables.  Note that the 'Functor', 'Traversable', and 'Num'@@ -33,36 +76,28 @@ -- -- Note also that the 'Num' instance assumes 'OverflowUndef' -- semantics!-data PrimExp v = LeafExp v PrimType-               | ValueExp PrimValue-               | BinOpExp BinOp (PrimExp v) (PrimExp v)-               | CmpOpExp CmpOp (PrimExp v) (PrimExp v)-               | UnOpExp UnOp (PrimExp v)-               | ConvOpExp ConvOp (PrimExp v)-               | FunExp String [PrimExp v] PrimType-               deriving (Ord, Show)+data PrimExp v+  = LeafExp v PrimType+  | ValueExp PrimValue+  | BinOpExp BinOp (PrimExp v) (PrimExp v)+  | CmpOpExp CmpOp (PrimExp v) (PrimExp v)+  | UnOpExp UnOp (PrimExp v)+  | ConvOpExp ConvOp (PrimExp v)+  | FunExp String [PrimExp v] PrimType+  deriving (Eq, Ord, Show, Generic) --- The Eq instance upcoerces all integer constants to their largest--- type before comparing for equality.  This is technically not a good--- idea, but solves annoying problems related to the Num instance--- always producing Int64s.-instance Eq v => Eq (PrimExp v) where-  LeafExp x xt == LeafExp y yt = x == y && xt == yt-  ValueExp (IntValue x) == ValueExp (IntValue y) =-    intToInt64 x == intToInt64 y-  ValueExp x == ValueExp y =-    x == y-  BinOpExp xop x1 x2 == BinOpExp yop y1 y2 =-    xop == yop && x1 == y1 && x2 == y2-  CmpOpExp xop x1 x2 == CmpOpExp yop y1 y2 =-    xop == yop && x1 == y1 && x2 == y2-  UnOpExp xop x == UnOpExp yop y =-    xop == yop && x == y-  ConvOpExp xop x == ConvOpExp yop y =-    xop == yop && x == y-  FunExp xf xargs _ == FunExp yf yargs _ =-    xf == yf && xargs == yargs-  _ == _ = False+instance SexpIso v => SexpIso (PrimExp v) where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "leaf") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+        With (. Sexp.list (Sexp.el (Sexp.sym "value") >>> Sexp.el sexpIso)) $+          With (. Sexp.list (Sexp.el (Sexp.sym "bin-op") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+            With (. Sexp.list (Sexp.el (Sexp.sym "cmp-op") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+              With (. Sexp.list (Sexp.el (Sexp.sym "un-op") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                With (. Sexp.list (Sexp.el (Sexp.sym "conv-op") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                  With+                    (. Sexp.list (Sexp.el (Sexp.sym "fun") >>> Sexp.el (iso T.unpack T.pack . sexpIso) >>> Sexp.el sexpIso >>> Sexp.el sexpIso))+                    End  instance Functor PrimExp where   fmap = fmapDefault@@ -89,48 +124,102 @@ instance FreeIn v => FreeIn (PrimExp v) where   freeIn' = foldMap freeIn' +-- | A 'PrimExp' tagged with a phantom type used to provide type-safe+-- construction.  Does not guarantee that the underlying expression is+-- actually type correct.+newtype TPrimExp t v = TPrimExp {untyped :: PrimExp v}+  deriving (Eq, Ord, Show, Generic)++instance SexpIso v => SexpIso (TPrimExp t v) where+  sexpIso = with $ \e -> sexpIso >>> e++instance Functor (TPrimExp t) where+  fmap = fmapDefault++instance Foldable (TPrimExp t) where+  foldMap = foldMapDefault++instance Traversable (TPrimExp t) where+  traverse f (TPrimExp e) = TPrimExp <$> traverse f e++instance FreeIn v => FreeIn (TPrimExp t v) where+  freeIn' = freeIn' . untyped++-- | This expression is of type 'Int8'.+isInt8 :: PrimExp v -> TPrimExp Int8 v+isInt8 = TPrimExp++-- | This expression is of type 'Int16'.+isInt16 :: PrimExp v -> TPrimExp Int16 v+isInt16 = TPrimExp++-- | This expression is of type 'Int32'.+isInt32 :: PrimExp v -> TPrimExp Int32 v+isInt32 = TPrimExp++-- | This expression is of type 'Int64'.+isInt64 :: PrimExp v -> TPrimExp Int64 v+isInt64 = TPrimExp++-- | This is a boolean expression.+isBool :: PrimExp v -> TPrimExp Bool v+isBool = TPrimExp++-- | This expression is of type 'Float'.+isF32 :: PrimExp v -> TPrimExp Float v+isF32 = TPrimExp++-- | This expression is of type 'Double'.+isF64 :: PrimExp v -> TPrimExp Double v+isF64 = TPrimExp+ -- | True if the 'PrimExp' has at least this many nodes.  This can be -- much more efficient than comparing with 'length' for large -- 'PrimExp's, as this function is lazy. primExpSizeAtLeast :: Int -> PrimExp v -> Bool-primExpSizeAtLeast k = maybe True (>=k) . descend 0-  where descend i _-          | i >= k = Nothing-        descend i LeafExp{} = Just (i+1)-        descend i ValueExp{} = Just (i+1)-        descend i (BinOpExp _ x y) = do x' <- descend (i+1) x-                                        descend x' y-        descend i (CmpOpExp _ x y) = do x' <- descend (i+1) x-                                        descend x' y-        descend i (ConvOpExp _ x) = descend (i+1) x-        descend i (UnOpExp _ x) = descend (i+1) x-        descend i (FunExp _ args _) = foldM descend (i+1) args+primExpSizeAtLeast k = maybe True (>= k) . descend 0+  where+    descend i _+      | i >= k = Nothing+    descend i LeafExp {} = Just (i + 1)+    descend i ValueExp {} = Just (i + 1)+    descend i (BinOpExp _ x y) = do+      x' <- descend (i + 1) x+      descend x' y+    descend i (CmpOpExp _ x y) = do+      x' <- descend (i + 1) x+      descend x' y+    descend i (ConvOpExp _ x) = descend (i + 1) x+    descend i (UnOpExp _ x) = descend (i + 1) x+    descend i (FunExp _ args _) = foldM descend (i + 1) args  -- | Perform quick and dirty constant folding on the top level of a -- PrimExp.  This is necessary because we want to consider -- e.g. equality modulo constant folding. constFoldPrimExp :: PrimExp v -> PrimExp v-constFoldPrimExp (BinOpExp Add{} x y)+constFoldPrimExp (BinOpExp Add {} x y)   | zeroIshExp x = y   | zeroIshExp y = x-constFoldPrimExp (BinOpExp Sub{} x y)+constFoldPrimExp (BinOpExp Sub {} x y)   | zeroIshExp y = x-constFoldPrimExp (BinOpExp Mul{} x y)+constFoldPrimExp (BinOpExp Mul {} x y)   | oneIshExp x = y   | oneIshExp y = x-  | zeroIshExp x, IntType it <- primExpType y =-      ValueExp $ IntValue $ intValue it (0::Int)-  | zeroIshExp y, IntType it <- primExpType x =-      ValueExp $ IntValue $ intValue it (0::Int)-constFoldPrimExp (BinOpExp SDiv{} x y)+  | zeroIshExp x,+    IntType it <- primExpType y =+    ValueExp $ IntValue $ intValue it (0 :: Int)+  | zeroIshExp y,+    IntType it <- primExpType x =+    ValueExp $ IntValue $ intValue it (0 :: Int)+constFoldPrimExp (BinOpExp SDiv {} x y)   | oneIshExp y = x-constFoldPrimExp (BinOpExp SQuot{} x y)+constFoldPrimExp (BinOpExp SQuot {} x y)   | oneIshExp y = x-constFoldPrimExp (BinOpExp UDiv{} x y)+constFoldPrimExp (BinOpExp UDiv {} x y)   | oneIshExp y = x constFoldPrimExp (BinOpExp bop (ValueExp x) (ValueExp y))   | Just z <- doBinOp bop x y =-      ValueExp z+    ValueExp z constFoldPrimExp (BinOpExp LogAnd x y)   | oneIshExp x = y   | oneIshExp y = x@@ -143,124 +232,201 @@   | zeroIshExp y = x constFoldPrimExp e = e --- The Num instance performs a little bit of magic: whenever an--- expression and a constant is combined with a binary operator, the--- type of the constant may be changed to be the type of the--- expression, if they are not already the same.  This permits us to--- write e.g. @x * 4@, where @x@ is an arbitrary PrimExp, and have the--- @4@ converted to the proper primitive type.  We also support--- converting integers to floating point values, but not the other way--- around.  All numeric instances assume unsigned integers for such--- conversions.------ We also perform simple constant folding, in particular to reduce--- expressions to constants so that the above works.  However, it is--- still a bit of a hack.-instance Pretty v => Num (PrimExp v) where-  x + y | Just z <- msum [asIntOp (`Add` OverflowUndef) x y,-                          asFloatOp FAdd x y] = constFoldPrimExp z-        | otherwise = numBad "+" (x,y)+-- | The class of numeric types that can be used for constructing+-- 'TPrimExp's.+class NumExp t where+  -- | Construct a typed expression from an integer.+  fromInteger' :: Integer -> TPrimExp t v -  x - y | Just z <- msum [asIntOp (`Sub` OverflowUndef) x y,-                          asFloatOp FSub x y] = constFoldPrimExp z-        | otherwise = numBad "-" (x,y)+-- | The class of integer types that can be used for constructing+-- 'TPrimExp's.+class NumExp t => IntExp t -  x * y | Just z <- msum [asIntOp (`Mul` OverflowUndef) x y,-                          asFloatOp FMul x y] = constFoldPrimExp z-        | otherwise = numBad "*" (x,y)+instance NumExp Int8 where+  fromInteger' = isInt8 . ValueExp . IntValue . Int8Value . fromInteger -  abs x | IntType t <- primExpType x = UnOpExp (Abs t) x-        | FloatType t <- primExpType x = UnOpExp (FAbs t) x-        | otherwise = numBad "abs" x+instance IntExp Int8 -  signum x | IntType t <- primExpType x = UnOpExp (SSignum t) x-           | otherwise = numBad "signum" x+instance NumExp Int16 where+  fromInteger' = isInt16 . ValueExp . IntValue . Int16Value . fromInteger -  fromInteger = fromInt32 . fromInteger+instance IntExp Int16 -instance Pretty v => Fractional (PrimExp v) where-  x / y | Just z <- msum [asFloatOp FDiv x y] = constFoldPrimExp z-        | otherwise = numBad "/" (x,y)+instance NumExp Int32 where+  fromInteger' = isInt32 . ValueExp . IntValue . Int32Value . fromInteger -  fromRational = ValueExp . FloatValue . Float64Value . fromRational+instance IntExp Int32 -instance Pretty v => IntegralExp (PrimExp v) where-  x `div` y | Just z <- msum [asIntOp (`SDiv` Unsafe) x y,-                              asFloatOp FDiv x y] =-                constFoldPrimExp z-            | otherwise = numBad "div" (x,y)+instance NumExp Int64 where+  fromInteger' = isInt64 . ValueExp . IntValue . Int64Value . fromInteger -  x `mod` y | Just z <- msum [asIntOp (`SMod` Unsafe) x y] = z-            | otherwise = numBad "mod" (x,y)+instance IntExp Int64 -  x `quot` y | oneIshExp y = x-             | Just z <- msum [asIntOp (`SQuot` Unsafe) x y] = constFoldPrimExp z-             | otherwise = numBad "quot" (x,y)+-- | The class of floating-point types that can be used for+-- constructing 'TPrimExp's.+class NumExp t => FloatExp t where+  -- | Construct a typed expression from a rational.+  fromRational' :: Rational -> TPrimExp t v -  x `rem` y | Just z <- msum [asIntOp (`SRem` Unsafe) x y] = constFoldPrimExp z-            | otherwise = numBad "rem" (x,y)+instance NumExp Float where+  fromInteger' = TPrimExp . ValueExp . FloatValue . Float32Value . fromInteger -  x `divUp` y | Just z <- msum [asIntOp (`SDivUp` Unsafe) x y] =-                  constFoldPrimExp z-              | otherwise = numBad "divRoundingUp" (x,y)+instance NumExp Double where+  fromInteger' = TPrimExp . ValueExp . FloatValue . Float64Value . fromInteger -  sgn (ValueExp (IntValue i)) = Just $ signum $ valueIntegral i-  sgn _ = Nothing+instance FloatExp Float where+  fromRational' = TPrimExp . ValueExp . FloatValue . Float32Value . fromRational -  fromInt8  = ValueExp . IntValue . Int8Value-  fromInt16 = ValueExp . IntValue . Int16Value-  fromInt32 = ValueExp . IntValue . Int32Value-  fromInt64 = ValueExp . IntValue . Int64Value+instance FloatExp Double where+  fromRational' = TPrimExp . ValueExp . FloatValue . Float64Value . fromRational +instance (NumExp t, Pretty v) => Num (TPrimExp t v) where+  TPrimExp x + TPrimExp y+    | Just z <-+        msum+          [ asIntOp (`Add` OverflowUndef) x y,+            asFloatOp FAdd x y+          ] =+      TPrimExp $ constFoldPrimExp z+    | otherwise = numBad "+" (x, y)++  TPrimExp x - TPrimExp y+    | Just z <-+        msum+          [ asIntOp (`Sub` OverflowUndef) x y,+            asFloatOp FSub x y+          ] =+      TPrimExp $ constFoldPrimExp z+    | otherwise = numBad "-" (x, y)++  TPrimExp x * TPrimExp y+    | Just z <-+        msum+          [ asIntOp (`Mul` OverflowUndef) x y,+            asFloatOp FMul x y+          ] =+      TPrimExp $ constFoldPrimExp z+    | otherwise = numBad "*" (x, y)++  abs (TPrimExp x)+    | IntType t <- primExpType x = TPrimExp $ UnOpExp (Abs t) x+    | FloatType t <- primExpType x = TPrimExp $ UnOpExp (FAbs t) x+    | otherwise = numBad "abs" x++  signum (TPrimExp x)+    | IntType t <- primExpType x = TPrimExp $ UnOpExp (SSignum t) x+    | otherwise = numBad "signum" x++  fromInteger = fromInteger'++instance (FloatExp t, Pretty v) => Fractional (TPrimExp t v) where+  TPrimExp x / TPrimExp y+    | Just z <- msum [asFloatOp FDiv x y] = TPrimExp $ constFoldPrimExp z+    | otherwise = numBad "/" (x, y)++  fromRational = fromRational'++instance (IntExp t, Pretty v) => IntegralExp (TPrimExp t v) where+  TPrimExp x `div` TPrimExp y+    | Just z <-+        msum+          [ asIntOp (`SDiv` Unsafe) x y,+            asFloatOp FDiv x y+          ] =+      TPrimExp $ constFoldPrimExp z+    | otherwise = numBad "div" (x, y)++  TPrimExp x `mod` TPrimExp y+    | Just z <- msum [asIntOp (`SMod` Unsafe) x y] =+      TPrimExp z+    | otherwise = numBad "mod" (x, y)++  TPrimExp x `quot` TPrimExp y+    | oneIshExp y = TPrimExp x+    | Just z <- msum [asIntOp (`SQuot` Unsafe) x y] =+      TPrimExp $ constFoldPrimExp z+    | otherwise = numBad "quot" (x, y)++  TPrimExp x `rem` TPrimExp y+    | Just z <- msum [asIntOp (`SRem` Unsafe) x y] =+      TPrimExp $ constFoldPrimExp z+    | otherwise = numBad "rem" (x, y)++  TPrimExp x `divUp` TPrimExp y+    | Just z <- msum [asIntOp (`SDivUp` Unsafe) x y] =+      TPrimExp $ constFoldPrimExp z+    | otherwise = numBad "divRoundingUp" (x, y)++  sgn (TPrimExp (ValueExp (IntValue i))) = Just $ signum $ valueIntegral i+  sgn _ = Nothing+ -- | Lifted logical conjunction.-(.&&.) :: PrimExp v -> PrimExp v -> PrimExp v-x .&&. y = constFoldPrimExp $ BinOpExp LogAnd x y+(.&&.) :: TPrimExp Bool v -> TPrimExp Bool v -> TPrimExp Bool v+TPrimExp x .&&. TPrimExp y = TPrimExp $ constFoldPrimExp $ BinOpExp LogAnd x y  -- | Lifted logical conjunction.-(.||.) :: PrimExp v -> PrimExp v -> PrimExp v-x .||. y = constFoldPrimExp $ BinOpExp LogOr x y+(.||.) :: TPrimExp Bool v -> TPrimExp Bool v -> TPrimExp Bool v+TPrimExp x .||. TPrimExp y = TPrimExp $ constFoldPrimExp $ BinOpExp LogOr x y  -- | Lifted relational operators; assuming signed numbers in case of -- integers.-(.<.), (.>.), (.<=.), (.>=.), (.==.) :: PrimExp v -> PrimExp v -> PrimExp v-x .<. y = constFoldPrimExp $-          CmpOpExp cmp x y where cmp = case primExpType x of-                                         IntType t -> CmpSlt $ t `min` primExpIntType y-                                         FloatType t -> FCmpLt t-                                         _ -> CmpLlt-x .<=. y = constFoldPrimExp $-           CmpOpExp cmp x y where cmp = case primExpType x of-                                          IntType t -> CmpSle $ t `min` primExpIntType y-                                          FloatType t -> FCmpLe t-                                          _ -> CmpLle-x .==. y = constFoldPrimExp $-           CmpOpExp (CmpEq $ primExpType x `min` primExpType y) x y+(.<.), (.>.), (.<=.), (.>=.), (.==.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp Bool v+TPrimExp x .<. TPrimExp y =+  TPrimExp $+    constFoldPrimExp $+      CmpOpExp cmp x y+  where+    cmp = case primExpType x of+      IntType t -> CmpSlt t+      FloatType t -> FCmpLt t+      _ -> CmpLlt+TPrimExp x .<=. TPrimExp y =+  TPrimExp $+    constFoldPrimExp $+      CmpOpExp cmp x y+  where+    cmp = case primExpType x of+      IntType t -> CmpSle t+      FloatType t -> FCmpLe t+      _ -> CmpLle+TPrimExp x .==. TPrimExp y =+  TPrimExp $+    constFoldPrimExp $+      CmpOpExp (CmpEq $ primExpType x `min` primExpType y) x y x .>. y = y .<. x x .>=. y = y .<=. x  -- | Lifted bitwise operators.-(.&.), (.|.), (.^.) :: PrimExp v -> PrimExp v -> PrimExp v-x .&. y = constFoldPrimExp $-          BinOpExp (And $ primExpIntType x `min` primExpIntType y) x y-x .|. y = constFoldPrimExp $-          BinOpExp (Or $ primExpIntType x `min` primExpIntType y) x y-x .^. y = constFoldPrimExp $-          BinOpExp (Xor $ primExpIntType x `min` primExpIntType y) x y+(.&.), (.|.), (.^.) :: TPrimExp t v -> TPrimExp t v -> TPrimExp t v+TPrimExp x .&. TPrimExp y =+  TPrimExp $+    constFoldPrimExp $+      BinOpExp (And $ primExpIntType x) x y+TPrimExp x .|. TPrimExp y =+  TPrimExp $+    constFoldPrimExp $+      BinOpExp (Or $ primExpIntType x) x y+TPrimExp x .^. TPrimExp y =+  TPrimExp $+    constFoldPrimExp $+      BinOpExp (Xor $ primExpIntType x) x y  infix 4 .==., .<., .>., .<=., .>=.+ infixr 3 .&&.+ infixr 2 .||. --- | Smart constructor for sign extension that does a bit of constant--- folding.+-- | Untyped smart constructor for sign extension that does a bit of+-- constant folding. sExt :: IntType -> PrimExp v -> PrimExp v sExt it (ValueExp (IntValue v)) = ValueExp $ IntValue $ doSExt v it sExt it e   | primExpIntType e == it = e   | otherwise = ConvOpExp (SExt (primExpIntType e) it) e --- | Smart constructor for zero extension that does a bit of constant--- folding.+-- | Untyped smart constructor for zero extension that does a bit of+-- constant folding. zExt :: IntType -> PrimExp v -> PrimExp v zExt it (ValueExp (IntValue v)) = ValueExp $ IntValue $ doZExt v it zExt it e@@ -269,50 +435,14 @@  asIntOp :: (IntType -> BinOp) -> PrimExp v -> PrimExp v -> Maybe (PrimExp v) asIntOp f x y-  -- If either of the operands is a constant, then we prefer the type-  -- of the other operand.  This lets us use literals via fromInteger-  -- without imposing a specific type.-  | ValueExp{} <- x,-    IntType y_t <- primExpType y,-    Just x' <- asIntExp y_t x = Just $ BinOpExp (f y_t) x' y-  | ValueExp{} <- y,-    IntType x_t <- primExpType x,-    Just y' <- asIntExp x_t y = Just $ BinOpExp (f x_t) x y'--  -- Otherwise prefer the type of the leftmost operand.-  | IntType t <- primExpType x,-    Just y' <- asIntExp t y = Just $ BinOpExp (f t) x y'-  | IntType t <- primExpType y,-    Just x' <- asIntExp t x = Just $ BinOpExp (f t) x' y-+  | IntType x_t <- primExpType x = Just $ BinOpExp (f x_t) x y   | otherwise = Nothing -asIntExp :: IntType -> PrimExp v -> Maybe (PrimExp v)-asIntExp t e-  | primExpType e == IntType t = Just e-asIntExp t (ValueExp (IntValue v)) =-  Just $ ValueExp $ IntValue $ doSExt v t-asIntExp _ _ =-  Nothing- asFloatOp :: (FloatType -> BinOp) -> PrimExp v -> PrimExp v -> Maybe (PrimExp v) asFloatOp f x y-  | FloatType t <- primExpType x,-    Just y' <- asFloatExp t y = Just $ BinOpExp (f t) x y'-  | FloatType t <- primExpType y,-    Just x' <- asFloatExp t x = Just $ BinOpExp (f t) x' y+  | FloatType t <- primExpType x = Just $ BinOpExp (f t) x y   | otherwise = Nothing -asFloatExp :: FloatType -> PrimExp v -> Maybe (PrimExp v)-asFloatExp t e-  | primExpType e == FloatType t = Just e-asFloatExp t (ValueExp (FloatValue v)) =-  Just $ ValueExp $ FloatValue $ doFPConv v t-asFloatExp t (ValueExp (IntValue v)) =-  Just $ ValueExp $ FloatValue $ doSIToFP v t-asFloatExp _ _ =-  Nothing- numBad :: Pretty a => String -> a -> b numBad s x =   error $ "Invalid argument to PrimExp method " ++ s ++ ": " ++ pretty x@@ -325,11 +455,11 @@ evalPrimExp f (BinOpExp op x y) = do   x' <- evalPrimExp f x   y' <- evalPrimExp f y-  maybe (evalBad op (x,y)) return $ doBinOp op x' y'+  maybe (evalBad op (x, y)) return $ doBinOp op x' y' evalPrimExp f (CmpOpExp op x y) = do   x' <- evalPrimExp f x   y' <- evalPrimExp f y-  maybe (evalBad op (x,y)) (return . BoolValue) $ doCmpOp op x' y'+  maybe (evalBad op (x, y)) (return . BoolValue) $ doCmpOp op x' y' evalPrimExp f (UnOpExp op x) = do   x' <- evalPrimExp f x   maybe (evalBad op x) return $ doUnOp op x'@@ -338,60 +468,113 @@   maybe (evalBad op x) return $ doConvOp op x' evalPrimExp f (FunExp h args _) = do   args' <- mapM (evalPrimExp f) args-  maybe (evalBad h args) return $ do (_, _, fun) <- M.lookup h primFuns-                                     fun args'+  maybe (evalBad h args) return $ do+    (_, _, fun) <- M.lookup h primFuns+    fun args'  evalBad :: (Pretty a, Pretty b, MonadFail m) => a -> b -> m c-evalBad op arg = fail $ "evalPrimExp: Type error when applying " ++-                 pretty op ++ " to " ++ pretty arg+evalBad op arg =+  fail $+    "evalPrimExp: Type error when applying "+      ++ pretty op+      ++ " to "+      ++ pretty arg  -- | The type of values returned by a 'PrimExp'.  This function -- returning does not imply that the 'PrimExp' is type-correct. primExpType :: PrimExp v -> PrimType-primExpType (LeafExp _ t)     = t-primExpType (ValueExp v)      = primValueType v+primExpType (LeafExp _ t) = t+primExpType (ValueExp v) = primValueType v primExpType (BinOpExp op _ _) = binOpType op-primExpType CmpOpExp{}        = Bool-primExpType (UnOpExp op _)    = unOpType op-primExpType (ConvOpExp op _)  = snd $ convOpType op-primExpType (FunExp _ _ t)    = t+primExpType CmpOpExp {} = Bool+primExpType (UnOpExp op _) = unOpType op+primExpType (ConvOpExp op _) = snd $ convOpType op+primExpType (FunExp _ _ t) = t  -- | Is the expression a constant zero of some sort? zeroIshExp :: PrimExp v -> Bool zeroIshExp (ValueExp v) = zeroIsh v-zeroIshExp _            = False+zeroIshExp _ = False  -- | Is the expression a constant one of some sort? oneIshExp :: PrimExp v -> Bool oneIshExp (ValueExp v) = oneIsh v-oneIshExp _            = False+oneIshExp _ = False  -- | If the given 'PrimExp' is a constant of the wrong integer type, -- coerce it to the given integer type.  This is a workaround for an -- issue in the 'Num' instance. coerceIntPrimExp :: IntType -> PrimExp v -> PrimExp v coerceIntPrimExp t (ValueExp (IntValue v)) = ValueExp $ IntValue $ doSExt v t-coerceIntPrimExp _ e                       = e+coerceIntPrimExp _ e = e  primExpIntType :: PrimExp v -> IntType-primExpIntType e = case primExpType e of IntType t -> t-                                         _         -> Int64+primExpIntType e = case primExpType e of+  IntType t -> t+  _ -> Int64  -- | Boolean-valued PrimExps.-true, false :: PrimExp v-true = ValueExp $ BoolValue True-false = ValueExp $ BoolValue False+true, false :: TPrimExp Bool v+true = TPrimExp $ ValueExp $ BoolValue True+false = TPrimExp $ ValueExp $ BoolValue False +-- | Boolean negation smart constructor.+bNot :: TPrimExp Bool v -> TPrimExp Bool v+bNot = TPrimExp . UnOpExp Not . untyped++-- | SMax on 32-bit integers.+sMax32 :: TPrimExp Int32 v -> TPrimExp Int32 v -> TPrimExp Int32 v+sMax32 x y = TPrimExp $ BinOpExp (SMax Int32) (untyped x) (untyped y)++-- | SMin on 32-bit integers.+sMin32 :: TPrimExp Int32 v -> TPrimExp Int32 v -> TPrimExp Int32 v+sMin32 x y = TPrimExp $ BinOpExp (SMin Int32) (untyped x) (untyped y)++-- | SMax on 64-bit integers.+sMax64 :: TPrimExp Int64 v -> TPrimExp Int64 v -> TPrimExp Int64 v+sMax64 x y = TPrimExp $ BinOpExp (SMax Int64) (untyped x) (untyped y)++-- | SMin on 64-bit integers.+sMin64 :: TPrimExp Int64 v -> TPrimExp Int64 v -> TPrimExp Int64 v+sMin64 x y = TPrimExp $ BinOpExp (SMin Int64) (untyped x) (untyped y)++-- | Sign-extend to 32 bit integer.+sExt32 :: IntExp t => TPrimExp t v -> TPrimExp Int32 v+sExt32 = isInt32 . sExt Int32 . untyped++-- | Sign-extend to 64 bit integer.+sExt64 :: IntExp t => TPrimExp t v -> TPrimExp Int64 v+sExt64 = isInt64 . sExt Int64 . untyped++-- | Zero-extend to 32 bit integer.+zExt32 :: IntExp t => TPrimExp t v -> TPrimExp Int32 v+zExt32 = isInt32 . zExt Int32 . untyped++-- | Zero-extend to 64 bit integer.+zExt64 :: IntExp t => TPrimExp t v -> TPrimExp Int64 v+zExt64 = isInt64 . zExt Int64 . untyped++-- | 64-bit float minimum.+fMin64 :: TPrimExp Double v -> TPrimExp Double v -> TPrimExp Double v+fMin64 x y = TPrimExp $ BinOpExp (FMin Float64) (untyped x) (untyped y)++-- | 64-bit float maximum.+fMax64 :: TPrimExp Double v -> TPrimExp Double v -> TPrimExp Double v+fMax64 x y = TPrimExp $ BinOpExp (FMax Float64) (untyped x) (untyped y)+ -- Prettyprinting instances  instance Pretty v => Pretty (PrimExp v) where-  ppr (LeafExp v _)     = ppr v-  ppr (ValueExp v)      = ppr v+  ppr (LeafExp v _) = ppr v+  ppr (ValueExp v) = ppr v   ppr (BinOpExp op x y) = ppr op <+> parens (ppr x) <+> parens (ppr y)   ppr (CmpOpExp op x y) = ppr op <+> parens (ppr x) <+> parens (ppr y)-  ppr (ConvOpExp op x)  = ppr op <+> parens (ppr x)-  ppr (UnOpExp op x)    = ppr op <+> parens (ppr x)+  ppr (ConvOpExp op x) = ppr op <+> parens (ppr x)+  ppr (UnOpExp op x) = ppr op <+> parens (ppr x)   ppr (FunExp h args _) = text h <+> parens (commasep $ map ppr args)++instance Pretty v => Pretty (TPrimExp t v) where+  ppr = ppr . untyped  -- | Produce a mapping from the leaves of the 'PrimExp' to their -- designated types.
src/Futhark/Analysis/PrimExp/Convert.hs view
@@ -1,29 +1,33 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}+ -- | Converting back and forth between 'PrimExp's.  Use the 'ToExp' -- instance to convert to Futhark expressions. module Futhark.Analysis.PrimExp.Convert-  (-    primExpFromExp-  , primExpFromSubExp-  , primExpFromSubExpM-  , replaceInPrimExp-  , replaceInPrimExpM-  , substituteInPrimExp-  , primExpSlice-  , subExpSlice+  ( primExpFromExp,+    primExpFromSubExp,+    pe32,+    le32,+    pe64,+    le64,+    primExpFromSubExpM,+    replaceInPrimExp,+    replaceInPrimExpM,+    substituteInPrimExp,+    primExpSlice,+    subExpSlice,      -- * Module reexport-    , module Futhark.Analysis.PrimExp-  ) where+    module Futhark.Analysis.PrimExp,+  )+where  import qualified Control.Monad.Fail as Fail-import           Control.Monad.Identity+import Control.Monad.Identity import qualified Data.Map.Strict as M-import           Data.Maybe--import           Futhark.Analysis.PrimExp-import           Futhark.Construct-import           Futhark.IR+import Data.Maybe+import Futhark.Analysis.PrimExp+import Futhark.Construct+import Futhark.IR  instance ToExp v => ToExp (PrimExp v) where   toExp (BinOpExp op x y) =@@ -37,18 +41,25 @@   toExp (ValueExp v) =     return $ BasicOp $ SubExp $ Constant v   toExp (FunExp h args t) =-    Apply (nameFromString h) <$> args' <*> pure [primRetType t] <*>-    pure (Safe, mempty, [])-    where args' = zip <$> mapM (toSubExp "apply_arg") args <*> pure (repeat Observe)+    Apply (nameFromString h) <$> args' <*> pure [primRetType t]+      <*> pure (Safe, mempty, [])+    where+      args' = zip <$> mapM (toSubExp "apply_arg") args <*> pure (repeat Observe)   toExp (LeafExp v _) =     toExp v +instance ToExp v => ToExp (TPrimExp t v) where+  toExp = toExp . untyped+ -- | Convert an expression to a 'PrimExp'.  The provided function is -- used to convert expressions that are not trivially 'PrimExp's. -- This includes constants and variable names, which are passed as -- t'SubExp's.-primExpFromExp :: (Fail.MonadFail m, Decorations lore) =>-                  (VName -> m (PrimExp v)) -> Exp lore -> m (PrimExp v)+primExpFromExp ::+  (Fail.MonadFail m, Decorations lore) =>+  (VName -> m (PrimExp v)) ->+  Exp lore ->+  m (PrimExp v) primExpFromExp f (BasicOp (BinOp op x y)) =   BinOpExp op <$> primExpFromSubExpM f x <*> primExpFromSubExpM f y primExpFromExp f (BasicOp (CmpOp op x y)) =@@ -60,8 +71,9 @@ primExpFromExp f (BasicOp (SubExp se)) =   primExpFromSubExpM f se primExpFromExp f (Apply fname args ts _)-  | isBuiltInFunction fname, [Prim t] <- map declExtTypeOf ts =-      FunExp (nameToString fname) <$> mapM (primExpFromSubExpM f . fst) args <*> pure t+  | isBuiltInFunction fname,+    [Prim t] <- map declExtTypeOf ts =+    FunExp (nameToString fname) <$> mapM (primExpFromSubExpM f . fst) args <*> pure t primExpFromExp _ _ = fail "Not a PrimExp"  -- | Like 'primExpFromExp', but for a t'SubExp'.@@ -71,20 +83,38 @@  -- | Convert t'SubExp's of a given type. primExpFromSubExp :: PrimType -> SubExp -> PrimExp VName-primExpFromSubExp t (Var v)      = LeafExp v t+primExpFromSubExp t (Var v) = LeafExp v t primExpFromSubExp _ (Constant v) = ValueExp v +-- | Shorthand for constructing a 'TPrimExp' of type 'Int32'.+pe32 :: SubExp -> TPrimExp Int32 VName+pe32 = isInt32 . primExpFromSubExp int32++-- | Shorthand for constructing a 'TPrimExp' of type 'Int32', from a leaf.+le32 :: a -> TPrimExp Int32 a+le32 = isInt32 . flip LeafExp int32++-- | Shorthand for constructing a 'TPrimExp' of type 'Int64'.+pe64 :: SubExp -> TPrimExp Int64 VName+pe64 = isInt64 . primExpFromSubExp int64++-- | Shorthand for constructing a 'TPrimExp' of type 'Int64', from a leaf.+le64 :: a -> TPrimExp Int64 a+le64 = isInt64 . flip LeafExp int64+ -- | Applying a monadic transformation to the leaves in a 'PrimExp'.-replaceInPrimExpM :: Monad m =>-                     (a -> PrimType -> m (PrimExp b)) ->-                     PrimExp a -> m (PrimExp b)+replaceInPrimExpM ::+  Monad m =>+  (a -> PrimType -> m (PrimExp b)) ->+  PrimExp a ->+  m (PrimExp b) replaceInPrimExpM f (LeafExp v pt) =   f v pt replaceInPrimExpM _ (ValueExp v) =   return $ ValueExp v replaceInPrimExpM f (BinOpExp bop pe1 pe2) =-  constFoldPrimExp <$>-  (BinOpExp bop <$> replaceInPrimExpM f pe1 <*> replaceInPrimExpM f pe2)+  constFoldPrimExp+    <$> (BinOpExp bop <$> replaceInPrimExpM f pe1 <*> replaceInPrimExpM f pe2) replaceInPrimExpM f (CmpOpExp cop pe1 pe2) =   CmpOpExp cop <$> replaceInPrimExpM f pe1 <*> replaceInPrimExpM f pe2 replaceInPrimExpM f (UnOpExp uop pe) =@@ -95,21 +125,27 @@   FunExp h <$> mapM (replaceInPrimExpM f) args <*> pure t  -- | As 'replaceInPrimExpM', but in the identity monad.-replaceInPrimExp :: (a -> PrimType -> PrimExp b) ->-                    PrimExp a -> PrimExp b+replaceInPrimExp ::+  (a -> PrimType -> PrimExp b) ->+  PrimExp a ->+  PrimExp b replaceInPrimExp f e = runIdentity $ replaceInPrimExpM f' e-  where f' x y = return $ f x y+  where+    f' x y = return $ f x y  -- | Substituting names in a PrimExp with other PrimExps-substituteInPrimExp :: Ord v => M.Map v (PrimExp v)-                    -> PrimExp v -> PrimExp v+substituteInPrimExp ::+  Ord v =>+  M.Map v (PrimExp v) ->+  PrimExp v ->+  PrimExp v substituteInPrimExp tab = replaceInPrimExp $ \v t ->   fromMaybe (LeafExp v t) $ M.lookup v tab  -- | Convert a 'SubExp' slice to a 'PrimExp' slice.-primExpSlice :: Slice SubExp -> Slice (PrimExp VName)-primExpSlice = map $ fmap $ primExpFromSubExp int32+primExpSlice :: Slice SubExp -> Slice (TPrimExp Int64 VName)+primExpSlice = map $ fmap pe64  -- | Convert a 'PrimExp' slice to a 'SubExp' slice.-subExpSlice :: MonadBinder m => Slice (PrimExp VName) -> m (Slice SubExp)+subExpSlice :: MonadBinder m => Slice (TPrimExp Int64 VName) -> m (Slice SubExp) subExpSlice = mapM $ traverse $ toSubExp "slice"
src/Futhark/Analysis/PrimExp/Generalize.hs view
@@ -1,69 +1,73 @@ -- | Generalization (anti-unification) of 'PrimExp's. module Futhark.Analysis.PrimExp.Generalize-  (-    leastGeneralGeneralization-  ) where--import           Data.List (elemIndex)+  ( leastGeneralGeneralization,+  )+where -import           Futhark.Analysis.PrimExp-import           Futhark.IR.Syntax.Core (Ext(..))+import Data.List (elemIndex)+import Futhark.Analysis.PrimExp+import Futhark.IR.Syntax.Core (Ext (..))  -- | Generalize two 'PrimExp's of the the same type.-leastGeneralGeneralization :: (Eq v) => [(PrimExp v, PrimExp v)] -> PrimExp v -> PrimExp v ->-                              (PrimExp (Ext v), [(PrimExp v, PrimExp v)])+leastGeneralGeneralization ::+  (Eq v) =>+  [(PrimExp v, PrimExp v)] ->+  PrimExp v ->+  PrimExp v ->+  (PrimExp (Ext v), [(PrimExp v, PrimExp v)]) leastGeneralGeneralization m exp1@(LeafExp v1 t1) exp2@(LeafExp v2 _) =-  if v1 == v2 then-    (LeafExp (Free v1) t1, m)-  else-    generalize m exp1 exp2+  if v1 == v2+    then (LeafExp (Free v1) t1, m)+    else generalize m exp1 exp2 leastGeneralGeneralization m exp1@(ValueExp v1) exp2@(ValueExp v2) =-  if v1 == v2 then-    (ValueExp v1, m)-  else-    generalize m exp1 exp2+  if v1 == v2+    then (ValueExp v1, m)+    else generalize m exp1 exp2 leastGeneralGeneralization m exp1@(BinOpExp op1 e11 e12) exp2@(BinOpExp op2 e21 e22) =-  if op1 == op2 then-    let (e1, m1) = leastGeneralGeneralization m e11 e21-        (e2, m2) = leastGeneralGeneralization m1 e12 e22-    in (BinOpExp op1 e1 e2, m2)-  else-    generalize m exp1 exp2+  if op1 == op2+    then+      let (e1, m1) = leastGeneralGeneralization m e11 e21+          (e2, m2) = leastGeneralGeneralization m1 e12 e22+       in (BinOpExp op1 e1 e2, m2)+    else generalize m exp1 exp2 leastGeneralGeneralization m exp1@(CmpOpExp op1 e11 e12) exp2@(CmpOpExp op2 e21 e22) =-  if op1 == op2 then-    let (e1, m1) = leastGeneralGeneralization m e11 e21-        (e2, m2) = leastGeneralGeneralization m1 e12 e22-    in (CmpOpExp op1 e1 e2, m2)-  else-    generalize m exp1 exp2+  if op1 == op2+    then+      let (e1, m1) = leastGeneralGeneralization m e11 e21+          (e2, m2) = leastGeneralGeneralization m1 e12 e22+       in (CmpOpExp op1 e1 e2, m2)+    else generalize m exp1 exp2 leastGeneralGeneralization m exp1@(UnOpExp op1 e1) exp2@(UnOpExp op2 e2) =-  if op1 == op2 then-    let (e, m1) = leastGeneralGeneralization m e1 e2-    in (UnOpExp op1 e, m1)-  else-    generalize m exp1 exp2+  if op1 == op2+    then+      let (e, m1) = leastGeneralGeneralization m e1 e2+       in (UnOpExp op1 e, m1)+    else generalize m exp1 exp2 leastGeneralGeneralization m exp1@(ConvOpExp op1 e1) exp2@(ConvOpExp op2 e2) =-  if op1 == op2 then-    let (e, m1) = leastGeneralGeneralization m e1 e2-    in (ConvOpExp op1 e, m1)-  else-    generalize m exp1 exp2+  if op1 == op2+    then+      let (e, m1) = leastGeneralGeneralization m e1 e2+       in (ConvOpExp op1 e, m1)+    else generalize m exp1 exp2 leastGeneralGeneralization m exp1@(FunExp s1 args1 t1) exp2@(FunExp s2 args2 _) =-  if s1 == s2 && length args1 == length args2 then-    let (args, m') =-          foldl (\(arg_acc, m_acc) (a1, a2) ->-                    let (a, m'') = leastGeneralGeneralization m_acc a1 a2-                    in  (a : arg_acc, m'')-                ) ([], m) (zip args1 args2)-    in (FunExp s1 (reverse args) t1, m')-  else-    generalize m exp1 exp2+  if s1 == s2 && length args1 == length args2+    then+      let (args, m') =+            foldl+              ( \(arg_acc, m_acc) (a1, a2) ->+                  let (a, m'') = leastGeneralGeneralization m_acc a1 a2+                   in (a : arg_acc, m'')+              )+              ([], m)+              (zip args1 args2)+       in (FunExp s1 (reverse args) t1, m')+    else generalize m exp1 exp2 leastGeneralGeneralization m exp1 exp2 =   generalize m exp1 exp2  generalize :: Eq v => [(PrimExp v, PrimExp v)] -> PrimExp v -> PrimExp v -> (PrimExp (Ext v), [(PrimExp v, PrimExp v)]) generalize m exp1 exp2 =   let t = primExpType exp1-  in case elemIndex (exp1, exp2) m of-       Just i -> (LeafExp (Ext i) t, m)-       Nothing -> (LeafExp (Ext $ length m) t, m ++ [(exp1, exp2)])+   in case elemIndex (exp1, exp2) m of+        Just i -> (LeafExp (Ext i) t, m)+        Nothing -> (LeafExp (Ext $ length m) t, m ++ [(exp1, exp2)])
src/Futhark/Analysis/PrimExp/Simplify.hs view
@@ -1,39 +1,46 @@ {-# LANGUAGE FlexibleContexts #-}+ -- | Defines simplification functions for 'PrimExp's.-module Futhark.Analysis.PrimExp.Simplify-  (simplifyPrimExp, simplifyExtPrimExp)-where+module Futhark.Analysis.PrimExp.Simplify (simplifyPrimExp, simplifyExtPrimExp) where -import           Futhark.Analysis.PrimExp-import           Futhark.Optimise.Simplify.Engine as Engine-import           Futhark.IR+import Futhark.Analysis.PrimExp+import Futhark.IR+import Futhark.Optimise.Simplify.Engine as Engine  -- | Simplify a 'PrimExp', including copy propagation.  If a 'LeafExp' -- refers to a name that is a 'Constant', the node turns into a -- 'ValueExp'.-simplifyPrimExp :: SimplifiableLore lore =>-                   PrimExp VName -> SimpleM lore (PrimExp VName)+simplifyPrimExp ::+  SimplifiableLore lore =>+  PrimExp VName ->+  SimpleM lore (PrimExp VName) simplifyPrimExp = simplifyAnyPrimExp onLeaf-  where onLeaf v pt = do-          se <- simplify $ Var v-          case se of-            Var v' -> return $ LeafExp v' pt-            Constant pv -> return $ ValueExp pv+  where+    onLeaf v pt = do+      se <- simplify $ Var v+      case se of+        Var v' -> return $ LeafExp v' pt+        Constant pv -> return $ ValueExp pv  -- | Like 'simplifyPrimExp', but where leaves may be 'Ext's.-simplifyExtPrimExp :: SimplifiableLore lore =>-                      PrimExp (Ext VName) -> SimpleM lore (PrimExp (Ext VName))+simplifyExtPrimExp ::+  SimplifiableLore lore =>+  PrimExp (Ext VName) ->+  SimpleM lore (PrimExp (Ext VName)) simplifyExtPrimExp = simplifyAnyPrimExp onLeaf-  where onLeaf (Free v) pt = do-          se <- simplify $ Var v-          case se of-            Var v' -> return $ LeafExp (Free v') pt-            Constant pv -> return $ ValueExp pv-        onLeaf (Ext i) pt = return $ LeafExp (Ext i) pt+  where+    onLeaf (Free v) pt = do+      se <- simplify $ Var v+      case se of+        Var v' -> return $ LeafExp (Free v') pt+        Constant pv -> return $ ValueExp pv+    onLeaf (Ext i) pt = return $ LeafExp (Ext i) pt -simplifyAnyPrimExp :: SimplifiableLore lore =>-                      (a -> PrimType -> SimpleM lore (PrimExp a))-                   -> PrimExp a -> SimpleM lore (PrimExp a)+simplifyAnyPrimExp ::+  SimplifiableLore lore =>+  (a -> PrimType -> SimpleM lore (PrimExp a)) ->+  PrimExp a ->+  SimpleM lore (PrimExp a) simplifyAnyPrimExp f (LeafExp v pt) = f v pt simplifyAnyPrimExp _ (ValueExp pv) =   return $ ValueExp pv
src/Futhark/Analysis/Rephrase.hs view
@@ -1,18 +1,19 @@-{-# LANGUAGE GADTs #-} {-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE GADTs #-}+ -- | Facilities for changing the lore of some fragment, with no -- context.  We call this "rephrasing", for no deep reason. module Futhark.Analysis.Rephrase-       ( rephraseProg-       , rephraseFunDef-       , rephraseExp-       , rephraseBody-       , rephraseStm-       , rephraseLambda-       , rephrasePattern-       , rephrasePatElem-       , Rephraser (..)-       )+  ( rephraseProg,+    rephraseFunDef,+    rephraseExp,+    rephraseBody,+    rephraseStm,+    rephraseLambda,+    rephrasePattern,+    rephrasePatElem,+    Rephraser (..),+  ) where  import Futhark.IR@@ -22,23 +23,23 @@ -- monad, we can conveniently do rephrasing that might fail.  This is -- useful if you want to see if some IR in e.g. the @Kernels@ lore -- actually uses any @Kernels@-specific operations.-data Rephraser m from to-  = Rephraser { rephraseExpLore :: ExpDec from -> m (ExpDec to)-              , rephraseLetBoundLore :: LetDec from -> m (LetDec to)-              , rephraseFParamLore :: FParamInfo from -> m (FParamInfo to)-              , rephraseLParamLore :: LParamInfo from -> m (LParamInfo to)-              , rephraseBodyLore :: BodyDec from -> m (BodyDec to)-              , rephraseRetType :: RetType from -> m (RetType to)-              , rephraseBranchType :: BranchType from -> m (BranchType to)-              , rephraseOp :: Op from -> m (Op to)-              }+data Rephraser m from to = Rephraser+  { rephraseExpLore :: ExpDec from -> m (ExpDec to),+    rephraseLetBoundLore :: LetDec from -> m (LetDec to),+    rephraseFParamLore :: FParamInfo from -> m (FParamInfo to),+    rephraseLParamLore :: LParamInfo from -> m (LParamInfo to),+    rephraseBodyLore :: BodyDec from -> m (BodyDec to),+    rephraseRetType :: RetType from -> m (RetType to),+    rephraseBranchType :: BranchType from -> m (BranchType to),+    rephraseOp :: Op from -> m (Op to)+  }  -- | Rephrase an entire program. rephraseProg :: Monad m => Rephraser m from to -> Prog from -> m (Prog to) rephraseProg rephraser (Prog consts funs) =   Prog-  <$> mapM (rephraseStm rephraser) consts-  <*> mapM (rephraseFunDef rephraser) funs+    <$> mapM (rephraseStm rephraser) consts+    <*> mapM (rephraseFunDef rephraser) funs  -- | Rephrase a function definition. rephraseFunDef :: Monad m => Rephraser m from to -> FunDef from -> m (FunDef to)@@ -46,7 +47,7 @@   body' <- rephraseBody rephraser $ funDefBody fundec   params' <- mapM (rephraseParam $ rephraseFParamLore rephraser) $ funDefParams fundec   rettype' <- mapM (rephraseRetType rephraser) $ funDefRetType fundec-  return fundec { funDefBody = body', funDefParams = params', funDefRetType = rettype' }+  return fundec {funDefBody = body', funDefParams = params', funDefRetType = rettype'}  -- | Rephrase an expression. rephraseExp :: Monad m => Rephraser m from to -> Exp from -> m (Exp to)@@ -55,16 +56,17 @@ -- | Rephrase a statement. rephraseStm :: Monad m => Rephraser m from to -> Stm from -> m (Stm to) rephraseStm rephraser (Let pat (StmAux cs attrs dec) e) =-  Let <$>-  rephrasePattern (rephraseLetBoundLore rephraser) pat <*>-  (StmAux cs attrs <$> rephraseExpLore rephraser dec) <*>-  rephraseExp rephraser e+  Let+    <$> rephrasePattern (rephraseLetBoundLore rephraser) pat+    <*> (StmAux cs attrs <$> rephraseExpLore rephraser dec)+    <*> rephraseExp rephraser e  -- | Rephrase a pattern.-rephrasePattern :: Monad m =>-                   (from -> m to)-                -> PatternT from-                -> m (PatternT to)+rephrasePattern ::+  Monad m =>+  (from -> m to) ->+  PatternT from ->+  m (PatternT to) rephrasePattern = traverse  -- | Rephrase a pattern element.@@ -80,24 +82,25 @@ -- | Rephrase a body. rephraseBody :: Monad m => Rephraser m from to -> Body from -> m (Body to) rephraseBody rephraser (Body lore bnds res) =-  Body <$>-  rephraseBodyLore rephraser lore <*>-  (stmsFromList <$> mapM (rephraseStm rephraser) (stmsToList bnds)) <*>-  pure res+  Body+    <$> rephraseBodyLore rephraser lore+    <*> (stmsFromList <$> mapM (rephraseStm rephraser) (stmsToList bnds))+    <*> pure res  -- | Rephrase a lambda. rephraseLambda :: Monad m => Rephraser m from to -> Lambda from -> m (Lambda to) rephraseLambda rephraser lam = do   body' <- rephraseBody rephraser $ lambdaBody lam   params' <- mapM (rephraseParam $ rephraseLParamLore rephraser) $ lambdaParams lam-  return lam { lambdaBody = body', lambdaParams = params' }+  return lam {lambdaBody = body', lambdaParams = params'}  mapper :: Monad m => Rephraser m from to -> Mapper from to m-mapper rephraser = identityMapper {-    mapOnBody = const $ rephraseBody rephraser-  , mapOnRetType = rephraseRetType rephraser-  , mapOnBranchType = rephraseBranchType rephraser-  , mapOnFParam = rephraseParam (rephraseFParamLore rephraser)-  , mapOnLParam = rephraseParam (rephraseLParamLore rephraser)-  , mapOnOp = rephraseOp rephraser-  }+mapper rephraser =+  identityMapper+    { mapOnBody = const $ rephraseBody rephraser,+      mapOnRetType = rephraseRetType rephraser,+      mapOnBranchType = rephraseBranchType rephraser,+      mapOnFParam = rephraseParam (rephraseFParamLore rephraser),+      mapOnLParam = rephraseParam (rephraseLParamLore rephraser),+      mapOnOp = rephraseOp rephraser+    }
src/Futhark/Analysis/SymbolTable.hs view
@@ -1,85 +1,87 @@-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+ module Futhark.Analysis.SymbolTable-  ( SymbolTable (bindings, loopDepth, availableAtClosestLoop, simplifyMemory)-  , empty-  , fromScope-  , toScope+  ( SymbolTable (bindings, loopDepth, availableAtClosestLoop, simplifyMemory),+    empty,+    fromScope,+    toScope,      -- * Entries-  , Entry-  , deepen-  , entryDepth-  , entryLetBoundDec-  , entryIsSize+    Entry,+    deepen,+    entryDepth,+    entryLetBoundDec,+    entryIsSize,      -- * Lookup-  , elem-  , lookup-  , lookupStm-  , lookupExp-  , lookupBasicOp-  , lookupType-  , lookupSubExp-  , lookupAliases-  , lookupLoopVar-  , available-  , consume-  , index-  , index'-  , Indexed(..)-  , indexedAddCerts-  , IndexOp(..)+    elem,+    lookup,+    lookupStm,+    lookupExp,+    lookupBasicOp,+    lookupType,+    lookupSubExp,+    lookupAliases,+    lookupLoopVar,+    lookupLoopParam,+    available,+    consume,+    index,+    index',+    Indexed (..),+    indexedAddCerts,+    IndexOp (..),      -- * Insertion-  , insertStm-  , insertStms-  , insertFParams-  , insertLParam-  , insertLoopVar+    insertStm,+    insertStms,+    insertFParams,+    insertLParam,+    insertLoopVar,+    insertLoopMerge,      -- * Misc-  , hideCertified+    hideCertified,   )-  where+where  import Control.Arrow ((&&&)) import Control.Monad-import Data.Ord-import Data.Maybe-import Data.List (foldl', elemIndex)+import Data.List (elemIndex, foldl') import qualified Data.Map.Strict as M--import Prelude hiding (elem, lookup)-+import Data.Maybe+import Data.Ord import Futhark.Analysis.PrimExp.Convert import Futhark.IR hiding (FParam, lookupType) import qualified Futhark.IR as AST- import qualified Futhark.IR.Prop.Aliases as Aliases+import Prelude hiding (elem, lookup) -data SymbolTable lore = SymbolTable {-    loopDepth :: Int-  , bindings :: M.Map VName (Entry lore)-  , availableAtClosestLoop :: Names-    -- ^ Which names are available just before the most enclosing+data SymbolTable lore = SymbolTable+  { loopDepth :: Int,+    bindings :: M.Map VName (Entry lore),+    -- | Which names are available just before the most enclosing     -- loop?-  , simplifyMemory :: Bool-    -- ^ We are in a situation where we should+    availableAtClosestLoop :: Names,+    -- | We are in a situation where we should     -- simplify/hoist/un-existentialise memory as much as possible -     -- typically, inside a kernel.+    simplifyMemory :: Bool   }  instance Semigroup (SymbolTable lore) where   table1 <> table2 =-    SymbolTable { loopDepth = max (loopDepth table1) (loopDepth table2)-                , bindings = bindings table1 <> bindings table2-                , availableAtClosestLoop = availableAtClosestLoop table1 <>-                                           availableAtClosestLoop table2-                , simplifyMemory = simplifyMemory table1 || simplifyMemory table2-                }+    SymbolTable+      { loopDepth = max (loopDepth table1) (loopDepth table2),+        bindings = bindings table1 <> bindings table2,+        availableAtClosestLoop =+          availableAtClosestLoop table1+            <> availableAtClosestLoop table2,+        simplifyMemory = simplifyMemory table1 || simplifyMemory table2+      }  instance Monoid (SymbolTable lore) where   mempty = empty@@ -89,95 +91,103 @@  fromScope :: ASTLore lore => Scope lore -> SymbolTable lore fromScope = M.foldlWithKey' insertFreeVar' empty-  where insertFreeVar' m k dec = insertFreeVar k dec m+  where+    insertFreeVar' m k dec = insertFreeVar k dec m  toScope :: SymbolTable lore -> Scope lore toScope = M.map entryInfo . bindings  deepen :: SymbolTable lore -> SymbolTable lore-deepen vtable = vtable { loopDepth = loopDepth vtable + 1,-                         availableAtClosestLoop = namesFromList $ M.keys $ bindings vtable-                       }+deepen vtable =+  vtable+    { loopDepth = loopDepth vtable + 1,+      availableAtClosestLoop = namesFromList $ M.keys $ bindings vtable+    }  -- | The result of indexing a delayed array.-data Indexed = Indexed Certificates (PrimExp VName)-               -- ^ A PrimExp based on the indexes (that is, without-               -- accessing any actual array).-             | IndexedArray Certificates VName [PrimExp VName]-               -- ^ The indexing corresponds to another (perhaps more-               -- advantageous) array.+data Indexed+  = -- | A PrimExp based on the indexes (that is, without+    -- accessing any actual array).+    Indexed Certificates (PrimExp VName)+  | -- | The indexing corresponds to another (perhaps more+    -- advantageous) array.+    IndexedArray Certificates VName [TPrimExp Int64 VName]  indexedAddCerts :: Certificates -> Indexed -> Indexed-indexedAddCerts cs1 (Indexed cs2 v) = Indexed (cs1<>cs2) v-indexedAddCerts cs1 (IndexedArray cs2 arr v) = IndexedArray (cs1<>cs2) arr v+indexedAddCerts cs1 (Indexed cs2 v) = Indexed (cs1 <> cs2) v+indexedAddCerts cs1 (IndexedArray cs2 arr v) = IndexedArray (cs1 <> cs2) arr v  instance FreeIn Indexed where   freeIn' (Indexed cs v) = freeIn' cs <> freeIn' v   freeIn' (IndexedArray cs arr v) = freeIn' cs <> freeIn' arr <> freeIn' v  -- | Indexing a delayed array if possible.-type IndexArray = [PrimExp VName] -> Maybe Indexed+type IndexArray = [TPrimExp Int64 VName] -> Maybe Indexed -data Entry lore =-  Entry { entryConsumed :: Bool-          -- ^ True if consumed.-        , entryDepth :: Int-        , entryIsSize :: Bool-          -- ^ True if this name has been used as an array size,-          -- implying that it is non-negative.-        , entryType :: EntryType lore-        }+data Entry lore = Entry+  { -- | True if consumed.+    entryConsumed :: Bool,+    entryDepth :: Int,+    -- | True if this name has been used as an array size,+    -- implying that it is non-negative.+    entryIsSize :: Bool,+    entryType :: EntryType lore+  } -data EntryType lore = LoopVar (LoopVarEntry lore)-                    | LetBound (LetBoundEntry lore)-                    | FParam (FParamEntry lore)-                    | LParam (LParamEntry lore)-                    | FreeVar (FreeVarEntry lore)+data EntryType lore+  = LoopVar (LoopVarEntry lore)+  | LetBound (LetBoundEntry lore)+  | FParam (FParamEntry lore)+  | LParam (LParamEntry lore)+  | FreeVar (FreeVarEntry lore) -data LoopVarEntry lore =-  LoopVarEntry { loopVarType     :: IntType-               , loopVarBound    :: SubExp-               }+data LoopVarEntry lore = LoopVarEntry+  { loopVarType :: IntType,+    loopVarBound :: SubExp+  } -data LetBoundEntry lore =-  LetBoundEntry { letBoundDec      :: LetDec lore-                , letBoundAliases  :: Names-                , letBoundStm      :: Stm lore-                , letBoundIndex    :: Int -> IndexArray-                -- ^ Index a delayed array, if possible.-                }+data LetBoundEntry lore = LetBoundEntry+  { letBoundDec :: LetDec lore,+    letBoundAliases :: Names,+    letBoundStm :: Stm lore,+    -- | Index a delayed array, if possible.+    letBoundIndex :: Int -> IndexArray+  } -data FParamEntry lore =-  FParamEntry { fparamDec      :: FParamInfo lore-              , fparamAliases  :: Names-              }+data FParamEntry lore = FParamEntry+  { fparamDec :: FParamInfo lore,+    fparamAliases :: Names,+    -- | If a loop parameter, the initial value and the eventual+    -- result.  The result need not be in scope in the symbol table.+    fparamMerge :: Maybe (SubExp, SubExp)+  } -data LParamEntry lore =-  LParamEntry { lparamDec      :: LParamInfo lore-              , lparamIndex    :: IndexArray-              }+data LParamEntry lore = LParamEntry+  { lparamDec :: LParamInfo lore,+    lparamIndex :: IndexArray+  } -data FreeVarEntry lore =-  FreeVarEntry { freeVarDec      :: NameInfo lore-               , freeVarIndex    :: VName -> IndexArray-                -- ^ Index a delayed array, if possible.-               }+data FreeVarEntry lore = FreeVarEntry+  { freeVarDec :: NameInfo lore,+    -- | Index a delayed array, if possible.+    freeVarIndex :: VName -> IndexArray+  }  instance ASTLore lore => Typed (Entry lore) where   typeOf = typeOf . entryInfo  entryInfo :: Entry lore -> NameInfo lore entryInfo e = case entryType e of-                LetBound entry -> LetName $ letBoundDec entry-                LoopVar entry -> IndexName $ loopVarType entry-                FParam entry -> FParamName $ fparamDec entry-                LParam entry -> LParamName $ lparamDec entry-                FreeVar entry -> freeVarDec entry+  LetBound entry -> LetName $ letBoundDec entry+  LoopVar entry -> IndexName $ loopVarType entry+  FParam entry -> FParamName $ fparamDec entry+  LParam entry -> LParamName $ lparamDec entry+  FreeVar entry -> freeVarDec entry  isLetBound :: Entry lore -> Maybe (LetBoundEntry lore) isLetBound e = case entryType e of-                 LetBound entry -> Just entry-                 _ -> Nothing+  LetBound entry -> Just entry+  _ -> Nothing  entryStm :: Entry lore -> Maybe (Stm lore) entryStm = fmap letBoundStm . isLetBound@@ -200,7 +210,7 @@ lookupBasicOp :: VName -> SymbolTable lore -> Maybe (BasicOp, Certificates) lookupBasicOp name vtable = case lookupExp name vtable of   Just (BasicOp e, cs) -> Just (e, cs)-  _                    -> Nothing+  _ -> Nothing  lookupType :: ASTLore lore => VName -> SymbolTable lore -> Maybe Type lookupType name vtable = typeOf <$> lookup name vtable@@ -211,17 +221,17 @@  lookupSubExp :: VName -> SymbolTable lore -> Maybe (SubExp, Certificates) lookupSubExp name vtable = do-  (e,cs) <- lookupExp name vtable+  (e, cs) <- lookupExp name vtable   case e of-    BasicOp (SubExp se) -> Just (se,cs)-    _                   -> Nothing+    BasicOp (SubExp se) -> Just (se, cs)+    _ -> Nothing  lookupAliases :: VName -> SymbolTable lore -> Names lookupAliases name vtable =   case entryType <$> M.lookup name (bindings vtable) of     Just (LetBound e) -> letBoundAliases e-    Just (FParam e)   -> fparamAliases e-    _                 -> mempty+    Just (FParam e) -> fparamAliases e+    _ -> mempty  -- | If the given variable name is the name of a 'ForLoop' parameter, -- then return the bound of that loop.@@ -230,27 +240,42 @@   LoopVar e <- entryType <$> M.lookup name (bindings vtable)   return $ loopVarBound e +lookupLoopParam :: VName -> SymbolTable lore -> Maybe (SubExp, SubExp)+lookupLoopParam name vtable = do+  FParam e <- entryType <$> M.lookup name (bindings vtable)+  fparamMerge e+ -- | In symbol table and not consumed. available :: VName -> SymbolTable lore -> Bool available name = maybe False (not . entryConsumed) . M.lookup name . bindings -index :: ASTLore lore => VName -> [SubExp] -> SymbolTable lore-      -> Maybe Indexed+index ::+  ASTLore lore =>+  VName ->+  [SubExp] ->+  SymbolTable lore ->+  Maybe Indexed index name is table = do   is' <- mapM asPrimExp is   index' name is' table-  where asPrimExp i = do-          Prim t <- lookupSubExpType i table-          return $ primExpFromSubExp t i+  where+    asPrimExp i = do+      Prim t <- lookupSubExpType i table+      return $ TPrimExp $ primExpFromSubExp t i -index' :: VName -> [PrimExp VName] -> SymbolTable lore-       -> Maybe Indexed+index' ::+  VName ->+  [TPrimExp Int64 VName] ->+  SymbolTable lore ->+  Maybe Indexed index' name is vtable = do   entry <- lookup name vtable   case entryType entry of-    LetBound entry' |-      Just k <- elemIndex name $ patternValueNames $-                stmPattern $ letBoundStm entry' ->+    LetBound entry'+      | Just k <-+          elemIndex name $+            patternValueNames $+              stmPattern $ letBoundStm entry' ->         letBoundIndex entry' k is     FreeVar entry' ->       freeVarIndex entry' name is@@ -258,73 +283,83 @@     _ -> Nothing  class IndexOp op where-  indexOp :: (ASTLore lore, IndexOp (Op lore)) =>-             SymbolTable lore -> Int -> op-          -> [PrimExp VName] -> Maybe Indexed+  indexOp ::+    (ASTLore lore, IndexOp (Op lore)) =>+    SymbolTable lore ->+    Int ->+    op ->+    [TPrimExp Int64 VName] ->+    Maybe Indexed   indexOp _ _ _ _ = Nothing -instance IndexOp () where--indexExp :: (IndexOp (Op lore), ASTLore lore) =>-            SymbolTable lore -> Exp lore -> Int -> IndexArray+instance IndexOp () +indexExp ::+  (IndexOp (Op lore), ASTLore lore) =>+  SymbolTable lore ->+  Exp lore ->+  Int ->+  IndexArray indexExp vtable (Op op) k is =   indexOp vtable k op is- indexExp _ (BasicOp (Iota _ x s to_it)) _ [i] =-  Just $ Indexed mempty $-  sExt to_it i-  * primExpFromSubExp (IntType to_it) s-  + primExpFromSubExp (IntType to_it) x-+  Just $+    Indexed mempty $+      ( sExt to_it (untyped i)+          `mul` primExpFromSubExp (IntType to_it) s+      )+        `add` primExpFromSubExp (IntType to_it) x+  where+    mul = BinOpExp (Mul to_it OverflowWrap)+    add = BinOpExp (Add to_it OverflowWrap) indexExp table (BasicOp (Replicate (Shape ds) v)) _ is   | length ds == length is,     Just (Prim t) <- lookupSubExpType v table =-      Just $ Indexed mempty $ primExpFromSubExp t v--indexExp table (BasicOp (Replicate (Shape [_]) (Var v))) _ (_:is) =+    Just $ Indexed mempty $ primExpFromSubExp t v+indexExp table (BasicOp (Replicate (Shape [_]) (Var v))) _ (_ : is) =   index' v is table- indexExp table (BasicOp (Reshape newshape v)) _ is   | Just oldshape <- arrayDims <$> lookupType v table =-      let is' =-            reshapeIndex (map (primExpFromSubExp int32) oldshape)-                         (map (primExpFromSubExp int32) $ newDims newshape)-                         is-      in index' v is' table-+    let is' =+          reshapeIndex+            (map pe64 oldshape)+            (map pe64 $ newDims newshape)+            is+     in index' v is' table indexExp table (BasicOp (Index v slice)) _ is =   index' v (adjust slice is) table-  where adjust (DimFix j:js') is' =-          pe j : adjust js' is'-        adjust (DimSlice j _ s:js') (i:is') =-          let i_t_s = i * pe s-              j_p_i_t_s = pe j + i_t_s-          in j_p_i_t_s : adjust js' is'-        adjust _ _ = []--        pe = primExpFromSubExp (IntType Int32)-+  where+    adjust (DimFix j : js') is' =+      pe64 j : adjust js' is'+    adjust (DimSlice j _ s : js') (i : is') =+      let i_t_s = i * pe64 s+          j_p_i_t_s = pe64 j + i_t_s+       in j_p_i_t_s : adjust js' is'+    adjust _ _ = [] indexExp _ _ _ _ = Nothing -defBndEntry :: (ASTLore lore, IndexOp (Op lore)) =>-               SymbolTable lore-            -> PatElem lore-            -> Names-            -> Stm lore-            -> LetBoundEntry lore+defBndEntry ::+  (ASTLore lore, IndexOp (Op lore)) =>+  SymbolTable lore ->+  PatElem lore ->+  Names ->+  Stm lore ->+  LetBoundEntry lore defBndEntry vtable patElem als bnd =-  LetBoundEntry {-      letBoundDec = patElemDec patElem-    , letBoundAliases = als-    , letBoundStm = bnd-    , letBoundIndex = \k -> fmap (indexedAddCerts (stmAuxCerts $ stmAux bnd)) .-                            indexExp vtable (stmExp bnd) k+  LetBoundEntry+    { letBoundDec = patElemDec patElem,+      letBoundAliases = als,+      letBoundStm = bnd,+      letBoundIndex = \k ->+        fmap (indexedAddCerts (stmAuxCerts $ stmAux bnd))+          . indexExp vtable (stmExp bnd) k     } -bindingEntries :: (ASTLore lore, Aliases.Aliased lore, IndexOp (Op lore)) =>-                  Stm lore -> SymbolTable lore-               -> [LetBoundEntry lore]+bindingEntries ::+  (ASTLore lore, Aliases.Aliased lore, IndexOp (Op lore)) =>+  Stm lore ->+  SymbolTable lore ->+  [LetBoundEntry lore] bindingEntries bnd@(Let pat _ _) vtable = do   pat_elem <- patternElements pat   return $ defBndEntry vtable pat_elem (Aliases.aliasesOf pat_elem) bnd@@ -332,111 +367,186 @@ adjustSeveral :: Ord k => (v -> v) -> [k] -> M.Map k v -> M.Map k v adjustSeveral f = flip $ foldl' $ flip $ M.adjust f -insertEntry :: ASTLore lore =>-               VName -> EntryType lore -> SymbolTable lore-            -> SymbolTable lore+insertEntry ::+  ASTLore lore =>+  VName ->+  EntryType lore ->+  SymbolTable lore ->+  SymbolTable lore insertEntry name entry vtable =-  let entry' = Entry { entryConsumed = False-                     , entryDepth = loopDepth vtable-                     , entryIsSize = False-                     , entryType = entry-                     }+  let entry' =+        Entry+          { entryConsumed = False,+            entryDepth = loopDepth vtable,+            entryIsSize = False,+            entryType = entry+          }       dims = mapMaybe subExpVar $ arrayDims $ typeOf entry'-      isSize e = e { entryIsSize = True }-  in vtable { bindings = adjustSeveral isSize dims $-                         M.insert name entry' $ bindings vtable }+      isSize e = e {entryIsSize = True}+   in vtable+        { bindings =+            adjustSeveral isSize dims $+              M.insert name entry' $ bindings vtable+        } -insertEntries :: ASTLore lore =>-                 [(VName, EntryType lore)] -> SymbolTable lore-              -> SymbolTable lore+insertEntries ::+  ASTLore lore =>+  [(VName, EntryType lore)] ->+  SymbolTable lore ->+  SymbolTable lore insertEntries entries vtable =   foldl' add vtable entries-  where add vtable' (name, entry) = insertEntry name entry vtable'+  where+    add vtable' (name, entry) = insertEntry name entry vtable' -insertStm :: (ASTLore lore, IndexOp (Op lore), Aliases.Aliased lore) =>-             Stm lore-          -> SymbolTable lore-          -> SymbolTable lore+insertStm ::+  (ASTLore lore, IndexOp (Op lore), Aliases.Aliased lore) =>+  Stm lore ->+  SymbolTable lore ->+  SymbolTable lore insertStm stm vtable =   flip (foldl' $ flip consume) (namesToList stm_consumed) $-  flip (foldl' addRevAliases) (patternElements $ stmPattern stm) $-  insertEntries (zip names $ map LetBound $ bindingEntries stm vtable) vtable-  where names = patternNames $ stmPattern stm-        stm_consumed = expandAliases (Aliases.consumedInStm stm) vtable-        addRevAliases vtable' pe =-          vtable' { bindings = adjustSeveral update inedges $ bindings vtable' }-          where inedges = namesToList $ expandAliases (Aliases.aliasesOf pe) vtable'-                update e = e { entryType = update' $ entryType e }-                update' (LetBound entry) =-                  LetBound entry-                  { letBoundAliases = oneName (patElemName pe) <> letBoundAliases entry }-                update' (FParam entry) =-                  FParam entry-                  { fparamAliases = oneName (patElemName pe) <> fparamAliases entry }-                update' e = e+    flip (foldl' addRevAliases) (patternElements $ stmPattern stm) $+      insertEntries (zip names $ map LetBound $ bindingEntries stm vtable) vtable+  where+    names = patternNames $ stmPattern stm+    stm_consumed = expandAliases (Aliases.consumedInStm stm) vtable+    addRevAliases vtable' pe =+      vtable' {bindings = adjustSeveral update inedges $ bindings vtable'}+      where+        inedges = namesToList $ expandAliases (Aliases.aliasesOf pe) vtable'+        update e = e {entryType = update' $ entryType e}+        update' (LetBound entry) =+          LetBound+            entry+              { letBoundAliases = oneName (patElemName pe) <> letBoundAliases entry+              }+        update' (FParam entry) =+          FParam+            entry+              { fparamAliases = oneName (patElemName pe) <> fparamAliases entry+              }+        update' e = e -insertStms :: (ASTLore lore, IndexOp (Op lore), Aliases.Aliased lore) =>-              Stms lore -> SymbolTable lore -> SymbolTable lore+insertStms ::+  (ASTLore lore, IndexOp (Op lore), Aliases.Aliased lore) =>+  Stms lore ->+  SymbolTable lore ->+  SymbolTable lore insertStms stms vtable = foldl' (flip insertStm) vtable $ stmsToList stms  expandAliases :: Names -> SymbolTable lore -> Names expandAliases names vtable = names <> aliasesOfAliases-  where aliasesOfAliases =-          mconcat . map (`lookupAliases` vtable) . namesToList $ names+  where+    aliasesOfAliases =+      mconcat . map (`lookupAliases` vtable) . namesToList $ names -insertFParam :: ASTLore lore =>-                AST.FParam lore -> SymbolTable lore -> SymbolTable lore+insertFParam ::+  ASTLore lore =>+  AST.FParam lore ->+  SymbolTable lore ->+  SymbolTable lore insertFParam fparam = insertEntry name entry-  where name = AST.paramName fparam-        entry = FParam FParamEntry { fparamDec = AST.paramDec fparam-                                   , fparamAliases = mempty-                                   }+  where+    name = AST.paramName fparam+    entry =+      FParam+        FParamEntry+          { fparamDec = AST.paramDec fparam,+            fparamAliases = mempty,+            fparamMerge = Nothing+          } -insertFParams :: ASTLore lore =>-                 [AST.FParam lore] -> SymbolTable lore -> SymbolTable lore+insertFParams ::+  ASTLore lore =>+  [AST.FParam lore] ->+  SymbolTable lore ->+  SymbolTable lore insertFParams fparams symtable = foldl' (flip insertFParam) symtable fparams  insertLParam :: ASTLore lore => LParam lore -> SymbolTable lore -> SymbolTable lore insertLParam param = insertEntry name bind-  where bind = LParam LParamEntry { lparamDec = AST.paramDec param-                                  , lparamIndex = const Nothing-                                  }-        name = AST.paramName param+  where+    bind =+      LParam+        LParamEntry+          { lparamDec = AST.paramDec param,+            lparamIndex = const Nothing+          }+    name = AST.paramName param +-- | Insert entries corresponding to the parameters of a loop (not+-- distinguishing contect and value part).  Apart from the parameter+-- itself, we also insert the initial value and the subexpression+-- providing the final value.  Note that the latter is likely not in+-- scope in the symbol at this point.  This is OK, and can still be+-- used to help some loop optimisations detect invariant loop+-- parameters.+insertLoopMerge ::+  ASTLore lore =>+  [(AST.FParam lore, SubExp, SubExp)] ->+  SymbolTable lore ->+  SymbolTable lore+insertLoopMerge = flip $ foldl' $ flip bind+  where+    bind (p, initial, res) =+      insertEntry (paramName p) $+        FParam+          FParamEntry+            { fparamDec = AST.paramDec p,+              fparamAliases = mempty,+              fparamMerge = Just (initial, res)+            }+ insertLoopVar :: ASTLore lore => VName -> IntType -> SubExp -> SymbolTable lore -> SymbolTable lore insertLoopVar name it bound = insertEntry name bind-  where bind = LoopVar LoopVarEntry {-            loopVarType = it-          , loopVarBound = bound+  where+    bind =+      LoopVar+        LoopVarEntry+          { loopVarType = it,+            loopVarBound = bound           }  insertFreeVar :: ASTLore lore => VName -> NameInfo lore -> SymbolTable lore -> SymbolTable lore insertFreeVar name dec = insertEntry name entry-  where entry = FreeVar FreeVarEntry {-            freeVarDec = dec-          , freeVarIndex  = \_ _ -> Nothing+  where+    entry =+      FreeVar+        FreeVarEntry+          { freeVarDec = dec,+            freeVarIndex = \_ _ -> Nothing           }  consume :: VName -> SymbolTable lore -> SymbolTable lore-consume consumee vtable = foldl' consume' vtable $ namesToList $-                          expandAliases (oneName consumee) vtable-  where consume' vtable' v =-          vtable' { bindings = M.adjust consume'' v $ bindings vtable' }-        consume'' e = e { entryConsumed = True }+consume consumee vtable =+  foldl' consume' vtable $+    namesToList $+      expandAliases (oneName consumee) vtable+  where+    consume' vtable' v =+      vtable' {bindings = M.adjust consume'' v $ bindings vtable'}+    consume'' e = e {entryConsumed = True}  -- | Hide definitions of those entries that satisfy some predicate. hideIf :: (Entry lore -> Bool) -> SymbolTable lore -> SymbolTable lore-hideIf hide vtable = vtable { bindings = M.map maybeHide $ bindings vtable }-  where maybeHide entry-          | hide entry = entry { entryType =-                                   FreeVar FreeVarEntry { freeVarDec = entryInfo entry-                                                        , freeVarIndex = \_ _ -> Nothing-                                                        }-                               }-          | otherwise = entry+hideIf hide vtable = vtable {bindings = M.map maybeHide $ bindings vtable}+  where+    maybeHide entry+      | hide entry =+        entry+          { entryType =+              FreeVar+                FreeVarEntry+                  { freeVarDec = entryInfo entry,+                    freeVarIndex = \_ _ -> Nothing+                  }+          }+      | otherwise = entry  -- | Hide these definitions, if they are protected by certificates in -- the set of names. hideCertified :: Names -> SymbolTable lore -> SymbolTable lore hideCertified to_hide = hideIf $ maybe False hide . entryStm-  where hide = any (`nameIn` to_hide) . unCertificates . stmCerts+  where+    hide = any (`nameIn` to_hide) . unCertificates . stmCerts
src/Futhark/Analysis/UsageTable.hs view
@@ -1,40 +1,39 @@ {-# LANGUAGE Strict #-}+ -- | A usage-table is sort of a bottom-up symbol table, describing how -- (and if) a variable is used. module Futhark.Analysis.UsageTable-  ( UsageTable-  , without-  , lookup-  , used-  , expand-  , isConsumed-  , isInResult-  , isUsedDirectly-  , isSize-  , usages-  , usage-  , consumedUsage-  , inResultUsage-  , sizeUsage-  , sizeUsages-  , Usages-  , usageInStm+  ( UsageTable,+    without,+    lookup,+    used,+    expand,+    isConsumed,+    isInResult,+    isUsedDirectly,+    isSize,+    usages,+    usage,+    consumedUsage,+    inResultUsage,+    sizeUsage,+    sizeUsages,+    Usages,+    usageInStm,   )-  where+where  import Data.Bits import qualified Data.Foldable as Foldable import qualified Data.IntMap.Strict as IM import Data.List (foldl')--import Prelude hiding (lookup)- import Futhark.IR import Futhark.IR.Prop.Aliases+import Prelude hiding (lookup)  -- | A usage table. newtype UsageTable = UsageTable (IM.IntMap Usages)-                   deriving (Eq, Show)+  deriving (Eq, Show)  instance Semigroup UsageTable where   UsageTable table1 <> UsageTable table2 =@@ -62,9 +61,11 @@ -- | Expand the usage table based on aliasing information. expand :: (VName -> Names) -> UsageTable -> UsageTable expand look (UsageTable m) = UsageTable $ foldl' grow m $ IM.toList m-  where grow m' (k, v) = foldl' (grow'' $ v `withoutU` presentU) m' $-                         namesIntMap $ look $ VName (nameFromString "") k-        grow'' v m'' k = IM.insertWith (<>) (baseTag k) v m''+  where+    grow m' (k, v) =+      foldl' (grow'' $ v `withoutU` presentU) m' $+        namesIntMap $ look $ VName (nameFromString "") k+    grow'' v m'' k = IM.insertWith (<>) (baseTag k) v m''  is :: Usages -> VName -> UsageTable -> Bool is = lookupPred . matches@@ -144,30 +145,43 @@ -- a single statement. usageInStm :: (ASTLore lore, Aliased lore) => Stm lore -> UsageTable usageInStm (Let pat lore e) =-  mconcat [usageInPat,-           usageInExpLore,-           usageInExp e,-           usages (freeIn e)]-  where usageInPat =-          usages (mconcat (map freeIn $ patternElements pat)-                  `namesSubtract`-                  namesFromList (patternNames pat)) <>-          sizeUsages (foldMap (freeIn . patElemType) (patternElements pat))-        usageInExpLore =-          usages $ freeIn lore+  mconcat+    [ usageInPat,+      usageInExpLore,+      usageInExp e,+      usages (freeIn e)+    ]+  where+    usageInPat =+      usages+        ( mconcat (map freeIn $ patternElements pat)+            `namesSubtract` namesFromList (patternNames pat)+        )+        <> sizeUsages (foldMap (freeIn . patElemType) (patternElements pat))+    usageInExpLore =+      usages $ freeIn lore  usageInExp :: Aliased lore => Exp lore -> UsageTable usageInExp (Apply _ args _ _) =-  mconcat [ mconcat $ map consumedUsage $-            namesToList $ subExpAliases arg-          | (arg,d) <- args, d == Consume ]+  mconcat+    [ mconcat $+        map consumedUsage $+          namesToList $ subExpAliases arg+      | (arg, d) <- args,+        d == Consume+    ] usageInExp (DoLoop _ merge _ _) =-  mconcat [ mconcat $ map consumedUsage $-            namesToList $ subExpAliases se-          | (v,se) <- merge, unique $ paramDeclType v ]+  mconcat+    [ mconcat $+        map consumedUsage $+          namesToList $ subExpAliases se+      | (v, se) <- merge,+        unique $ paramDeclType v+    ] usageInExp (If _ tbranch fbranch _) =-  foldMap consumedUsage $ namesToList $-  consumedInBody tbranch <> consumedInBody fbranch+  foldMap consumedUsage $+    namesToList $+      consumedInBody tbranch <> consumedInBody fbranch usageInExp (BasicOp (Update src _ _)) =   consumedUsage src usageInExp (Op op) =
src/Futhark/Bench.hs view
@@ -1,61 +1,59 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}+ -- | Facilities for handling Futhark benchmark results.  A Futhark -- benchmark program is just like a Futhark test program. module Futhark.Bench-  ( RunResult (..)-  , DataResult(..)-  , BenchResult(..)-  , encodeBenchResults-  , decodeBenchResults--  , binaryName--  , benchmarkDataset-  , RunOptions(..)--  , prepareBenchmarkProgram-  , CompileOptions(..)+  ( RunResult (..),+    DataResult (..),+    BenchResult (..),+    encodeBenchResults,+    decodeBenchResults,+    binaryName,+    benchmarkDataset,+    RunOptions (..),+    prepareBenchmarkProgram,+    CompileOptions (..),   )-  where+where  import Control.Applicative import Control.Concurrent (forkIO, killThread, threadDelay) import Control.Monad.Except+import qualified Data.Aeson as JSON import qualified Data.ByteString.Char8 as SBS import qualified Data.ByteString.Lazy.Char8 as LBS import qualified Data.HashMap.Strict as HM-import qualified Data.Aeson as JSON import qualified Data.Text as T-import qualified Data.Text.IO as T import qualified Data.Text.Encoding as T-import System.FilePath+import qualified Data.Text.IO as T+import Futhark.Test import System.Exit+import System.FilePath import System.IO import System.IO.Error import System.IO.Temp (withSystemTempFile) import System.Process.ByteString (readProcessWithExitCode) import System.Timeout (timeout) -import Futhark.Test- -- | The runtime of a single succesful run.-newtype RunResult = RunResult { runMicroseconds :: Int }-                  deriving (Eq, Show)+newtype RunResult = RunResult {runMicroseconds :: Int}+  deriving (Eq, Show)  -- | The results for a single named dataset is either an error -- message, or runtime measurements along the stderr that was -- produced. data DataResult = DataResult String (Either T.Text ([RunResult], T.Text))-                deriving (Eq, Show)+  deriving (Eq, Show)  -- | The results for all datasets for some benchmark program. data BenchResult = BenchResult FilePath [DataResult]-                 deriving (Eq, Show)+  deriving (Eq, Show) -newtype DataResults = DataResults { unDataResults :: [DataResult] }-newtype BenchResults = BenchResults { unBenchResults :: [BenchResult] }+newtype DataResults = DataResults {unDataResults :: [DataResult]} +newtype BenchResults = BenchResults {unBenchResults :: [BenchResult]}+ instance JSON.ToJSON RunResult where   toJSON = JSON.toJSON . runMicroseconds @@ -71,24 +69,29 @@ instance JSON.FromJSON DataResults where   parseJSON = JSON.withObject "datasets" $ \o ->     DataResults <$> mapM datasetResult (HM.toList o)-    where datasetResult (k, v) =-            DataResult (T.unpack k) <$>-            ((Right <$> success v) <|> (Left <$> JSON.parseJSON v))-          success = JSON.withObject "result" $ \o ->-            (,) <$> o JSON..: "runtimes" <*> o JSON..: "stderr"+    where+      datasetResult (k, v) =+        DataResult (T.unpack k)+          <$> ((Right <$> success v) <|> (Left <$> JSON.parseJSON v))+      success = JSON.withObject "result" $ \o ->+        (,) <$> o JSON..: "runtimes" <*> o JSON..: "stderr"  dataResultJSON :: DataResult -> (T.Text, JSON.Value) dataResultJSON (DataResult desc (Left err)) =   (T.pack desc, JSON.toJSON err) dataResultJSON (DataResult desc (Right (runtimes, progerr))) =-  (T.pack desc, JSON.object-                [("runtimes", JSON.toJSON $ map runMicroseconds runtimes),-                 ("stderr", JSON.toJSON progerr)])+  ( T.pack desc,+    JSON.object+      [ ("runtimes", JSON.toJSON $ map runMicroseconds runtimes),+        ("stderr", JSON.toJSON progerr)+      ]+  )  benchResultJSON :: BenchResult -> (T.Text, JSON.Value) benchResultJSON (BenchResult prog r) =-  (T.pack prog,-   JSON.Object $ HM.singleton "datasets" (JSON.toJSON $ DataResults r))+  ( T.pack prog,+    JSON.Object $ HM.singleton "datasets" (JSON.toJSON $ DataResults r)+  )  instance JSON.ToJSON BenchResults where   toJSON (BenchResults rs) =@@ -97,11 +100,12 @@ instance JSON.FromJSON BenchResults where   parseJSON = JSON.withObject "benchmarks" $ \o ->     BenchResults <$> mapM onBenchmark (HM.toList o)-    where onBenchmark (k, v) =-            BenchResult (T.unpack k) <$>-            JSON.withObject "benchmark" onBenchmark' v-          onBenchmark' o =-            fmap unDataResults . JSON.parseJSON =<< o JSON..: "datasets"+    where+      onBenchmark (k, v) =+        BenchResult (T.unpack k)+          <$> JSON.withObject "benchmark" onBenchmark' v+      onBenchmark' o =+        fmap unDataResults . JSON.parseJSON =<< o JSON..: "datasets"  -- | Transform benchmark results to a JSON bytestring. encodeBenchResults :: [BenchResult] -> LBS.ByteString@@ -116,18 +120,24 @@ readRuntime :: T.Text -> Maybe Int readRuntime s = case reads $ T.unpack s of   [(runtime, _)] -> Just runtime-  _              -> Nothing+  _ -> Nothing  didNotFail :: FilePath -> ExitCode -> T.Text -> ExceptT T.Text IO () didNotFail _ ExitSuccess _ =   return () didNotFail program (ExitFailure code) stderr_s =-  throwError $ T.pack $ program ++ " failed with error code " ++ show code ++-  " and output:\n" ++ T.unpack stderr_s+  throwError $+    T.pack $+      program ++ " failed with error code " ++ show code+        ++ " and output:\n"+        ++ T.unpack stderr_s -compareResult :: (MonadError T.Text m, MonadIO m) =>-                 FilePath -> (SBS.ByteString, [Value]) -> (SBS.ByteString, [Value])-              -> m ()+compareResult ::+  (MonadError T.Text m, MonadIO m) =>+  FilePath ->+  (SBS.ByteString, [Value]) ->+  (SBS.ByteString, [Value]) ->+  m () compareResult program (expected_bs, expected_vs) (actual_bs, actual_vs) =   case compareValues1 actual_vs expected_vs of     Just mismatch -> do@@ -135,165 +145,194 @@           expectedf = program `replaceExtension` "expected"       liftIO $ SBS.writeFile actualf actual_bs       liftIO $ SBS.writeFile expectedf expected_bs-      throwError $ T.pack actualf <> " and " <> T.pack expectedf <>-        " do not match:\n" <> T.pack (show mismatch)+      throwError $+        T.pack actualf <> " and " <> T.pack expectedf+          <> " do not match:\n"+          <> T.pack (show mismatch)     Nothing ->       return () -runResult :: (MonadError T.Text m, MonadIO m) =>-             FilePath-          -> ExitCode-          -> SBS.ByteString-          -> SBS.ByteString-          -> m (SBS.ByteString, [Value])+runResult ::+  (MonadError T.Text m, MonadIO m) =>+  FilePath ->+  ExitCode ->+  SBS.ByteString ->+  SBS.ByteString ->+  m (SBS.ByteString, [Value]) runResult program ExitSuccess stdout_s _ =   case valuesFromByteString "stdout" $ LBS.fromStrict stdout_s of-    Left e   -> do+    Left e -> do       let actualf = program `replaceExtension` "actual"       liftIO $ SBS.writeFile actualf stdout_s       throwError $ T.pack $ show e <> "\n(See " <> actualf <> ")"     Right vs -> return (stdout_s, vs) runResult program (ExitFailure code) _ stderr_s =-  throwError $ T.pack $ binaryName program ++ " failed with error code " ++ show code ++-  " and output:\n" ++ T.unpack (T.decodeUtf8 stderr_s)+  throwError $+    T.pack $+      binaryName program ++ " failed with error code " ++ show code+        ++ " and output:\n"+        ++ T.unpack (T.decodeUtf8 stderr_s)  -- | How to run a benchmark.-data RunOptions =-  RunOptions-  { runRunner :: String-  , runRuns :: Int-  , runExtraOptions :: [String]-  , runTimeout :: Int-  , runVerbose :: Int-  , runResultAction :: Maybe (Int -> IO ())-    -- ^ Invoked for every runtime measured during the run.  Can be+data RunOptions = RunOptions+  { runRunner :: String,+    runRuns :: Int,+    runExtraOptions :: [String],+    runTimeout :: Int,+    runVerbose :: Int,+    -- | Invoked for every runtime measured during the run.  Can be     -- used to provide a progress bar.+    runResultAction :: Maybe (Int -> IO ())   } - -- | Like @tail -f@, but running an arbitrary IO action per line. follow :: (String -> IO ()) -> FilePath -> IO () follow f fname = go 0-  where go i = do-          i' <- withFile fname ReadMode $ \h -> do-            hSeek h AbsoluteSeek i-            goH h i-          go i'+  where+    go i = do+      i' <- withFile fname ReadMode $ \h -> do+        hSeek h AbsoluteSeek i+        goH h i+      go i' -        goH h i = do-          res <- tryIOError $ hGetLine h-          case res of-            Left e | isEOFError e -> do-                       threadDelay followDelayMicroseconds-                       pure i-                   | otherwise -> ioError e-            Right l -> do f l-                          goH h =<< hTell h+    goH h i = do+      res <- tryIOError $ hGetLine h+      case res of+        Left e+          | isEOFError e -> do+            threadDelay followDelayMicroseconds+            pure i+          | otherwise -> ioError e+        Right l -> do+          f l+          goH h =<< hTell h -        triesPerSecond = 10-        followDelayMicroseconds = 1000000 `div` triesPerSecond+    triesPerSecond = 10+    followDelayMicroseconds = 1000000 `div` triesPerSecond  -- | Run the benchmark program on the indicated dataset.-benchmarkDataset :: RunOptions -> FilePath -> T.Text-                 -> Values -> Maybe Success -> FilePath-                 -> IO (Either T.Text ([RunResult], T.Text))+benchmarkDataset ::+  RunOptions ->+  FilePath ->+  T.Text ->+  Values ->+  Maybe Success ->+  FilePath ->+  IO (Either T.Text ([RunResult], T.Text)) benchmarkDataset opts program entry input_spec expected_spec ref_out =   -- We store the runtime in a temporary file.   withSystemTempFile "futhark-bench" $ \tmpfile h -> do-  hClose h -- We will be writing and reading this ourselves.-  input <- getValuesBS dir input_spec-  let getValuesAndBS (SuccessValues vs) = do-        vs' <- getValues dir vs-        bs <- getValuesBS dir vs-        return (LBS.toStrict bs, vs')-      getValuesAndBS SuccessGenerateValues =-        getValuesAndBS $ SuccessValues $ InFile ref_out-  maybe_expected <- maybe (return Nothing) (fmap Just . getValuesAndBS) expected_spec-  let options = runExtraOptions opts ++ ["-e", T.unpack entry,-                                         "-t", tmpfile,-                                         "-r", show $ runRuns opts,-                                         "-b"]+    hClose h -- We will be writing and reading this ourselves.+    input <- getValuesBS dir input_spec+    let getValuesAndBS (SuccessValues vs) = do+          vs' <- getValues dir vs+          bs <- getValuesBS dir vs+          return (LBS.toStrict bs, vs')+        getValuesAndBS SuccessGenerateValues =+          getValuesAndBS $ SuccessValues $ InFile ref_out+    maybe_expected <- maybe (return Nothing) (fmap Just . getValuesAndBS) expected_spec+    let options =+          runExtraOptions opts+            ++ [ "-e",+                 T.unpack entry,+                 "-t",+                 tmpfile,+                 "-r",+                 show $ runRuns opts,+                 "-b"+               ] -  -- Explicitly prefixing the current directory is necessary for-  -- readProcessWithExitCode to find the binary when binOutputf has-  -- no program component.-  let (to_run, to_run_args)-        | null $ runRunner opts = ("." </> binaryName program, options)-        | otherwise = (runRunner opts, binaryName program : options)+    -- Explicitly prefixing the current directory is necessary for+    -- readProcessWithExitCode to find the binary when binOutputf has+    -- no program component.+    let (to_run, to_run_args)+          | null $ runRunner opts = ("." </> binaryName program, options)+          | otherwise = (runRunner opts, binaryName program : options) -  when (runVerbose opts > 1) $-    putStrLn $ unwords ["Running executable", show to_run,-                        "with arguments", show to_run_args]+    when (runVerbose opts > 1) $+      putStrLn $+        unwords+          [ "Running executable",+            show to_run,+            "with arguments",+            show to_run_args+          ] -  let onResult l-        | Just f <- runResultAction opts,-          [(x, "")] <- reads l =+    let onResult l+          | Just f <- runResultAction opts,+            [(x, "")] <- reads l =             f x-        | otherwise =+          | otherwise =             pure ()-  watcher <- forkIO $ follow onResult tmpfile--  run_res <--    timeout (runTimeout opts * 1000000) $-    readProcessWithExitCode to_run to_run_args $-    LBS.toStrict input+    watcher <- forkIO $ follow onResult tmpfile -  killThread watcher+    run_res <-+      timeout (runTimeout opts * 1000000) $+        readProcessWithExitCode to_run to_run_args $+          LBS.toStrict input -  runExceptT $ case run_res of-    Just (progCode, output, progerr) -> do-      case maybe_expected of-        Nothing ->-          didNotFail program progCode $ T.decodeUtf8 progerr-        Just expected ->-          compareResult program expected =<<-          runResult program progCode output progerr-      runtime_result <- liftIO $ T.readFile tmpfile-      runtimes <- case mapM readRuntime $ T.lines runtime_result of-        Just runtimes -> return $ map RunResult runtimes-        Nothing -> throwError $ "Runtime file has invalid contents:\n" <> runtime_result+    killThread watcher -      return (runtimes, T.decodeUtf8 progerr)-    Nothing ->-      throwError $ T.pack $ "Execution exceeded " ++ show (runTimeout opts) ++ " seconds."+    runExceptT $ case run_res of+      Just (progCode, output, progerr) -> do+        case maybe_expected of+          Nothing ->+            didNotFail program progCode $ T.decodeUtf8 progerr+          Just expected ->+            compareResult program expected+              =<< runResult program progCode output progerr+        runtime_result <- liftIO $ T.readFile tmpfile+        runtimes <- case mapM readRuntime $ T.lines runtime_result of+          Just runtimes -> return $ map RunResult runtimes+          Nothing -> throwError $ "Runtime file has invalid contents:\n" <> runtime_result -  where dir = takeDirectory program+        return (runtimes, T.decodeUtf8 progerr)+      Nothing ->+        throwError $ T.pack $ "Execution exceeded " ++ show (runTimeout opts) ++ " seconds."+  where+    dir = takeDirectory program  -- | How to compile a benchmark.-data CompileOptions =-  CompileOptions-  { compFuthark :: String-  , compBackend :: String-  , compOptions :: [String]+data CompileOptions = CompileOptions+  { compFuthark :: String,+    compBackend :: String,+    compOptions :: [String]   }  progNotFound :: String -> String progNotFound s = s ++ ": command not found"  -- | Compile and produce reference datasets.-prepareBenchmarkProgram :: MonadIO m =>-                           Maybe Int-                        -> CompileOptions-                        -> FilePath-                        -> [InputOutputs]-                        -> m (Either (String, Maybe SBS.ByteString) ())+prepareBenchmarkProgram ::+  MonadIO m =>+  Maybe Int ->+  CompileOptions ->+  FilePath ->+  [InputOutputs] ->+  m (Either (String, Maybe SBS.ByteString) ()) prepareBenchmarkProgram concurrency opts program cases = do   let futhark = compFuthark opts    ref_res <- runExceptT $ ensureReferenceOutput concurrency futhark "c" program cases   case ref_res of     Left err ->-      return $ Left ("Reference output generation for " ++ program ++ " failed:\n" ++-                     unlines (map T.unpack err),-                     Nothing)-+      return $+        Left+          ( "Reference output generation for " ++ program ++ " failed:\n"+              ++ unlines (map T.unpack err),+            Nothing+          )     Right () -> do-      (futcode, _, futerr) <- liftIO $ readProcessWithExitCode futhark-                              ([compBackend opts, program, "-o", binaryName program] <>-                               compOptions opts)-                              ""+      (futcode, _, futerr) <-+        liftIO $+          readProcessWithExitCode+            futhark+            ( [compBackend opts, program, "-o", binaryName program]+                <> compOptions opts+            )+            ""        case futcode of-        ExitSuccess     -> return $ Right ()+        ExitSuccess -> return $ Right ()         ExitFailure 127 -> return $ Left (progNotFound futhark, Nothing)-        ExitFailure _   -> return $ Left ("Compilation of " ++ program ++ " failed:\n", Just futerr)+        ExitFailure _ -> return $ Left ("Compilation of " ++ program ++ " failed:\n", Just futerr)
src/Futhark/Binder.hs view
@@ -1,7 +1,13 @@-{-# LANGUAGE FlexibleContexts, GeneralizedNewtypeDeriving, TypeFamilies, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+ -- | This module defines a convenience monad/typeclass for creating -- normalised programs.  The fundamental building block is 'BinderT' -- and its execution functions, but it is usually easier to use@@ -10,27 +16,28 @@ -- See "Futhark.Construct" for a high-level description. module Futhark.Binder   ( -- * A concrete @MonadBinder@ monad.-    BinderT-  , runBinderT, runBinderT_-  , runBinderT', runBinderT'_-  , BinderOps (..)-  , Binder-  , runBinder-  , runBinder_-  , runBodyBinder+    BinderT,+    runBinderT,+    runBinderT_,+    runBinderT',+    runBinderT'_,+    BinderOps (..),+    Binder,+    runBinder,+    runBinder_,+    runBodyBinder, -  -- * The 'MonadBinder' typeclass-  , module Futhark.Binder.Class+    -- * The 'MonadBinder' typeclass+    module Futhark.Binder.Class,   ) where  import Control.Arrow (second)-import Control.Monad.Writer-import Control.Monad.State.Strict-import Control.Monad.Reader import Control.Monad.Error.Class+import Control.Monad.Reader+import Control.Monad.State.Strict+import Control.Monad.Writer import qualified Data.Map.Strict as M- import Futhark.Binder.Class import Futhark.IR @@ -38,23 +45,41 @@ -- 'MonadBinder' for lores that implement this type class, which -- contains methods for constructing statements. class ASTLore lore => BinderOps lore where-  mkExpDecB :: (MonadBinder m, Lore m ~ lore) =>-                Pattern lore -> Exp lore -> m (ExpDec lore)-  mkBodyB :: (MonadBinder m, Lore m ~ lore) =>-             Stms lore -> Result -> m (Body lore)-  mkLetNamesB :: (MonadBinder m, Lore m ~ lore) =>-                 [VName] -> Exp lore -> m (Stm lore)+  mkExpDecB ::+    (MonadBinder m, Lore m ~ lore) =>+    Pattern lore ->+    Exp lore ->+    m (ExpDec lore)+  mkBodyB ::+    (MonadBinder m, Lore m ~ lore) =>+    Stms lore ->+    Result ->+    m (Body lore)+  mkLetNamesB ::+    (MonadBinder m, Lore m ~ lore) =>+    [VName] ->+    Exp lore ->+    m (Stm lore) -  default mkExpDecB :: (MonadBinder m, Bindable lore) =>-                       Pattern lore -> Exp lore -> m (ExpDec lore)+  default mkExpDecB ::+    (MonadBinder m, Bindable lore) =>+    Pattern lore ->+    Exp lore ->+    m (ExpDec lore)   mkExpDecB pat e = return $ mkExpDec pat e -  default mkBodyB :: (MonadBinder m, Bindable lore) =>-                     Stms lore -> Result -> m (Body lore)+  default mkBodyB ::+    (MonadBinder m, Bindable lore) =>+    Stms lore ->+    Result ->+    m (Body lore)   mkBodyB stms res = return $ mkBody stms res -  default mkLetNamesB :: (MonadBinder m, Lore m ~ lore, Bindable lore) =>-                         [VName] -> Exp lore -> m (Stm lore)+  default mkLetNamesB ::+    (MonadBinder m, Lore m ~ lore, Bindable lore) =>+    [VName] ->+    Exp lore ->+    m (Stm lore)   mkLetNamesB = mkLetNames  -- | A monad transformer that tracks statements and provides a@@ -76,8 +101,10 @@   getNameSource = lift getNameSource   putNameSource = lift . putNameSource -instance (ASTLore lore, Monad m) =>-         HasScope lore (BinderT lore m) where+instance+  (ASTLore lore, Monad m) =>+  HasScope lore (BinderT lore m)+  where   lookupType name = do     t <- BinderT $ gets $ M.lookup name . snd     case t of@@ -85,24 +112,29 @@       Just t' -> return $ typeOf t'   askScope = BinderT $ gets snd -instance (ASTLore lore, Monad m) =>-         LocalScope lore (BinderT lore m) where+instance+  (ASTLore lore, Monad m) =>+  LocalScope lore (BinderT lore m)+  where   localScope types (BinderT m) = BinderT $ do     modify $ second (M.union types)     x <- m     modify $ second (`M.difference` types)     return x -instance (ASTLore lore, MonadFreshNames m, BinderOps lore) =>-         MonadBinder (BinderT lore m) where+instance+  (ASTLore lore, MonadFreshNames m, BinderOps lore) =>+  MonadBinder (BinderT lore m)+  where   type Lore (BinderT lore m) = lore   mkExpDecM = mkExpDecB   mkBodyM = mkBodyB   mkLetNamesM = mkLetNamesB    addStms stms =-    BinderT $ modify $ \(cur_stms,scope) ->-    (cur_stms<>stms, scope `M.union` scopeOf stms)+    BinderT $+      modify $ \(cur_stms, scope) ->+        (cur_stms <> stms, scope `M.union` scopeOf stms)    collectStms m = do     (old_stms, old_scope) <- BinderT get@@ -114,67 +146,88 @@  -- | Run a binder action given an initial scope, returning a value and -- the statements added ('addStm') during the action.-runBinderT :: MonadFreshNames m =>-              BinderT lore m a-           -> Scope lore-           -> m (a, Stms lore)+runBinderT ::+  MonadFreshNames m =>+  BinderT lore m a ->+  Scope lore ->+  m (a, Stms lore) runBinderT (BinderT m) scope = do   (x, (stms, _)) <- runStateT m (mempty, scope)   return (x, stms)  -- | Like 'runBinderT', but return only the statements.-runBinderT_ :: MonadFreshNames m =>-               BinderT lore m () -> Scope lore -> m (Stms lore)+runBinderT_ ::+  MonadFreshNames m =>+  BinderT lore m () ->+  Scope lore ->+  m (Stms lore) runBinderT_ m = fmap snd . runBinderT m  -- | Like 'runBinderT', but get the initial scope from the current -- monad.-runBinderT' :: (MonadFreshNames m, HasScope somelore m, SameScope somelore lore) =>-               BinderT lore m a-            -> m (a, Stms lore)+runBinderT' ::+  (MonadFreshNames m, HasScope somelore m, SameScope somelore lore) =>+  BinderT lore m a ->+  m (a, Stms lore) runBinderT' m = do   scope <- askScope   runBinderT m $ castScope scope  -- | Like 'runBinderT_', but get the initial scope from the current -- monad.-runBinderT'_ :: (MonadFreshNames m, HasScope somelore m, SameScope somelore lore) =>-                BinderT lore m a -> m (Stms lore)+runBinderT'_ ::+  (MonadFreshNames m, HasScope somelore m, SameScope somelore lore) =>+  BinderT lore m a ->+  m (Stms lore) runBinderT'_ = fmap snd . runBinderT'  -- | Run a binder action, returning a value and the statements added -- ('addStm') during the action.  Assumes that the current monad -- provides initial scope and name source.-runBinder :: (MonadFreshNames m,-              HasScope somelore m, SameScope somelore lore) =>-              Binder lore a-           -> m (a, Stms lore)+runBinder ::+  ( MonadFreshNames m,+    HasScope somelore m,+    SameScope somelore lore+  ) =>+  Binder lore a ->+  m (a, Stms lore) runBinder m = do   types <- askScope   modifyNameSource $ runState $ runBinderT m $ castScope types  -- | Like 'runBinder', but throw away the result and just return the -- added statements.-runBinder_ :: (MonadFreshNames m,-               HasScope somelore m, SameScope somelore lore) =>-              Binder lore a-           -> m (Stms lore)+runBinder_ ::+  ( MonadFreshNames m,+    HasScope somelore m,+    SameScope somelore lore+  ) =>+  Binder lore a ->+  m (Stms lore) runBinder_ = fmap snd . runBinder  -- | Run a binder that produces a t'Body', and prefix that t'Body' by -- the statements produced during execution of the action.-runBodyBinder :: (Bindable lore, MonadFreshNames m,-                  HasScope somelore m, SameScope somelore lore) =>-                 Binder lore (Body lore) -> m (Body lore)+runBodyBinder ::+  ( Bindable lore,+    MonadFreshNames m,+    HasScope somelore m,+    SameScope somelore lore+  ) =>+  Binder lore (Body lore) ->+  m (Body lore) runBodyBinder = fmap (uncurry $ flip insertStms) . runBinder  -- Utility instance defintions for MTL classes.  These require -- UndecidableInstances, but save on typing elsewhere. -mapInner :: Monad m =>-            (m (a, (Stms lore, Scope lore))-             -> m (b, (Stms lore, Scope lore)))-         -> BinderT lore m a -> BinderT lore m b+mapInner ::+  Monad m =>+  ( m (a, (Stms lore, Scope lore)) ->+    m (b, (Stms lore, Scope lore))+  ) ->+  BinderT lore m a ->+  BinderT lore m b mapInner f (BinderT m) = BinderT $ do   s <- get   (x, s') <- lift $ f $ runStateT m s@@ -202,4 +255,5 @@   throwError = lift . throwError   catchError (BinderT m) f =     BinderT $ catchError m $ unBinder . f-    where unBinder (BinderT m') = m'+    where+      unBinder (BinderT m') = m'
src/Futhark/Binder/Class.hs view
@@ -1,28 +1,28 @@-{-# LANGUAGE FlexibleContexts, TypeFamilies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+ -- | This module defines a convenience typeclass for creating -- normalised programs. -- -- See "Futhark.Construct" for a high-level description. module Futhark.Binder.Class-  ( Bindable (..)-  , mkLet-  , mkLet'-  , MonadBinder (..)-  , insertStms-  , insertStm-  , letBind-  , letBindNames-  , collectStms_-  , bodyBind-  , attributing-  , auxing--  , module Futhark.MonadFreshNames+  ( Bindable (..),+    mkLet,+    mkLet',+    MonadBinder (..),+    insertStms,+    insertStm,+    letBind,+    letBindNames,+    collectStms_,+    bodyBind,+    attributing,+    auxing,+    module Futhark.MonadFreshNames,   ) where  import qualified Data.Kind- import Futhark.IR import Futhark.MonadFreshNames @@ -32,18 +32,24 @@ -- often than you think, and the results thrown away.  If used -- exclusively within a 'MonadBinder' instance, it is acceptable for -- them to create new bindings, however.-class (ASTLore lore,-       FParamInfo lore ~ DeclType,-       LParamInfo lore ~ Type,-       RetType lore ~ DeclExtType,-       BranchType lore ~ ExtType,-       SetType (LetDec lore)) =>-      Bindable lore where+class+  ( ASTLore lore,+    FParamInfo lore ~ DeclType,+    LParamInfo lore ~ Type,+    RetType lore ~ DeclExtType,+    BranchType lore ~ ExtType,+    SetType (LetDec lore)+  ) =>+  Bindable lore+  where   mkExpPat :: [Ident] -> [Ident] -> Exp lore -> Pattern lore   mkExpDec :: Pattern lore -> Exp lore -> ExpDec lore   mkBody :: Stms lore -> Result -> Body lore-  mkLetNames :: (MonadFreshNames m, HasScope lore m) =>-                [VName] -> Exp lore -> m (Stm lore)+  mkLetNames ::+    (MonadFreshNames m, HasScope lore m) =>+    [VName] ->+    Exp lore ->+    m (Stm lore)  -- | A monad that supports the creation of bindings from expressions -- and bodies from bindings, with a specific lore.  This is the main@@ -56,21 +62,26 @@ -- effects!  They may be called more often than you think, and the -- results thrown away.  It is acceptable for them to create new -- bindings, however.-class (ASTLore (Lore m),-       MonadFreshNames m, Applicative m, Monad m,-       LocalScope (Lore m) m) =>-      MonadBinder m where+class+  ( ASTLore (Lore m),+    MonadFreshNames m,+    Applicative m,+    Monad m,+    LocalScope (Lore m) m+  ) =>+  MonadBinder m+  where   type Lore m :: Data.Kind.Type   mkExpDecM :: Pattern (Lore m) -> Exp (Lore m) -> m (ExpDec (Lore m))   mkBodyM :: Stms (Lore m) -> Result -> m (Body (Lore m))   mkLetNamesM :: [VName] -> Exp (Lore m) -> m (Stm (Lore m))    -- | Add a statement to the 'Stms' under construction.-  addStm      :: Stm (Lore m) -> m ()-  addStm      = addStms . oneStm+  addStm :: Stm (Lore m) -> m ()+  addStm = addStms . oneStm    -- | Add multiple statements to the 'Stms' under construction.-  addStms     :: Stms (Lore m) -> m ()+  addStms :: Stms (Lore m) -> m ()    -- | Obtain the statements constructed during a monadic action,   -- instead of adding them to the state.@@ -82,9 +93,11 @@   certifying = censorStms . fmap . certify  -- | Apply a function to the statements added by this action.-censorStms :: MonadBinder m =>-              (Stms (Lore m) -> Stms (Lore m))-           -> m a -> m a+censorStms ::+  MonadBinder m =>+  (Stms (Lore m) -> Stms (Lore m)) ->+  m a ->+  m a censorStms f m = do   (x, stms) <- collectStms m   addStms $ f stms@@ -93,22 +106,30 @@ -- | Add the given attributes to any statements added by this action. attributing :: MonadBinder m => Attrs -> m a -> m a attributing attrs = censorStms $ fmap onStm-  where onStm (Let pat aux e) =-          Let pat aux { stmAuxAttrs = attrs <> stmAuxAttrs aux } e+  where+    onStm (Let pat aux e) =+      Let pat aux {stmAuxAttrs = attrs <> stmAuxAttrs aux} e  -- | Add the certificates and attributes to any statements added by -- this action. auxing :: MonadBinder m => StmAux anylore -> m a -> m a auxing (StmAux cs attrs _) = censorStms $ fmap onStm-  where onStm (Let pat aux e) =-          Let pat aux' e-          where aux' = aux { stmAuxAttrs = attrs <> stmAuxAttrs aux-                           , stmAuxCerts = cs <> stmAuxCerts aux-                           }+  where+    onStm (Let pat aux e) =+      Let pat aux' e+      where+        aux' =+          aux+            { stmAuxAttrs = attrs <> stmAuxAttrs aux,+              stmAuxCerts = cs <> stmAuxCerts aux+            }  -- | Add a statement with the given pattern and expression.-letBind :: MonadBinder m =>-            Pattern (Lore m) -> Exp (Lore m) -> m ()+letBind ::+  MonadBinder m =>+  Pattern (Lore m) ->+  Exp (Lore m) ->+  m () letBind pat e =   addStm =<< Let pat <$> (defAux <$> mkExpDecM pat e) <*> pure e @@ -118,7 +139,7 @@ mkLet ctx val e =   let pat = mkExpPat ctx val e       dec = mkExpDec pat e-  in Let pat (defAux dec) e+   in Let pat (defAux dec) e  -- | Like mkLet, but also take attributes and certificates from the -- given 'StmAux'.@@ -126,7 +147,7 @@ mkLet' ctx val (StmAux cs attrs _) e =   let pat = mkExpPat ctx val e       dec = mkExpDec pat e-  in Let pat (StmAux cs attrs dec) e+   in Let pat (StmAux cs attrs dec) e  -- | Add a statement with the given pattern element names and -- expression.@@ -145,7 +166,7 @@  -- | Add several bindings at the outermost level of a t'Body'. insertStms :: Bindable lore => Stms lore -> Body lore -> Body lore-insertStms stms1 (Body _ stms2 res) = mkBody (stms1<>stms2) res+insertStms stms1 (Body _ stms2 res) = mkBody (stms1 <> stms2) res  -- | Add a single binding at the outermost level of a t'Body'. insertStm :: Bindable lore => Stm lore -> Body lore -> Body lore
src/Futhark/CLI/Autotune.hs view
@@ -1,40 +1,39 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+ -- | @futhark autotune@ module Futhark.CLI.Autotune (main) where  import Control.Monad import qualified Data.ByteString.Char8 as SBS import Data.Function (on)-import Data.Tree-import Data.List (intersect, isPrefixOf, sort, sortOn, elemIndex, minimumBy)+import Data.List (elemIndex, intersect, isPrefixOf, minimumBy, sort, sortOn) import Data.Maybe-import Text.Read (readMaybe)-import Text.Regex.TDFA-import qualified Data.Text as T import qualified Data.Set as S--import System.Environment (getExecutablePath)-import System.Exit-import System.Process-import System.FilePath-import System.Console.GetOpt-+import qualified Data.Text as T+import Data.Tree import Futhark.Bench import Futhark.Test import Futhark.Util (maxinum) import Futhark.Util.Options+import System.Console.GetOpt+import System.Environment (getExecutablePath)+import System.Exit+import System.FilePath+import System.Process+import Text.Read (readMaybe)+import Text.Regex.TDFA  data AutotuneOptions = AutotuneOptions-                    { optBackend :: String-                    , optFuthark :: Maybe String-                    , optRuns :: Int-                    , optTuning :: Maybe String-                    , optExtraOptions :: [String]-                    , optVerbose :: Int-                    , optTimeout :: Int-                    , optDefaultThreshold :: Int-                    }+  { optBackend :: String,+    optFuthark :: Maybe String,+    optRuns :: Int,+    optTuning :: Maybe String,+    optExtraOptions :: [String],+    optVerbose :: Int,+    optTimeout :: Int,+    optDefaultThreshold :: Int+  }  initialAutotuneOptions :: AutotuneOptions initialAutotuneOptions =@@ -43,25 +42,30 @@ compileOptions :: AutotuneOptions -> IO CompileOptions compileOptions opts = do   futhark <- maybe getExecutablePath return $ optFuthark opts-  return $ CompileOptions { compFuthark = futhark-                          , compBackend = optBackend opts-                          , compOptions = mempty-                          }+  return $+    CompileOptions+      { compFuthark = futhark,+        compBackend = optBackend opts,+        compOptions = mempty+      }  runOptions :: Path -> Int -> AutotuneOptions -> RunOptions runOptions path timeout_s opts =-  RunOptions { runRunner = ""-             , runRuns = optRuns opts-             , runExtraOptions = "--default-threshold" :-                                 show (optDefaultThreshold opts) :-                                 "-L" :-                                 map opt path ++-                                 optExtraOptions opts-             , runTimeout = timeout_s-             , runVerbose = optVerbose opts-             , runResultAction = Nothing-             }-  where opt (name, val) = "--size=" ++ name ++ "=" ++ show val+  RunOptions+    { runRunner = "",+      runRuns = optRuns opts,+      runExtraOptions =+        "--default-threshold" :+        show (optDefaultThreshold opts) :+        "-L" :+        map opt path+          ++ optExtraOptions opts,+      runTimeout = timeout_s,+      runVerbose = optVerbose opts,+      runResultAction = Nothing+    }+  where+    opt (name, val) = "--size=" ++ name ++ "=" ++ show val  type Path = [(String, Int)] @@ -71,13 +75,14 @@     matchM regex s :: Maybe (String, String, String, [String])   Just groups -comparisons :: String -> [(String,Int)]+comparisons :: String -> [(String, Int)] comparisons = mapMaybe isComparison . lines-  where regex = makeRegex ("Compared ([^ ]+) <= (-?[0-9]+)" :: String)-        isComparison l = do [thresh, val] <- regexGroups regex l-                            val' <- readMaybe val-                            return (thresh, val')-+  where+    regex = makeRegex ("Compared ([^ ]+) <= (-?[0-9]+)" :: String)+    isComparison l = do+      [thresh, val] <- regexGroups regex l+      val' <- readMaybe val+      return (thresh, val')  type RunDataset = Int -> Path -> IO (Either String ([(String, Int)], Int)) @@ -91,18 +96,18 @@   truns <-     case testAction spec of       RunCases ios _ _ | not $ null ios -> do-            when (optVerbose opts > 1) $-               putStrLn $-                 unwords ("Entry points:" : map (T.unpack . iosEntryPoint) ios)+        when (optVerbose opts > 1) $+          putStrLn $+            unwords ("Entry points:" : map (T.unpack . iosEntryPoint) ios) -            res <- prepareBenchmarkProgram Nothing copts prog ios-            case res of-              Left (err, errstr) -> do-                putStrLn err-                maybe (return ()) SBS.putStrLn errstr-                exitFailure-              Right () ->-                return ios+        res <- prepareBenchmarkProgram Nothing copts prog ios+        case res of+          Left (err, errstr) -> do+            putStrLn err+            maybe (return ()) SBS.putStrLn errstr+            exitFailure+          Right () ->+            return ios       _ ->         fail "Unsupported test spec." @@ -110,36 +115,44 @@         case runExpectedResult trun of           Succeeds expected             | null (runTags trun `intersect` ["notune", "disable"]) ->-                Just (runDescription trun, run entry_point trun expected)-+              Just (runDescription trun, run entry_point trun expected)           _ -> Nothing -  fmap concat $ forM truns $ \ios ->-    forM (mapMaybe (runnableDataset $ iosEntryPoint ios)-                   (iosTestRuns ios)) $-      \(dataset, do_run) ->-        return (dataset, do_run, iosEntryPoint ios)--  where run entry_point trun expected timeout path = do-          let bestRuntime :: ([RunResult], T.Text) -> ([(String, Int)], Int)-              bestRuntime (runres, errout) =-                (comparisons (T.unpack errout),-                 minimum $ map runMicroseconds runres)+  fmap concat $+    forM truns $ \ios ->+      forM+        ( mapMaybe+            (runnableDataset $ iosEntryPoint ios)+            (iosTestRuns ios)+        )+        $ \(dataset, do_run) ->+          return (dataset, do_run, iosEntryPoint ios)+  where+    run entry_point trun expected timeout path = do+      let bestRuntime :: ([RunResult], T.Text) -> ([(String, Int)], Int)+          bestRuntime (runres, errout) =+            ( comparisons (T.unpack errout),+              minimum $ map runMicroseconds runres+            ) -              ropts = runOptions path timeout opts+          ropts = runOptions path timeout opts -          when (optVerbose opts > 1) $-            putStrLn $ "Running with options: " ++ unwords (runExtraOptions ropts)+      when (optVerbose opts > 1) $+        putStrLn $ "Running with options: " ++ unwords (runExtraOptions ropts) -          either (Left . T.unpack) (Right . bestRuntime) <$>-            benchmarkDataset ropts prog entry_point-            (runInput trun) expected-            (testRunReferenceOutput prog entry_point trun)+      either (Left . T.unpack) (Right . bestRuntime)+        <$> benchmarkDataset+          ropts+          prog+          entry_point+          (runInput trun)+          expected+          (testRunReferenceOutput prog entry_point trun)  --- Benchmarking a program  data DatasetResult = DatasetResult [(String, Int)] Double-             deriving Show+  deriving (Show)  --- Finding initial comparisons. @@ -156,101 +169,120 @@ -- are used. tuningPaths :: ThresholdForest -> [(String, Path)] tuningPaths = concatMap (treePaths [])-  where treePaths ancestors (Node (v, _) children) =-          concatMap (onChild ancestors v) children ++ [(v, ancestors)]+  where+    treePaths ancestors (Node (v, _) children) =+      concatMap (onChild ancestors v) children ++ [(v, ancestors)] -        onChild ancestors v child@(Node (_, cmp) _) =-          treePaths (ancestors++[(v, t cmp)]) child+    onChild ancestors v child@(Node (_, cmp) _) =+      treePaths (ancestors ++ [(v, t cmp)]) child -        t False = thresholdMax-        t True = thresholdMin+    t False = thresholdMax+    t True = thresholdMin  thresholdForest :: FilePath -> IO ThresholdForest thresholdForest prog = do-  thresholds <- getThresholds <$>-    readProcess ("." </> dropExtension prog) ["--print-sizes"] ""+  thresholds <-+    getThresholds+      <$> readProcess ("." </> dropExtension prog) ["--print-sizes"] ""   let root (v, _) = ((v, False), [])-  return $ unfoldForest (unfold thresholds) $-    map root $ filter (null . snd) thresholds-  where getThresholds = mapMaybe findThreshold . lines-        regex = makeRegex ("(.*)\\ \\(threshold\\ \\((.*)\\)\\)" :: String)--        findThreshold :: String -> Maybe (String, [(String, Bool)])-        findThreshold l = do [grp1, grp2] <- regexGroups regex l-                             return (grp1,-                                     filter (not . null . fst) $-                                     map (\x -> if "!" `isPrefixOf` x-                                                then (drop 1 x, False)-                                                else (x, True)) $-                                     words grp2)+  return $+    unfoldForest (unfold thresholds) $+      map root $ filter (null . snd) thresholds+  where+    getThresholds = mapMaybe findThreshold . lines+    regex = makeRegex ("(.*)\\ \\(threshold\\ \\((.*)\\)\\)" :: String) -        unfold thresholds ((parent, parent_cmp), ancestors) =-          let ancestors' = parent : ancestors+    findThreshold :: String -> Maybe (String, [(String, Bool)])+    findThreshold l = do+      [grp1, grp2] <- regexGroups regex l+      return+        ( grp1,+          filter (not . null . fst) $+            map+              ( \x ->+                  if "!" `isPrefixOf` x+                    then (drop 1 x, False)+                    else (x, True)+              )+              $ words grp2+        ) -              isChild (v, v_ancestors) = do-                cmp <- lookup parent v_ancestors-                guard $-                  sort (map fst v_ancestors) ==-                  sort (parent : ancestors)-                return ((v, cmp), ancestors')+    unfold thresholds ((parent, parent_cmp), ancestors) =+      let ancestors' = parent : ancestors -          in ((parent, parent_cmp), mapMaybe isChild thresholds)+          isChild (v, v_ancestors) = do+            cmp <- lookup parent v_ancestors+            guard $+              sort (map fst v_ancestors)+                == sort (parent : ancestors)+            return ((v, cmp), ancestors')+       in ((parent, parent_cmp), mapMaybe isChild thresholds)  --- Doing the atual tuning -tuneThreshold :: AutotuneOptions-              -> [(DatasetName, RunDataset, T.Text)]-              -> Path -> (String, Path)-              -> IO Path+tuneThreshold ::+  AutotuneOptions ->+  [(DatasetName, RunDataset, T.Text)] ->+  Path ->+  (String, Path) ->+  IO Path tuneThreshold opts datasets already_tuned (v, _v_path) = do   (_, threshold) <-     foldM tuneDataset (thresholdMin, thresholdMax) datasets   return $ (v, threshold) : already_tuned-   where     tuneDataset :: (Int, Int) -> (DatasetName, RunDataset, T.Text) -> IO (Int, Int)     tuneDataset (tMin, tMax) (dataset_name, run, entry_point) =-      if not $ isPrefixOf (T.unpack entry_point ++ ".") v then do-        when (optVerbose opts > 0) $-          putStrLn $ unwords [v, "is irrelevant for", T.unpack entry_point]-        return (tMin, tMax)-      else do--        putStrLn $ unwords ["Tuning", v, "on entry point", T.unpack entry_point,-                             "and dataset", dataset_name]+      if not $ isPrefixOf (T.unpack entry_point ++ ".") v+        then do+          when (optVerbose opts > 0) $+            putStrLn $ unwords [v, "is irrelevant for", T.unpack entry_point]+          return (tMin, tMax)+        else do+          putStrLn $+            unwords+              [ "Tuning",+                v,+                "on entry point",+                T.unpack entry_point,+                "and dataset",+                dataset_name+              ] -        sample_run <- run (optTimeout opts) ((v, tMax) : already_tuned)+          sample_run <- run (optTimeout opts) ((v, tMax) : already_tuned) -        case sample_run of-          Left err -> do-            -- If the sampling run fails, we treat it as zero information.-            -- One of our ancestor thresholds will have be set such that-            -- this path is never taken.-            when (optVerbose opts > 0) $ putStrLn $-              "Sampling run failed:\n" ++ err-            return (tMin, tMax)-          Right (cmps, t) -> do-            let ePars = S.toAscList $-                        S.map snd $+          case sample_run of+            Left err -> do+              -- If the sampling run fails, we treat it as zero information.+              -- One of our ancestor thresholds will have be set such that+              -- this path is never taken.+              when (optVerbose opts > 0) $+                putStrLn $+                  "Sampling run failed:\n" ++ err+              return (tMin, tMax)+            Right (cmps, t) -> do+              let ePars =+                    S.toAscList $+                      S.map snd $                         S.filter (candidateEPar (tMin, tMax)) $-                        S.fromList cmps+                          S.fromList cmps -                runner :: Int -> Int -> IO (Maybe Int)-                runner timeout' threshold = do-                  res <- run timeout' ((v, threshold) : already_tuned)-                  case res of-                    Right (_, runTime) ->-                      return $ Just runTime-                    _ ->-                      return Nothing+                  runner :: Int -> Int -> IO (Maybe Int)+                  runner timeout' threshold = do+                    res <- run timeout' ((v, threshold) : already_tuned)+                    case res of+                      Right (_, runTime) ->+                        return $ Just runTime+                      _ ->+                        return Nothing -            when (optVerbose opts > 1) $-              putStrLn $ unwords ("Got ePars: " : map show ePars)+              when (optVerbose opts > 1) $+                putStrLn $ unwords ("Got ePars: " : map show ePars) -            newMax <- binarySearch runner (t, tMax) ePars-            let newMinIdx = pred <$> elemIndex newMax ePars-            let newMin = maxinum $ catMaybes [Just tMin, newMinIdx]-            return (newMin, newMax)+              newMax <- binarySearch runner (t, tMax) ePars+              let newMinIdx = pred <$> elemIndex newMax ePars+              let newMin = maxinum $ catMaybes [Just tMin, newMinIdx]+              return (newMin, newMax)      bestPair :: [(Int, Int)] -> (Int, Int)     bestPair = minimumBy (compare `on` fst)@@ -263,40 +295,49 @@     candidateEPar (tMin, tMax) (threshold, ePar) =       ePar > tMin && ePar < tMax && threshold == v -     binarySearch :: (Int -> Int -> IO (Maybe Int)) -> (Int, Int) -> [Int] -> IO Int     binarySearch runner best@(best_t, best_e_par) xs =       case splitAt (length xs `div` 2) xs of         (lower, middle : middle' : upper) -> do           when (optVerbose opts > 0) $-            putStrLn $ unwords ["Trying e_par", show middle,-                                "and", show middle']+            putStrLn $+              unwords+                [ "Trying e_par",+                  show middle,+                  "and",+                  show middle'+                ]           candidate <- runner (timeout best_t) middle           candidate' <- runner (timeout best_t) middle'           case (candidate, candidate') of             (Just new_t, Just new_t') ->-              if new_t < new_t' then-                -- recurse into lower half-                binarySearch runner (bestPair [(new_t, middle), best]) lower-              else-                -- recurse into upper half-                binarySearch runner (bestPair [(new_t', middle'), best]) upper+              if new_t < new_t'+                then -- recurse into lower half+                  binarySearch runner (bestPair [(new_t, middle), best]) lower+                else -- recurse into upper half+                  binarySearch runner (bestPair [(new_t', middle'), best]) upper             (Just new_t, Nothing) ->               -- recurse into lower half               binarySearch runner (bestPair [(new_t, middle), best]) lower             (Nothing, Just new_t') ->-                -- recurse into upper half-                binarySearch runner (bestPair [(new_t', middle'), best]) upper+              -- recurse into upper half+              binarySearch runner (bestPair [(new_t', middle'), best]) upper             (Nothing, Nothing) -> do               when (optVerbose opts > 2) $-                putStrLn $ unwords ["Timing failed for candidates",-                                    show middle, "and", show middle']+                putStrLn $+                  unwords+                    [ "Timing failed for candidates",+                      show middle,+                      "and",+                      show middle'+                    ]               return best_e_par         (_, _) -> do           when (optVerbose opts > 0) $             putStrLn $ unwords ["Trying e_pars", show xs]-          candidates <- catMaybes . zipWith (fmap . flip (,)) xs <$>-                        mapM (runner $ timeout best_t) xs+          candidates <-+            catMaybes . zipWith (fmap . flip (,)) xs+              <$> mapM (runner $ timeout best_t) xs           return $ snd $ bestPair $ best : candidates  --- CLI@@ -308,7 +349,7 @@    forest <- thresholdForest prog   when (optVerbose opts > 0) $-    putStrLn $ ("Threshold forest:\n"++) $ drawForest $ map (fmap show) forest+    putStrLn $ ("Threshold forest:\n" ++) $ drawForest $ map (fmap show) forest    foldM (tuneThreshold opts datasets) [] $ tuningPaths forest @@ -316,8 +357,9 @@ runAutotuner opts prog = do   best <- tune opts prog -  let tuning = unlines $ do (s, n) <- sortOn fst best-                            return $ s ++ "=" ++ show n+  let tuning = unlines $ do+        (s, n) <- sortOn fst best+        return $ s ++ "=" ++ show n    case optTuning opts of     Nothing -> return ()@@ -328,54 +370,87 @@   putStrLn $ "Result of autotuning:\n" ++ tuning  commandLineOptions :: [FunOptDescr AutotuneOptions]-commandLineOptions = [-    Option "r" ["runs"]-    (ReqArg (\n ->+commandLineOptions =+  [ Option+      "r"+      ["runs"]+      ( ReqArg+          ( \n ->               case reads n of                 [(n', "")] | n' >= 0 ->                   Right $ \config ->-                  config { optRuns = n'-                         }+                    config+                      { optRuns = n'+                      }                 _ ->-                  Left $ error $ "'" ++ n ++ "' is not a non-negative integer.")-     "RUNS")-    "Run each test case this many times."-  , Option [] ["backend"]-    (ReqArg (\backend -> Right $ \config -> config { optBackend = backend })-     "BACKEND")-    "The compiler used (defaults to 'opencl')."-  , Option [] ["futhark"]-    (ReqArg (\prog -> Right $ \config -> config { optFuthark = Just prog })-     "PROGRAM")-    "The binary used for operations (defaults to 'futhark')."-  , Option [] ["pass-option"]-    (ReqArg (\opt ->-               Right $ \config ->-               config { optExtraOptions = opt : optExtraOptions config })-     "OPT")-    "Pass this option to programs being run."-  , Option [] ["tuning"]-    (ReqArg (\s -> Right $ \config -> config { optTuning = Just s })-    "EXTENSION")-    "Write tuning files with this extension (default: .tuning)."-  , Option [] ["timeout"]-    (ReqArg (\n ->-               case reads n of-                 [(n', "")] ->-                   Right $ \config -> config { optTimeout = n' }-                 _ ->-                   Left $ error $ "'" ++ n ++ "' is not a non-negative integer.")-    "SECONDS")-    "Initial tuning timeout for each dataset. Later tuning runs are based off of the runtime of the first run."-  , Option "v" ["verbose"]-    (NoArg $ Right $ \config -> config { optVerbose = optVerbose config + 1 })-    "Enable logging.  Pass multiple times for more."-   ]+                  Left $ error $ "'" ++ n ++ "' is not a non-negative integer."+          )+          "RUNS"+      )+      "Run each test case this many times.",+    Option+      []+      ["backend"]+      ( ReqArg+          (\backend -> Right $ \config -> config {optBackend = backend})+          "BACKEND"+      )+      "The compiler used (defaults to 'opencl').",+    Option+      []+      ["futhark"]+      ( ReqArg+          (\prog -> Right $ \config -> config {optFuthark = Just prog})+          "PROGRAM"+      )+      "The binary used for operations (defaults to 'futhark').",+    Option+      []+      ["pass-option"]+      ( ReqArg+          ( \opt ->+              Right $ \config ->+                config {optExtraOptions = opt : optExtraOptions config}+          )+          "OPT"+      )+      "Pass this option to programs being run.",+    Option+      []+      ["tuning"]+      ( ReqArg+          (\s -> Right $ \config -> config {optTuning = Just s})+          "EXTENSION"+      )+      "Write tuning files with this extension (default: .tuning).",+    Option+      []+      ["timeout"]+      ( ReqArg+          ( \n ->+              case reads n of+                [(n', "")] ->+                  Right $ \config -> config {optTimeout = n'}+                _ ->+                  Left $ error $ "'" ++ n ++ "' is not a non-negative integer."+          )+          "SECONDS"+      )+      "Initial tuning timeout for each dataset. Later tuning runs are based off of the runtime of the first run.",+    Option+      "v"+      ["verbose"]+      (NoArg $ Right $ \config -> config {optVerbose = optVerbose config + 1})+      "Enable logging.  Pass multiple times for more."+  ]  -- | Run @futhark autotune@ main :: String -> [String] -> IO ()-main = mainWithOptions initialAutotuneOptions commandLineOptions-       "options... program" $-       \progs config ->-         case progs of [prog] -> Just $ runAutotuner config prog-                       _      -> Nothing+main = mainWithOptions+  initialAutotuneOptions+  commandLineOptions+  "options... program"+  $ \progs config ->+    case progs of+      [prog] -> Just $ runAutotuner config prog+      _ -> Nothing
src/Futhark/CLI/Bench.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE BangPatterns #-}-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+ -- | @futhark bench@-module Futhark.CLI.Bench ( main ) where+module Futhark.CLI.Bench (main) where  import Control.Monad import Control.Monad.Except@@ -10,46 +11,60 @@ import qualified Data.ByteString.Lazy.Char8 as LBS import Data.Either import Data.IORef-import Data.Maybe import Data.List (foldl', sortBy)+import Data.Maybe import Data.Ord import qualified Data.Text as T+import Futhark.Bench+import Futhark.Test+import Futhark.Util (fancyTerminal, maxinum, maybeNth, pmapIO)+import Futhark.Util.Console+import Futhark.Util.Options import System.Console.ANSI (clearLine) import System.Console.GetOpt import System.Directory import System.Environment-import System.IO import System.Exit+import System.IO import Text.Printf import Text.Regex.TDFA -import Futhark.Bench-import Futhark.Test-import Futhark.Util (fancyTerminal, maybeNth, maxinum, pmapIO)-import Futhark.Util.Console-import Futhark.Util.Options- data BenchOptions = BenchOptions-                   { optBackend :: String-                   , optFuthark :: Maybe String-                   , optRunner :: String-                   , optRuns :: Int-                   , optExtraOptions :: [String]-                   , optCompilerOptions :: [String]-                   , optJSON :: Maybe FilePath-                   , optTimeout :: Int-                   , optSkipCompilation :: Bool-                   , optExcludeCase :: [String]-                   , optIgnoreFiles :: [Regex]-                   , optEntryPoint :: Maybe String-                   , optTuning :: Maybe String-                   , optConcurrency :: Maybe Int-                   , optVerbose :: Int-                   }+  { optBackend :: String,+    optFuthark :: Maybe String,+    optRunner :: String,+    optRuns :: Int,+    optExtraOptions :: [String],+    optCompilerOptions :: [String],+    optJSON :: Maybe FilePath,+    optTimeout :: Int,+    optSkipCompilation :: Bool,+    optExcludeCase :: [String],+    optIgnoreFiles :: [Regex],+    optEntryPoint :: Maybe String,+    optTuning :: Maybe String,+    optConcurrency :: Maybe Int,+    optVerbose :: Int+  }  initialBenchOptions :: BenchOptions-initialBenchOptions = BenchOptions "c" Nothing "" 10 [] [] Nothing (-1) False-                      ["nobench", "disable"] [] Nothing (Just "tuning") Nothing 0+initialBenchOptions =+  BenchOptions+    "c"+    Nothing+    ""+    10+    []+    []+    Nothing+    (-1)+    False+    ["nobench", "disable"]+    []+    Nothing+    (Just "tuning")+    Nothing+    0  runBenchmarks :: BenchOptions -> [FilePath] -> IO () runBenchmarks opts paths = do@@ -60,9 +75,10 @@    benchmarks <- filter (not . ignored . fst) <$> testSpecsFromPathsOrDie paths   -- Try to avoid concurrency at both program and data set level.-  let opts' = if length paths /= 1-              then opts { optConcurrency = Just 1}-              else opts+  let opts' =+        if length paths /= 1+          then opts {optConcurrency = Just 1}+          else opts   (skipped_benchmarks, compiled_benchmarks) <-     partitionEithers <$> pmapIO (optConcurrency opts) (compileBenchmark opts') benchmarks @@ -70,24 +86,28 @@    putStrLn $ "Reporting average runtime of " ++ show (optRuns opts) ++ " runs for each dataset." -  results <- concat <$> mapM (runBenchmark opts)-             (sortBy (comparing fst) compiled_benchmarks)+  results <-+    concat+      <$> mapM+        (runBenchmark opts)+        (sortBy (comparing fst) compiled_benchmarks)   case optJSON opts of     Nothing -> return ()     Just file -> LBS.writeFile file $ encodeBenchResults results   when (anyFailed results) exitFailure--  where ignored f = any (`match` f) $ optIgnoreFiles opts+  where+    ignored f = any (`match` f) $ optIgnoreFiles opts  anyFailed :: [BenchResult] -> Bool anyFailed = any failedBenchResult-  where failedBenchResult (BenchResult _ xs) =-          any failedResult xs-        failedResult (DataResult _ Left{}) = True-        failedResult _                     = False+  where+    failedBenchResult (BenchResult _ xs) =+      any failedResult xs+    failedResult (DataResult _ Left {}) = True+    failedResult _ = False  anyFailedToCompile :: [SkipReason] -> Bool-anyFailedToCompile = not . all (==Skipped)+anyFailedToCompile = not . all (== Skipped)  data SkipReason = Skipped | FailedToCompile   deriving (Eq)@@ -95,89 +115,108 @@ compileOptions :: BenchOptions -> IO CompileOptions compileOptions opts = do   futhark <- maybe getExecutablePath return $ optFuthark opts-  return $ CompileOptions { compFuthark = futhark-                          , compBackend = optBackend opts-                          , compOptions = optCompilerOptions opts-                          }+  return $+    CompileOptions+      { compFuthark = futhark,+        compBackend = optBackend opts,+        compOptions = optCompilerOptions opts+      } -compileBenchmark :: BenchOptions -> (FilePath, ProgramTest)-                 -> IO (Either SkipReason (FilePath, [InputOutputs]))+compileBenchmark ::+  BenchOptions ->+  (FilePath, ProgramTest) ->+  IO (Either SkipReason (FilePath, [InputOutputs])) compileBenchmark opts (program, spec) =   case testAction spec of-    RunCases cases _ _ | "nobench" `notElem` testTags spec,-                         "disable" `notElem` testTags spec,-                         any hasRuns cases ->-      if optSkipCompilation opts-        then do-        exists <- doesFileExist $ binaryName program-        if exists-          then return $ Right (program, cases)-          else do putStrLn $ binaryName program ++ " does not exist, but --skip-compilation passed."-                  return $ Left FailedToCompile-        else do--        putStr $ "Compiling " ++ program ++ "...\n"--        compile_opts <- compileOptions opts+    RunCases cases _ _+      | "nobench" `notElem` testTags spec,+        "disable" `notElem` testTags spec,+        any hasRuns cases ->+        if optSkipCompilation opts+          then do+            exists <- doesFileExist $ binaryName program+            if exists+              then return $ Right (program, cases)+              else do+                putStrLn $ binaryName program ++ " does not exist, but --skip-compilation passed."+                return $ Left FailedToCompile+          else do+            putStr $ "Compiling " ++ program ++ "...\n" -        res <- prepareBenchmarkProgram (optConcurrency opts) compile_opts program cases+            compile_opts <- compileOptions opts -        case res of-          Left (err, errstr) -> do-            putStrLn $ inRed err-            maybe (return ()) SBS.putStrLn errstr-            return $ Left FailedToCompile-          Right () ->-            return $ Right (program, cases)+            res <- prepareBenchmarkProgram (optConcurrency opts) compile_opts program cases +            case res of+              Left (err, errstr) -> do+                putStrLn $ inRed err+                maybe (return ()) SBS.putStrLn errstr+                return $ Left FailedToCompile+              Right () ->+                return $ Right (program, cases)     _ ->       return $ Left Skipped-  where hasRuns (InputOutputs _ runs) = not $ null runs+  where+    hasRuns (InputOutputs _ runs) = not $ null runs  runBenchmark :: BenchOptions -> (FilePath, [InputOutputs]) -> IO [BenchResult] runBenchmark opts (program, cases) = mapM forInputOutputs $ filter relevant cases-  where forInputOutputs (InputOutputs entry_name runs) = do-          (tuning_opts, tuning_desc) <- determineTuning (optTuning opts) program+  where+    forInputOutputs (InputOutputs entry_name runs) = do+      (tuning_opts, tuning_desc) <- determineTuning (optTuning opts) program -          putStr $ inBold $ "\nResults for " ++ program' ++ tuning_desc ++ ":\n"-          let opts' = opts { optExtraOptions =-                               optExtraOptions opts ++ tuning_opts }-          BenchResult program' . catMaybes <$>-            mapM (runBenchmarkCase opts' program entry_name pad_to) runs-          where program' = if entry_name == "main"-                           then program-                           else program ++ ":" ++ T.unpack entry_name+      putStr $ inBold $ "\nResults for " ++ program' ++ tuning_desc ++ ":\n"+      let opts' =+            opts+              { optExtraOptions =+                  optExtraOptions opts ++ tuning_opts+              }+      BenchResult program' . catMaybes+        <$> mapM (runBenchmarkCase opts' program entry_name pad_to) runs+      where+        program' =+          if entry_name == "main"+            then program+            else program ++ ":" ++ T.unpack entry_name -        relevant = maybe (const True) (==) (optEntryPoint opts) . T.unpack . iosEntryPoint+    relevant = maybe (const True) (==) (optEntryPoint opts) . T.unpack . iosEntryPoint -        pad_to = foldl max 0 $ concatMap (map (length . runDescription) . iosTestRuns) cases+    pad_to = foldl max 0 $ concatMap (map (length . runDescription) . iosTestRuns) cases  runOptions :: (Int -> IO ()) -> BenchOptions -> RunOptions runOptions f opts =-  RunOptions { runRunner = optRunner opts-             , runRuns = optRuns opts-             , runExtraOptions = optExtraOptions opts-             , runTimeout = optTimeout opts-             , runVerbose = optVerbose opts-             , runResultAction = Just f-             }+  RunOptions+    { runRunner = optRunner opts,+      runRuns = optRuns opts,+      runExtraOptions = optExtraOptions opts,+      runTimeout = optTimeout opts,+      runVerbose = optVerbose opts,+      runResultAction = Just f+    }  progressBar :: Int -> Int -> Int -> String progressBar cur bound steps =-  "[" ++ map cell [1..steps] ++ "] " ++ show cur ++ "/" ++ show bound-  where step_size :: Double-        step_size = fromIntegral bound / fromIntegral steps-        cur' = fromIntegral cur-        chars = " ▏▎▍▍▌▋▊▉█"-        char i = fromMaybe ' ' $ maybeNth (i::Int) chars-        num_chars = fromIntegral $ length chars+  "[" ++ map cell [1 .. steps] ++ "] " ++ show cur ++ "/" ++ show bound+  where+    step_size :: Double+    step_size = fromIntegral bound / fromIntegral steps+    cur' = fromIntegral cur+    chars = " ▏▎▍▍▌▋▊▉█"+    char i = fromMaybe ' ' $ maybeNth (i :: Int) chars+    num_chars = fromIntegral $ length chars -        cell :: Int -> Char-        cell i-          | i' * step_size <= cur' = char 9-          | otherwise = char (floor (((cur' - (i'-1) * step_size) * num_chars)-                                     / step_size))-          where i' = fromIntegral i+    cell :: Int -> Char+    cell i+      | i' * step_size <= cur' = char 9+      | otherwise =+        char+          ( floor+              ( ((cur' - (i' -1) * step_size) * num_chars)+                  / step_size+              )+          )+      where+        i' = fromIntegral i  descString :: String -> Int -> String descString desc pad_to = desc ++ ": " ++ replicate (pad_to - length desc) ' '@@ -185,36 +224,45 @@ mkProgressPrompt :: Int -> Int -> String -> IO (Maybe Int -> IO ()) mkProgressPrompt runs pad_to dataset_desc   | fancyTerminal = do-      count <- newIORef (0::Int)-      return $ \us -> do-        putStr "\r" -- Go to start of line.-        i <- readIORef count-        let i' = if isJust us then i+1 else i-        writeIORef count i'-        putStr $ descString dataset_desc pad_to ++ progressBar i' runs 10-        putStr " " -- Just to move the cursor away from the progress bar.-        hFlush stdout--  | otherwise = do-      putStr $ descString dataset_desc pad_to+    count <- newIORef (0 :: Int)+    return $ \us -> do+      putStr "\r" -- Go to start of line.+      i <- readIORef count+      let i' = if isJust us then i + 1 else i+      writeIORef count i'+      putStr $ descString dataset_desc pad_to ++ progressBar i' runs 10+      putStr " " -- Just to move the cursor away from the progress bar.       hFlush stdout-      return $ const $ return ()+  | otherwise = do+    putStr $ descString dataset_desc pad_to+    hFlush stdout+    return $ const $ return ()  reportResult :: [RunResult] -> IO () reportResult results = do   let runtimes = map (fromIntegral . runMicroseconds) results       avg = sum runtimes / fromIntegral (length runtimes)       rsd = stddevp runtimes / mean runtimes :: Double-  putStrLn $ printf "%10.0fμs (RSD: %.3f; min: %3.0f%%; max: %+3.0f%%)"-    avg rsd ((minimum runtimes / avg - 1) * 100) ((maxinum runtimes / avg - 1) * 100)+  putStrLn $+    printf+      "%10.0fμs (RSD: %.3f; min: %3.0f%%; max: %+3.0f%%)"+      avg+      rsd+      ((minimum runtimes / avg - 1) * 100)+      ((maxinum runtimes / avg - 1) * 100) -runBenchmarkCase :: BenchOptions -> FilePath -> T.Text -> Int -> TestRun-                 -> IO (Maybe DataResult)-runBenchmarkCase _ _ _ _ (TestRun _ _ RunTimeFailure{} _ _) =+runBenchmarkCase ::+  BenchOptions ->+  FilePath ->+  T.Text ->+  Int ->+  TestRun ->+  IO (Maybe DataResult)+runBenchmarkCase _ _ _ _ (TestRun _ _ RunTimeFailure {} _ _) =   return Nothing -- Not our concern, we are not a testing tool. runBenchmarkCase opts _ _ _ (TestRun tags _ _ _ _)   | any (`elem` tags) $ optExcludeCase opts =-      return Nothing+    return Nothing runBenchmarkCase opts program entry pad_to tr@(TestRun _ input_spec (Succeeds expected_spec) _ dataset_desc) = do   prompt <- mkProgressPrompt (optRuns opts) pad_to dataset_desc @@ -222,9 +270,14 @@   -- error occurs it's easier to see where.   prompt Nothing -  res <- benchmarkDataset (runOptions (prompt . Just) opts)-         program entry input_spec expected_spec-         (testRunReferenceOutput program entry tr)+  res <-+    benchmarkDataset+      (runOptions (prompt . Just) opts)+      program+      entry+      input_spec+      expected_spec+      (testRunReferenceOutput program entry tr)    when fancyTerminal $ do     clearLine@@ -243,98 +296,169 @@       return $ Just $ DataResult dataset_desc $ Right (runtimes, errout)  commandLineOptions :: [FunOptDescr BenchOptions]-commandLineOptions = [-    Option "r" ["runs"]-    (ReqArg (\n ->+commandLineOptions =+  [ Option+      "r"+      ["runs"]+      ( ReqArg+          ( \n ->               case reads n of                 [(n', "")] | n' > 0 ->                   Right $ \config ->-                  config { optRuns = n'-                         }+                    config+                      { optRuns = n'+                      }                 _ ->-                  Left $ error $ "'" ++ n ++ "' is not a positive integer.")-     "RUNS")-    "Run each test case this many times."-  , Option [] ["backend"]-    (ReqArg (\backend -> Right $ \config -> config { optBackend = backend })-     "PROGRAM")-    "The compiler used (defaults to 'futhark-c')."-  , Option [] ["futhark"]-    (ReqArg (\prog -> Right $ \config -> config { optFuthark = Just prog })-     "PROGRAM")-    "The binary used for operations (defaults to same binary as 'futhark bench')."-  , Option [] ["runner"]-    (ReqArg (\prog -> Right $ \config -> config { optRunner = prog }) "PROGRAM")-    "The program used to run the Futhark-generated programs (defaults to nothing)."-  , Option "p" ["pass-option"]-    (ReqArg (\opt ->-               Right $ \config ->-               config { optExtraOptions = opt : optExtraOptions config })-     "OPT")-    "Pass this option to programs being run."-  , Option [] ["pass-compiler-option"]-    (ReqArg (\opt ->-               Right $ \config ->-               config { optCompilerOptions = opt : optCompilerOptions config })-     "OPT")-    "Pass this option to the compiler (or typechecker if in -t mode)."-  , Option [] ["json"]-    (ReqArg (\file ->-               Right $ \config -> config { optJSON = Just file})-    "FILE")-    "Scatter results in JSON format here."-  , Option [] ["timeout"]-    (ReqArg (\n ->-               case reads n of-                 [(n', "")]-                   | n' < max_timeout ->-                   Right $ \config -> config { optTimeout = fromIntegral n' }-                 _ ->-                   Left $ error $ "'" ++ n ++-                   "' is not an integer smaller than" ++ show max_timeout ++ ".")-    "SECONDS")-    "Number of seconds before a dataset is aborted."-  , Option [] ["skip-compilation"]-    (NoArg $ Right $ \config -> config { optSkipCompilation = True })-    "Use already compiled program."-  , Option [] ["exclude-case"]-    (ReqArg (\s -> Right $ \config ->-                config { optExcludeCase = s : optExcludeCase config })-      "TAG")-    "Do not run test cases with this tag."-  , Option [] ["ignore-files"]-    (ReqArg (\s -> Right $ \config ->-                config { optIgnoreFiles = makeRegex s : optIgnoreFiles config })-      "REGEX")-    "Ignore files matching this regular expression."-  , Option "e" ["entry-point"]-    (ReqArg (\s -> Right $ \config ->-                config { optEntryPoint = Just s })-      "NAME")-    "Only run this entry point."-  , Option [] ["tuning"]-    (ReqArg (\s -> Right $ \config -> config { optTuning = Just s })-    "EXTENSION")-    "Look for tuning files with this extension (defaults to .tuning)."-  , Option [] ["no-tuning"]-    (NoArg $ Right $ \config -> config { optTuning = Nothing })-    "Do not load tuning files."-  , Option [] ["concurrency"]-    (ReqArg (\n ->-               case reads n of-                 [(n', "")]-                   | n' > 0 ->-                   Right $ \config -> config { optConcurrency = Just n' }-                 _ ->-                   Left $ error $ "'" ++ n ++ "' is not a positive integer.")-    "NUM")-    "Number of benchmarks to prepare (not run) concurrently."-  , Option "v" ["verbose"]-    (NoArg $ Right $ \config -> config { optVerbose = optVerbose config + 1 })-    "Enable logging.  Pass multiple times for more."+                  Left $ error $ "'" ++ n ++ "' is not a positive integer."+          )+          "RUNS"+      )+      "Run each test case this many times.",+    Option+      []+      ["backend"]+      ( ReqArg+          (\backend -> Right $ \config -> config {optBackend = backend})+          "PROGRAM"+      )+      "The compiler used (defaults to 'futhark-c').",+    Option+      []+      ["futhark"]+      ( ReqArg+          (\prog -> Right $ \config -> config {optFuthark = Just prog})+          "PROGRAM"+      )+      "The binary used for operations (defaults to same binary as 'futhark bench').",+    Option+      []+      ["runner"]+      (ReqArg (\prog -> Right $ \config -> config {optRunner = prog}) "PROGRAM")+      "The program used to run the Futhark-generated programs (defaults to nothing).",+    Option+      "p"+      ["pass-option"]+      ( ReqArg+          ( \opt ->+              Right $ \config ->+                config {optExtraOptions = opt : optExtraOptions config}+          )+          "OPT"+      )+      "Pass this option to programs being run.",+    Option+      []+      ["pass-compiler-option"]+      ( ReqArg+          ( \opt ->+              Right $ \config ->+                config {optCompilerOptions = opt : optCompilerOptions config}+          )+          "OPT"+      )+      "Pass this option to the compiler (or typechecker if in -t mode).",+    Option+      []+      ["json"]+      ( ReqArg+          ( \file ->+              Right $ \config -> config {optJSON = Just file}+          )+          "FILE"+      )+      "Scatter results in JSON format here.",+    Option+      []+      ["timeout"]+      ( ReqArg+          ( \n ->+              case reads n of+                [(n', "")]+                  | n' < max_timeout ->+                    Right $ \config -> config {optTimeout = fromIntegral n'}+                _ ->+                  Left $+                    error $+                      "'" ++ n+                        ++ "' is not an integer smaller than"+                        ++ show max_timeout+                        ++ "."+          )+          "SECONDS"+      )+      "Number of seconds before a dataset is aborted.",+    Option+      []+      ["skip-compilation"]+      (NoArg $ Right $ \config -> config {optSkipCompilation = True})+      "Use already compiled program.",+    Option+      []+      ["exclude-case"]+      ( ReqArg+          ( \s -> Right $ \config ->+              config {optExcludeCase = s : optExcludeCase config}+          )+          "TAG"+      )+      "Do not run test cases with this tag.",+    Option+      []+      ["ignore-files"]+      ( ReqArg+          ( \s -> Right $ \config ->+              config {optIgnoreFiles = makeRegex s : optIgnoreFiles config}+          )+          "REGEX"+      )+      "Ignore files matching this regular expression.",+    Option+      "e"+      ["entry-point"]+      ( ReqArg+          ( \s -> Right $ \config ->+              config {optEntryPoint = Just s}+          )+          "NAME"+      )+      "Only run this entry point.",+    Option+      []+      ["tuning"]+      ( ReqArg+          (\s -> Right $ \config -> config {optTuning = Just s})+          "EXTENSION"+      )+      "Look for tuning files with this extension (defaults to .tuning).",+    Option+      []+      ["no-tuning"]+      (NoArg $ Right $ \config -> config {optTuning = Nothing})+      "Do not load tuning files.",+    Option+      []+      ["concurrency"]+      ( ReqArg+          ( \n ->+              case reads n of+                [(n', "")]+                  | n' > 0 ->+                    Right $ \config -> config {optConcurrency = Just n'}+                _ ->+                  Left $ error $ "'" ++ n ++ "' is not a positive integer."+          )+          "NUM"+      )+      "Number of benchmarks to prepare (not run) concurrently.",+    Option+      "v"+      ["verbose"]+      (NoArg $ Right $ \config -> config {optVerbose = optVerbose config + 1})+      "Enable logging.  Pass multiple times for more."   ]-  where max_timeout :: Int-        max_timeout = maxBound `div` 1000000+  where+    max_timeout :: Int+    max_timeout = maxBound `div` 1000000  -- | Run @futhark bench@. main :: String -> [String] -> IO ()@@ -346,7 +470,7 @@  -- | Numerically stable mean mean :: Floating a => [a] -> a-mean x = fst $ foldl' (\(!m, !n) x' -> (m+(x'-m)/(n+1),n+1)) (0,0) x+mean x = fst $ foldl' (\(!m, !n) x' -> (m + (x' - m) / (n + 1), n + 1)) (0, 0) x  -- | Standard deviation of population stddevp :: (Floating a) => [a] -> a@@ -354,12 +478,12 @@  -- | Population variance pvar :: (Floating a) => [a] -> a-pvar xs = centralMoment xs (2::Int)+pvar xs = centralMoment xs (2 :: Int)  -- | Central moments centralMoment :: (Floating b, Integral t) => [b] -> t -> b-centralMoment _  1 = 0-centralMoment xs r = sum (map (\x -> (x-m)^r) xs) / n-    where-      m = mean xs-      n = fromIntegral $ length xs+centralMoment _ 1 = 0+centralMoment xs r = sum (map (\x -> (x - m) ^ r) xs) / n+  where+    m = mean xs+    n = fromIntegral $ length xs
src/Futhark/CLI/C.hs view
@@ -1,14 +1,19 @@ {-# LANGUAGE FlexibleContexts #-}+ -- | @futhark c@ module Futhark.CLI.C (main) where  import Futhark.Actions (compileCAction)-import Futhark.Passes (sequentialCpuPipeline) import Futhark.Compiler.CLI+import Futhark.Passes (sequentialCpuPipeline)  -- | Run @futhark c@ main :: String -> [String] -> IO ()-main = compilerMain () []-       "Compile sequential C" "Generate sequential C code from optimised Futhark program."-       sequentialCpuPipeline $ \fcfg () mode outpath prog ->-  actionProcedure (compileCAction fcfg mode outpath) prog+main = compilerMain+  ()+  []+  "Compile sequential C"+  "Generate sequential C code from optimised Futhark program."+  sequentialCpuPipeline+  $ \fcfg () mode outpath prog ->+    actionProcedure (compileCAction fcfg mode outpath) prog
src/Futhark/CLI/CUDA.hs view
@@ -1,14 +1,19 @@ {-# LANGUAGE FlexibleContexts #-}+ -- | @futhark cuda@ module Futhark.CLI.CUDA (main) where  import Futhark.Actions (compileCUDAAction)-import Futhark.Passes (gpuPipeline) import Futhark.Compiler.CLI+import Futhark.Passes (gpuPipeline)  -- | Run @futhark cuda@. main :: String -> [String] -> IO ()-main = compilerMain () []-       "Compile CUDA" "Generate CUDA/C code from optimised Futhark program."-       gpuPipeline $ \fcfg () mode outpath prog ->-  actionProcedure (compileCUDAAction fcfg mode outpath) prog+main = compilerMain+  ()+  []+  "Compile CUDA"+  "Generate CUDA/C code from optimised Futhark program."+  gpuPipeline+  $ \fcfg () mode outpath prog ->+    actionProcedure (compileCUDAAction fcfg mode outpath) prog
src/Futhark/CLI/Check.hs view
@@ -3,21 +3,24 @@  import Control.Monad import Control.Monad.IO.Class-import System.Console.GetOpt-import System.IO- import Futhark.Compiler import Futhark.Util.Options+import System.Console.GetOpt+import System.IO -newtype CheckConfig = CheckConfig { checkWarn :: Bool }+newtype CheckConfig = CheckConfig {checkWarn :: Bool}  newCheckConfig :: CheckConfig newCheckConfig = CheckConfig True  options :: [FunOptDescr CheckConfig]-options = [Option "w" []-           (NoArg $ Right $ \cfg -> cfg { checkWarn = False })-           "Disable all warnings."]+options =+  [ Option+      "w"+      []+      (NoArg $ Right $ \cfg -> cfg {checkWarn = False})+      "Disable all warnings."+  ]  -- | Run @futhark check@. main :: String -> [String] -> IO ()
src/Futhark/CLI/Datacmp.hs view
@@ -1,29 +1,30 @@ {-# LANGUAGE OverloadedStrings #-}+ -- | @futhark datacmp@ module Futhark.CLI.Datacmp (main) where  import qualified Data.ByteString.Lazy.Char8 as BS-import System.Exit- import Futhark.Test.Values import Futhark.Util.Options+import System.Exit  -- | Run @futhark datacmp@ main :: String -> [String] -> IO () main = mainWithOptions () [] "<file> <file>" f-  where f [file_a, file_b] () = Just $ do-          vs_a_maybe <- readValues <$> BS.readFile file_a-          vs_b_maybe <- readValues <$> BS.readFile file_b-          case (vs_a_maybe, vs_b_maybe) of-            (Nothing, _) ->-              error $ "Error reading values from " ++ file_a-            (_, Nothing) ->-              error $ "Error reading values from " ++ file_b-            (Just vs_a, Just vs_b) ->-              case compareValues vs_a vs_b of-                [] -> return ()-                es -> do mapM_ print es-                         exitWith $ ExitFailure 2--        f _ _ =-          Nothing+  where+    f [file_a, file_b] () = Just $ do+      vs_a_maybe <- readValues <$> BS.readFile file_a+      vs_b_maybe <- readValues <$> BS.readFile file_b+      case (vs_a_maybe, vs_b_maybe) of+        (Nothing, _) ->+          error $ "Error reading values from " ++ file_a+        (_, Nothing) ->+          error $ "Error reading values from " ++ file_b+        (Just vs_a, Just vs_b) ->+          case compareValues vs_a vs_b of+            [] -> return ()+            es -> do+              mapM_ print es+              exitWith $ ExitFailure 2+    f _ _ =+      Nothing
src/Futhark/CLI/Dataset.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Strict #-}+ -- | @futhark dataset@ module Futhark.CLI.Dataset (main) where @@ -9,155 +10,193 @@ import qualified Data.ByteString.Lazy.Char8 as BS import qualified Data.Map.Strict as M import qualified Data.Text as T-import Data.Word-import qualified Data.Vector.Unboxed.Mutable as UMVec-import qualified Data.Vector.Unboxed as UVec import Data.Vector.Generic (freeze)+import qualified Data.Vector.Unboxed as UVec+import qualified Data.Vector.Unboxed.Mutable as UMVec+import Data.Word+import Futhark.Test.Values+import Futhark.Util.Options+import Language.Futhark.Parser+import Language.Futhark.Pretty ()+import Language.Futhark.Prop (UncheckedTypeExp, namesToPrimTypes)+import Language.Futhark.Syntax hiding+  ( FloatValue (..),+    IntValue (..),+    PrimValue (..),+    Value,+    ValueType,+  ) import System.Console.GetOpt import System.Exit import System.IO-import System.Random.PCG (initialize, Variate, uniformR)--import Language.Futhark.Syntax hiding-  (Value, ValueType, PrimValue(..), IntValue(..), FloatValue(..))-import Language.Futhark.Prop (UncheckedTypeExp, namesToPrimTypes)-import Language.Futhark.Parser-import Language.Futhark.Pretty ()--import Futhark.Test.Values-import Futhark.Util.Options+import System.Random.PCG (Variate, initialize, uniformR)  -- | Run @futhark dataset@. main :: String -> [String] -> IO () main = mainWithOptions initialDataOptions commandLineOptions "options..." f-  where f [] config-          | null $ optOrders config = Just $ do-              maybe_vs <- readValues <$> BS.getContents-              case maybe_vs of-                Nothing -> do hPutStrLn stderr "Malformed data on standard input."-                              exitFailure-                Just vs ->-                  case format config of-                    Text -> mapM_ (putStrLn . pretty) vs-                    Binary -> mapM_ (BS.putStr . Bin.encode) vs-                    Type -> mapM_ (putStrLn . pretty . valueType) vs-          | otherwise =-              Just $ zipWithM_ ($) (optOrders config)-              [fromIntegral (optSeed config)..]-        f _ _ =-          Nothing+  where+    f [] config+      | null $ optOrders config = Just $ do+        maybe_vs <- readValues <$> BS.getContents+        case maybe_vs of+          Nothing -> do+            hPutStrLn stderr "Malformed data on standard input."+            exitFailure+          Just vs ->+            case format config of+              Text -> mapM_ (putStrLn . pretty) vs+              Binary -> mapM_ (BS.putStr . Bin.encode) vs+              Type -> mapM_ (putStrLn . pretty . valueType) vs+      | otherwise =+        Just $+          zipWithM_+            ($)+            (optOrders config)+            [fromIntegral (optSeed config) ..]+    f _ _ =+      Nothing -data OutputFormat = Text-                  | Binary-                  | Type-                  deriving (Eq, Ord, Show)+data OutputFormat+  = Text+  | Binary+  | Type+  deriving (Eq, Ord, Show)  data DataOptions = DataOptions-                   { optSeed :: Int-                   , optRange :: RandomConfiguration-                   , optOrders :: [Word64 -> IO ()]-                   , format :: OutputFormat-                   }+  { optSeed :: Int,+    optRange :: RandomConfiguration,+    optOrders :: [Word64 -> IO ()],+    format :: OutputFormat+  }  initialDataOptions :: DataOptions initialDataOptions = DataOptions 1 initialRandomConfiguration [] Text  commandLineOptions :: [FunOptDescr DataOptions]-commandLineOptions = [-    Option "s" ["seed"]-    (ReqArg (\n ->+commandLineOptions =+  [ Option+      "s"+      ["seed"]+      ( ReqArg+          ( \n ->               case reads n of                 [(n', "")] ->-                  Right $ \config -> config { optSeed = n' }+                  Right $ \config -> config {optSeed = n'}                 _ ->-                  Left $ do hPutStrLn stderr $ "'" ++ n ++ "' is not an integer."-                            exitFailure)-     "SEED")-    "The seed to use when initialising the RNG."-  , Option "g" ["generate"]-    (ReqArg (\t ->+                  Left $ do+                    hPutStrLn stderr $ "'" ++ n ++ "' is not an integer."+                    exitFailure+          )+          "SEED"+      )+      "The seed to use when initialising the RNG.",+    Option+      "g"+      ["generate"]+      ( ReqArg+          ( \t ->               case tryMakeGenerator t of                 Right g ->                   Right $ \config ->-                  config { optOrders =-                             optOrders config ++-                             [g (optRange config) (format config)]-                         }+                    config+                      { optOrders =+                          optOrders config+                            ++ [g (optRange config) (format config)]+                      }                 Left err ->-                  Left $ do hPutStrLn stderr err-                            exitFailure)-     "TYPE")-    "Generate a random value of this type."-  , Option [] ["text"]-    (NoArg $ Right $ \opts -> opts { format = Text })-    "Output data in text format (must precede --generate)."-  , Option "b" ["binary"]-    (NoArg $ Right $ \opts -> opts { format = Binary })-    "Output data in binary Futhark format (must precede --generate)."-  , Option "t" ["type"]-    (NoArg $ Right $ \opts -> opts { format = Type })-    "Output the type (textually) rather than the value (must precede --generate)."-  , setRangeOption "i8" seti8Range-  , setRangeOption "i16" seti16Range-  , setRangeOption "i32" seti32Range-  , setRangeOption "i64" seti64Range-  , setRangeOption "u8" setu8Range-  , setRangeOption "u16" setu16Range-  , setRangeOption "u32" setu32Range-  , setRangeOption "u64" setu64Range-  , setRangeOption "f32" setf32Range-  , setRangeOption "f64" setf64Range+                  Left $ do+                    hPutStrLn stderr err+                    exitFailure+          )+          "TYPE"+      )+      "Generate a random value of this type.",+    Option+      []+      ["text"]+      (NoArg $ Right $ \opts -> opts {format = Text})+      "Output data in text format (must precede --generate).",+    Option+      "b"+      ["binary"]+      (NoArg $ Right $ \opts -> opts {format = Binary})+      "Output data in binary Futhark format (must precede --generate).",+    Option+      "t"+      ["type"]+      (NoArg $ Right $ \opts -> opts {format = Type})+      "Output the type (textually) rather than the value (must precede --generate).",+    setRangeOption "i8" seti8Range,+    setRangeOption "i16" seti16Range,+    setRangeOption "i32" seti32Range,+    setRangeOption "i64" seti64Range,+    setRangeOption "u8" setu8Range,+    setRangeOption "u16" setu16Range,+    setRangeOption "u32" setu32Range,+    setRangeOption "u64" setu64Range,+    setRangeOption "f32" setf32Range,+    setRangeOption "f64" setf64Range   ] -setRangeOption :: Read a => String-                -> (Range a -> RandomConfiguration -> RandomConfiguration)-                -> FunOptDescr DataOptions+setRangeOption ::+  Read a =>+  String ->+  (Range a -> RandomConfiguration -> RandomConfiguration) ->+  FunOptDescr DataOptions setRangeOption tname set =-  Option "" [name]-  (ReqArg (\b ->-            let (lower,rest) = span (/=':') b+  Option+    ""+    [name]+    ( ReqArg+        ( \b ->+            let (lower, rest) = span (/= ':') b                 upper = drop 1 rest-            in case (reads lower, reads upper) of-              ([(lower', "")], [(upper', "")]) ->-                Right $ \config ->-                config { optRange = set (lower', upper') $ optRange config }-              _ ->-                Left $ do-                hPutStrLn stderr $ "Invalid bounds for " ++ tname ++ ": " ++ b-                exitFailure-            )-   "MIN:MAX") $-  "Range of " ++ tname ++ " values."-  where name = tname ++ "-bounds"+             in case (reads lower, reads upper) of+                  ([(lower', "")], [(upper', "")]) ->+                    Right $ \config ->+                      config {optRange = set (lower', upper') $ optRange config}+                  _ ->+                    Left $ do+                      hPutStrLn stderr $ "Invalid bounds for " ++ tname ++ ": " ++ b+                      exitFailure+        )+        "MIN:MAX"+    )+    $ "Range of " ++ tname ++ " values."+  where+    name = tname ++ "-bounds" -tryMakeGenerator :: String-                 -> Either String (RandomConfiguration -> OutputFormat -> Word64  -> IO ())+tryMakeGenerator ::+  String ->+  Either String (RandomConfiguration -> OutputFormat -> Word64 -> IO ()) tryMakeGenerator t   | Just vs <- readValues $ BS.pack t =-      return $ \_ fmt _ -> mapM_ (putValue fmt) vs+    return $ \_ fmt _ -> mapM_ (putValue fmt) vs   | otherwise = do-  t' <- toValueType =<< either (Left . show) Right (parseType name (T.pack t))-  return $ \conf fmt seed -> do-    let v = randomValue conf t' seed-    putValue fmt v-  where name = "option " ++ t-        putValue Text = putStrLn . pretty-        putValue Binary = BS.putStr . Bin.encode-        putValue Type = putStrLn . pretty . valueType+    t' <- toValueType =<< either (Left . show) Right (parseType name (T.pack t))+    return $ \conf fmt seed -> do+      let v = randomValue conf t' seed+      putValue fmt v+  where+    name = "option " ++ t+    putValue Text = putStrLn . pretty+    putValue Binary = BS.putStr . Bin.encode+    putValue Type = putStrLn . pretty . valueType  toValueType :: UncheckedTypeExp -> Either String ValueType-toValueType TETuple{} = Left "Cannot handle tuples yet."-toValueType TERecord{} = Left "Cannot handle records yet."-toValueType TEApply{} = Left "Cannot handle type applications yet."-toValueType TEArrow{} = Left "Cannot generate functions."-toValueType TESum{} = Left "Cannot handle sumtypes yet."+toValueType TETuple {} = Left "Cannot handle tuples yet."+toValueType TERecord {} = Left "Cannot handle records yet."+toValueType TEApply {} = Left "Cannot handle type applications yet."+toValueType TEArrow {} = Left "Cannot generate functions."+toValueType TESum {} = Left "Cannot handle sumtypes yet." toValueType (TEUnique t _) = toValueType t toValueType (TEArray t d _) = do   d' <- constantDim d   ValueType ds t' <- toValueType t-  return $ ValueType (d':ds) t'-  where constantDim (DimExpConst k _) = Right k-        constantDim _ = Left "Array has non-constant dimension declaration."+  return $ ValueType (d' : ds) t'+  where+    constantDim (DimExpConst k _) = Right k+    constantDim _ = Left "Array has non-constant dimension declaration." toValueType (TEVar (QualName [] v) _)   | Just t <- M.lookup v namesToPrimTypes = Right $ ValueType [] t toValueType (TEVar v _) =@@ -167,70 +206,88 @@ type Range a = (a, a)  data RandomConfiguration = RandomConfiguration-                           { i8Range  :: Range Int8-                           , i16Range :: Range Int16-                           , i32Range :: Range Int32-                           , i64Range :: Range Int64-                           , u8Range  :: Range Word8-                           , u16Range :: Range Word16-                           , u32Range :: Range Word32-                           , u64Range :: Range Word64-                           , f32Range :: Range Float-                           , f64Range :: Range Double-                           }+  { i8Range :: Range Int8,+    i16Range :: Range Int16,+    i32Range :: Range Int32,+    i64Range :: Range Int64,+    u8Range :: Range Word8,+    u16Range :: Range Word16,+    u32Range :: Range Word32,+    u64Range :: Range Word64,+    f32Range :: Range Float,+    f64Range :: Range Double+  }  -- The following lines provide evidence about how Haskells record -- system sucks. seti8Range :: Range Int8 -> RandomConfiguration -> RandomConfiguration-seti8Range bounds config = config { i8Range = bounds }+seti8Range bounds config = config {i8Range = bounds}+ seti16Range :: Range Int16 -> RandomConfiguration -> RandomConfiguration-seti16Range bounds config = config { i16Range = bounds }+seti16Range bounds config = config {i16Range = bounds}+ seti32Range :: Range Int32 -> RandomConfiguration -> RandomConfiguration-seti32Range bounds config = config { i32Range = bounds }+seti32Range bounds config = config {i32Range = bounds}+ seti64Range :: Range Int64 -> RandomConfiguration -> RandomConfiguration-seti64Range bounds config = config { i64Range = bounds }+seti64Range bounds config = config {i64Range = bounds}  setu8Range :: Range Word8 -> RandomConfiguration -> RandomConfiguration-setu8Range bounds config = config { u8Range = bounds }+setu8Range bounds config = config {u8Range = bounds}+ setu16Range :: Range Word16 -> RandomConfiguration -> RandomConfiguration-setu16Range bounds config = config { u16Range = bounds }+setu16Range bounds config = config {u16Range = bounds}+ setu32Range :: Range Word32 -> RandomConfiguration -> RandomConfiguration-setu32Range bounds config = config { u32Range = bounds }+setu32Range bounds config = config {u32Range = bounds}+ setu64Range :: Range Word64 -> RandomConfiguration -> RandomConfiguration-setu64Range bounds config = config { u64Range = bounds }+setu64Range bounds config = config {u64Range = bounds}  setf32Range :: Range Float -> RandomConfiguration -> RandomConfiguration-setf32Range bounds config = config { f32Range = bounds }+setf32Range bounds config = config {f32Range = bounds}+ setf64Range :: Range Double -> RandomConfiguration -> RandomConfiguration-setf64Range bounds config = config { f64Range = bounds }+setf64Range bounds config = config {f64Range = bounds}  initialRandomConfiguration :: RandomConfiguration-initialRandomConfiguration = RandomConfiguration-  (minBound, maxBound) (minBound, maxBound) (minBound, maxBound) (minBound, maxBound)-  (minBound, maxBound) (minBound, maxBound) (minBound, maxBound) (minBound, maxBound)-  (0.0, 1.0) (0.0, 1.0)+initialRandomConfiguration =+  RandomConfiguration+    (minBound, maxBound)+    (minBound, maxBound)+    (minBound, maxBound)+    (minBound, maxBound)+    (minBound, maxBound)+    (minBound, maxBound)+    (minBound, maxBound)+    (minBound, maxBound)+    (0.0, 1.0)+    (0.0, 1.0)  randomValue :: RandomConfiguration -> ValueType -> Word64 -> Value randomValue conf (ValueType ds t) seed =   case t of-    Signed Int8  -> gen  i8Range Int8Value+    Signed Int8 -> gen i8Range Int8Value     Signed Int16 -> gen i16Range Int16Value     Signed Int32 -> gen i32Range Int32Value     Signed Int64 -> gen i64Range Int64Value-    Unsigned Int8  -> gen  u8Range Word8Value+    Unsigned Int8 -> gen u8Range Word8Value     Unsigned Int16 -> gen u16Range Word16Value     Unsigned Int32 -> gen u32Range Word32Value     Unsigned Int64 -> gen u64Range Word64Value     FloatType Float32 -> gen f32Range Float32Value     FloatType Float64 -> gen f64Range Float64Value-    Bool -> gen (const (False,True)) BoolValue-  where gen range final = randomVector (range conf) final ds seed+    Bool -> gen (const (False, True)) BoolValue+  where+    gen range final = randomVector (range conf) final ds seed -randomVector :: (UMVec.Unbox v, Variate v) =>-                Range v-             -> (UVec.Vector Int -> UVec.Vector v -> Value)-             -> [Int] -> Word64-             -> Value+randomVector ::+  (UMVec.Unbox v, Variate v) =>+  Range v ->+  (UVec.Vector Int -> UVec.Vector v -> Value) ->+  [Int] ->+  Word64 ->+  Value randomVector range final ds seed = runST $ do   -- USe some nice impure computation where we can preallocate a   -- vector of the desired size, populate it via the random number@@ -239,10 +296,11 @@   g <- initialize 6364136223846793006 seed   let fill i         | i < n = do-            v <- uniformR range g-            UMVec.write arr i v-            fill $! i+1+          v <- uniformR range g+          UMVec.write arr i v+          fill $! i + 1         | otherwise =-            final (UVec.fromList ds) <$> freeze arr+          final (UVec.fromList ds) <$> freeze arr   fill 0-  where n = product ds+  where+    n = product ds
src/Futhark/CLI/Dev.hs view
@@ -1,96 +1,103 @@+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-}+ -- | Futhark Compiler Driver module Futhark.CLI.Dev (main) where -import Data.Maybe-import Data.List (intersperse) import Control.Category (id) import Control.Monad import Control.Monad.State+import qualified Data.ByteString.Lazy as ByteString+import Data.List (intersperse)+import Data.Maybe import qualified Data.Text.IO as T-import System.IO-import System.Exit-import System.Console.GetOpt-import System.FilePath--import Prelude hiding (id)--import Futhark.Compiler.CLI-import Futhark.Pass import Futhark.Actions-import Language.Futhark.Parser (parseFuthark)-import Futhark.Util.Options-import qualified Futhark.IR.SOACS as SOACS+import Futhark.Analysis.Metrics (OpMetrics)+import Futhark.Compiler.CLI+import Futhark.IR (ASTLore, Op, Prog, pretty) import qualified Futhark.IR.Kernels as Kernels-import qualified Futhark.IR.Seq as Seq import qualified Futhark.IR.KernelsMem as KernelsMem+import Futhark.IR.Prop.Aliases (CanBeAliased)+import qualified Futhark.IR.SOACS as SOACS+import qualified Futhark.IR.Seq as Seq import qualified Futhark.IR.SeqMem as SeqMem-import Futhark.IR (Prog, pretty)-import Futhark.TypeCheck (Checkable)-import qualified Futhark.Util.Pretty as PP-+import Futhark.Internalise.Defunctionalise as Defunctionalise import Futhark.Internalise.Defunctorise as Defunctorise import Futhark.Internalise.Monomorphise as Monomorphise-import Futhark.Internalise.Defunctionalise as Defunctionalise-import Futhark.Optimise.InliningDeadFun import Futhark.Optimise.CSE+import Futhark.Optimise.DoubleBuffer import Futhark.Optimise.Fusion-import Futhark.Pass.FirstOrderTransform-import Futhark.Pass.Simplify import Futhark.Optimise.InPlaceLowering-import Futhark.Optimise.DoubleBuffer+import Futhark.Optimise.InliningDeadFun import Futhark.Optimise.Sink import Futhark.Optimise.TileLoops import Futhark.Optimise.Unstream-import Futhark.Pass.KernelBabysitting-import Futhark.Pass.ExtractKernels+import Futhark.Pass import Futhark.Pass.ExpandAllocations import qualified Futhark.Pass.ExplicitAllocations.Kernels as Kernels import qualified Futhark.Pass.ExplicitAllocations.Seq as Seq+import Futhark.Pass.ExtractKernels+import Futhark.Pass.FirstOrderTransform+import Futhark.Pass.KernelBabysitting+import Futhark.Pass.Simplify import Futhark.Passes+import Futhark.TypeCheck (Checkable) import Futhark.Util.Log+import Futhark.Util.Options+import qualified Futhark.Util.Pretty as PP+import Language.Futhark.Parser (parseFuthark)+import qualified Language.SexpGrammar as Sexp+import System.Console.GetOpt+import System.Exit+import System.FilePath+import System.IO+import Prelude hiding (id)  -- | What to do with the program after it has been read.-data FutharkPipeline = PrettyPrint-                     -- ^ Just print it.-                     | TypeCheck-                     -- ^ Run the type checker; print type errors.-                     | Pipeline [UntypedPass]-                     -- ^ Run this pipeline.-                     | Defunctorise-                     -- ^ Partially evaluate away the module language.-                     | Monomorphise-                     -- ^ Defunctorise and monomorphise.-                     | Defunctionalise-                     -- ^ Defunctorise, monomorphise, and defunctionalise.--data Config = Config { futharkConfig :: FutharkConfig-                     , futharkPipeline :: FutharkPipeline-                     -- ^ Nothing is distinct from a empty pipeline --                     -- it means we don't even run the internaliser.-                     , futharkAction :: UntypedAction-                     , futharkPrintAST :: Bool-                     -- ^ If true, prints programs as raw ASTs instead-                     -- of their prettyprinted form.-                     }+data FutharkPipeline+  = -- | Just print it.+    PrettyPrint+  | -- | Run the type checker; print type errors.+    TypeCheck+  | -- | Run this pipeline.+    Pipeline [UntypedPass]+  | -- | Partially evaluate away the module language.+    Defunctorise+  | -- | Defunctorise and monomorphise.+    Monomorphise+  | -- | Defunctorise, monomorphise, and defunctionalise.+    Defunctionalise +data Config = Config+  { futharkConfig :: FutharkConfig,+    -- | Nothing is distinct from a empty pipeline -+    -- it means we don't even run the internaliser.+    futharkPipeline :: FutharkPipeline,+    futharkAction :: UntypedAction,+    -- | If true, prints programs as raw ASTs instead+    -- of their prettyprinted form.+    futharkPrintAST :: Bool+  }  -- | Get a Futhark pipeline from the configuration - an empty one if -- none exists. getFutharkPipeline :: Config -> [UntypedPass] getFutharkPipeline = toPipeline . futharkPipeline-  where toPipeline (Pipeline p) = p-        toPipeline _            = []+  where+    toPipeline (Pipeline p) = p+    toPipeline _ = [] -data UntypedPassState = SOACS (Prog SOACS.SOACS)-                      | Kernels (Prog Kernels.Kernels)-                      | Seq (Prog Seq.Seq)-                      | KernelsMem (Prog KernelsMem.KernelsMem)-                      | SeqMem (Prog SeqMem.SeqMem)+data UntypedPassState+  = SOACS (Prog SOACS.SOACS)+  | Kernels (Prog Kernels.Kernels)+  | Seq (Prog Seq.Seq)+  | KernelsMem (Prog KernelsMem.KernelsMem)+  | SeqMem (Prog SeqMem.SeqMem)  getSOACSProg :: UntypedPassState -> Maybe (Prog SOACS.SOACS) getSOACSProg (SOACS prog) = Just prog-getSOACSProg _            = Nothing+getSOACSProg _ = Nothing  class Representation s where   -- | A human-readable description of the representation expected or@@ -111,382 +118,512 @@   ppr (SeqMem prog) = PP.ppr prog   ppr (KernelsMem prog) = PP.ppr prog -newtype UntypedPass = UntypedPass (UntypedPassState-                                  -> PipelineConfig-                                  -> FutharkM UntypedPassState)--data AllActions = AllActions-  { actionSOACS :: Action SOACS.SOACS-  , actionKernels :: Action Kernels.Kernels-  , actionSeq :: Action Seq.Seq-  , actionKernelsMem :: Action KernelsMem.KernelsMem-  , actionSeqMem :: Action SeqMem.SeqMem-  }+newtype UntypedPass+  = UntypedPass+      ( UntypedPassState ->+        PipelineConfig ->+        FutharkM UntypedPassState+      ) -data UntypedAction = SOACSAction (Action SOACS.SOACS)-                   | KernelsAction (Action Kernels.Kernels)-                   | KernelsMemAction (FilePath -> Action KernelsMem.KernelsMem)-                   | SeqMemAction (FilePath -> Action SeqMem.SeqMem)-                   | PolyAction AllActions+data UntypedAction+  = SOACSAction (Action SOACS.SOACS)+  | KernelsAction (Action Kernels.Kernels)+  | KernelsMemAction (FilePath -> Action KernelsMem.KernelsMem)+  | SeqMemAction (FilePath -> Action SeqMem.SeqMem)+  | PolyAction+      ( forall lore.+        ( ASTLore lore,+          (CanBeAliased (Op lore)),+          (OpMetrics (Op lore))+        ) =>+        Action lore+      )  untypedActionName :: UntypedAction -> String untypedActionName (SOACSAction a) = actionName a untypedActionName (KernelsAction a) = actionName a untypedActionName (SeqMemAction a) = actionName $ a "" untypedActionName (KernelsMemAction a) = actionName $ a ""-untypedActionName (PolyAction a) = actionName (actionSOACS a)+untypedActionName (PolyAction a) = actionName (a :: Action SOACS.SOACS)  instance Representation UntypedAction where   representation (SOACSAction _) = "SOACS"   representation (KernelsAction _) = "Kernels"   representation (KernelsMemAction _) = "KernelsMem"   representation (SeqMemAction _) = "SeqMem"-  representation PolyAction{} = "<any>"+  representation PolyAction {} = "<any>"  newConfig :: Config newConfig = Config newFutharkConfig (Pipeline []) action False-  where action = PolyAction $ AllActions printAction printAction printAction printAction printAction+  where+    action = PolyAction printAction -changeFutharkConfig :: (FutharkConfig -> FutharkConfig)-                    -> Config -> Config-changeFutharkConfig f cfg = cfg { futharkConfig = f $ futharkConfig cfg }+changeFutharkConfig ::+  (FutharkConfig -> FutharkConfig) ->+  Config ->+  Config+changeFutharkConfig f cfg = cfg {futharkConfig = f $ futharkConfig cfg}  type FutharkOption = FunOptDescr Config  passOption :: String -> UntypedPass -> String -> [String] -> FutharkOption passOption desc pass short long =-  Option short long-  (NoArg $ Right $ \cfg ->-   cfg { futharkPipeline = Pipeline $ getFutharkPipeline cfg ++ [pass] })-  desc+  Option+    short+    long+    ( NoArg $+        Right $ \cfg ->+          cfg {futharkPipeline = Pipeline $ getFutharkPipeline cfg ++ [pass]}+    )+    desc -kernelsMemProg :: String -> UntypedPassState-               -> FutharkM (Prog KernelsMem.KernelsMem)+kernelsMemProg ::+  String ->+  UntypedPassState ->+  FutharkM (Prog KernelsMem.KernelsMem) kernelsMemProg _ (KernelsMem prog) =   return prog kernelsMemProg name rep =-  externalErrorS $ "Pass " ++ name ++-  " expects KernelsMem representation, but got " ++ representation rep+  externalErrorS $+    "Pass " ++ name+      ++ " expects KernelsMem representation, but got "+      ++ representation rep  soacsProg :: String -> UntypedPassState -> FutharkM (Prog SOACS.SOACS) soacsProg _ (SOACS prog) =   return prog soacsProg name rep =-  externalErrorS $ "Pass " ++ name ++-  " expects SOACS representation, but got " ++ representation rep+  externalErrorS $+    "Pass " ++ name+      ++ " expects SOACS representation, but got "+      ++ representation rep  kernelsProg :: String -> UntypedPassState -> FutharkM (Prog Kernels.Kernels) kernelsProg _ (Kernels prog) =   return prog kernelsProg name rep =   externalErrorS $-  "Pass " ++ name ++" expects Kernels representation, but got " ++ representation rep+    "Pass " ++ name ++ " expects Kernels representation, but got " ++ representation rep -typedPassOption :: Checkable tolore =>-                   (String -> UntypedPassState -> FutharkM (Prog fromlore))-                -> (Prog tolore -> UntypedPassState)-                -> Pass fromlore tolore-                -> String-                -> FutharkOption+typedPassOption ::+  Checkable tolore =>+  (String -> UntypedPassState -> FutharkM (Prog fromlore)) ->+  (Prog tolore -> UntypedPassState) ->+  Pass fromlore tolore ->+  String ->+  FutharkOption typedPassOption getProg putProg pass short =   passOption (passDescription pass) (UntypedPass perform) short long-  where perform s config = do-          prog <- getProg (passName pass) s-          putProg <$> runPipeline (onePass pass) config prog+  where+    perform s config = do+      prog <- getProg (passName pass) s+      putProg <$> runPipeline (onePass pass) config prog -        long = [passLongOption pass]+    long = [passLongOption pass]  soacsPassOption :: Pass SOACS.SOACS SOACS.SOACS -> String -> FutharkOption soacsPassOption =   typedPassOption soacsProg SOACS -kernelsPassOption :: Pass Kernels.Kernels Kernels.Kernels-                  -> String -> FutharkOption+kernelsPassOption ::+  Pass Kernels.Kernels Kernels.Kernels ->+  String ->+  FutharkOption kernelsPassOption =   typedPassOption kernelsProg Kernels -kernelsMemPassOption :: Pass KernelsMem.KernelsMem KernelsMem.KernelsMem-                     -> String -> FutharkOption+kernelsMemPassOption ::+  Pass KernelsMem.KernelsMem KernelsMem.KernelsMem ->+  String ->+  FutharkOption kernelsMemPassOption =   typedPassOption kernelsMemProg KernelsMem  simplifyOption :: String -> FutharkOption simplifyOption short =   passOption (passDescription pass) (UntypedPass perform) short long-  where perform (SOACS prog) config =-          SOACS <$> runPipeline (onePass simplifySOACS) config prog-        perform (Kernels prog) config =-          Kernels <$> runPipeline (onePass simplifyKernels) config prog-        perform (Seq prog) config =-          Seq <$> runPipeline (onePass simplifySeq) config prog-        perform (SeqMem prog) config =-          SeqMem <$> runPipeline (onePass simplifySeqMem) config prog-        perform (KernelsMem prog) config =-          KernelsMem <$> runPipeline (onePass simplifyKernelsMem) config prog+  where+    perform (SOACS prog) config =+      SOACS <$> runPipeline (onePass simplifySOACS) config prog+    perform (Kernels prog) config =+      Kernels <$> runPipeline (onePass simplifyKernels) config prog+    perform (Seq prog) config =+      Seq <$> runPipeline (onePass simplifySeq) config prog+    perform (SeqMem prog) config =+      SeqMem <$> runPipeline (onePass simplifySeqMem) config prog+    perform (KernelsMem prog) config =+      KernelsMem <$> runPipeline (onePass simplifyKernelsMem) config prog -        long = [passLongOption pass]-        pass = simplifySOACS+    long = [passLongOption pass]+    pass = simplifySOACS  allocateOption :: String -> FutharkOption allocateOption short =   passOption (passDescription pass) (UntypedPass perform) short long-  where perform (Kernels prog) config =-          KernelsMem <$>-          runPipeline (onePass Kernels.explicitAllocations) config prog-        perform (Seq prog) config =-          SeqMem <$>-          runPipeline (onePass Seq.explicitAllocations) config prog-        perform s _ =-          externalErrorS $-          "Pass '" ++ passDescription pass ++ "' cannot operate on " ++ representation s+  where+    perform (Kernels prog) config =+      KernelsMem+        <$> runPipeline (onePass Kernels.explicitAllocations) config prog+    perform (Seq prog) config =+      SeqMem+        <$> runPipeline (onePass Seq.explicitAllocations) config prog+    perform s _ =+      externalErrorS $+        "Pass '" ++ passDescription pass ++ "' cannot operate on " ++ representation s -        long = [passLongOption pass]-        pass = Seq.explicitAllocations+    long = [passLongOption pass]+    pass = Seq.explicitAllocations  iplOption :: String -> FutharkOption iplOption short =   passOption (passDescription pass) (UntypedPass perform) short long-  where perform (Kernels prog) config =-          Kernels <$>-          runPipeline (onePass inPlaceLoweringKernels) config prog-        perform (Seq prog) config =-          Seq <$>-          runPipeline (onePass inPlaceLoweringSeq) config prog-        perform s _ =-          externalErrorS $-          "Pass '" ++ passDescription pass ++ "' cannot operate on " ++ representation s+  where+    perform (Kernels prog) config =+      Kernels+        <$> runPipeline (onePass inPlaceLoweringKernels) config prog+    perform (Seq prog) config =+      Seq+        <$> runPipeline (onePass inPlaceLoweringSeq) config prog+    perform s _ =+      externalErrorS $+        "Pass '" ++ passDescription pass ++ "' cannot operate on " ++ representation s -        long = [passLongOption pass]-        pass = inPlaceLoweringSeq+    long = [passLongOption pass]+    pass = inPlaceLoweringSeq  cseOption :: String -> FutharkOption cseOption short =   passOption (passDescription pass) (UntypedPass perform) short long-  where perform (SOACS prog) config =-          SOACS <$> runPipeline (onePass $ performCSE True) config prog-        perform (Kernels prog) config =-          Kernels <$> runPipeline (onePass $ performCSE True) config prog-        perform (Seq prog) config =-          Seq <$> runPipeline (onePass $ performCSE True) config prog-        perform (SeqMem prog) config =-          SeqMem <$> runPipeline (onePass $ performCSE False) config prog-        perform (KernelsMem prog) config =-          KernelsMem <$> runPipeline (onePass $ performCSE False) config prog+  where+    perform (SOACS prog) config =+      SOACS <$> runPipeline (onePass $ performCSE True) config prog+    perform (Kernels prog) config =+      Kernels <$> runPipeline (onePass $ performCSE True) config prog+    perform (Seq prog) config =+      Seq <$> runPipeline (onePass $ performCSE True) config prog+    perform (SeqMem prog) config =+      SeqMem <$> runPipeline (onePass $ performCSE False) config prog+    perform (KernelsMem prog) config =+      KernelsMem <$> runPipeline (onePass $ performCSE False) config prog -        long = [passLongOption pass]-        pass = performCSE True :: Pass SOACS.SOACS SOACS.SOACS+    long = [passLongOption pass]+    pass = performCSE True :: Pass SOACS.SOACS SOACS.SOACS -pipelineOption :: (UntypedPassState -> Maybe (Prog fromlore))-               -> String-               -> (Prog tolore -> UntypedPassState)-               -> String-               -> Pipeline fromlore tolore-               -> String-               -> [String]-               -> FutharkOption+pipelineOption ::+  (UntypedPassState -> Maybe (Prog fromlore)) ->+  String ->+  (Prog tolore -> UntypedPassState) ->+  String ->+  Pipeline fromlore tolore ->+  String ->+  [String] ->+  FutharkOption pipelineOption getprog repdesc repf desc pipeline =   passOption desc $ UntypedPass pipelinePass-  where pipelinePass rep config =-          case getprog rep of-            Just prog ->-              repf <$> runPipeline pipeline config prog-            Nothing   ->-              externalErrorS $ "Expected " ++ repdesc ++ " representation, but got " ++-              representation rep+  where+    pipelinePass rep config =+      case getprog rep of+        Just prog ->+          repf <$> runPipeline pipeline config prog+        Nothing ->+          externalErrorS $+            "Expected " ++ repdesc ++ " representation, but got "+              ++ representation rep -soacsPipelineOption :: String -> Pipeline SOACS.SOACS SOACS.SOACS -> String -> [String]-                    -> FutharkOption+soacsPipelineOption ::+  String ->+  Pipeline SOACS.SOACS SOACS.SOACS ->+  String ->+  [String] ->+  FutharkOption soacsPipelineOption = pipelineOption getSOACSProg "SOACS" SOACS  commandLineOptions :: [FutharkOption] commandLineOptions =-  [ Option "v" ["verbose"]-    (OptArg (Right . changeFutharkConfig . incVerbosity) "FILE")-    "Print verbose output on standard error; wrong program to FILE."-  , Option [] ["Werror"]-    (NoArg $ Right $ changeFutharkConfig $ \opts -> opts { futharkWerror = True })-    "Treat warnings as errors."--  , Option "t" ["type-check"]-    (NoArg $ Right $ \opts ->-        opts { futharkPipeline = TypeCheck })-    "Print on standard output the type-checked program."--  , Option [] ["pretty-print"]-    (NoArg $ Right $ \opts ->-        opts { futharkPipeline = PrettyPrint })-    "Parse and pretty-print the AST of the given program."--  , Option [] ["compile-imperative"]-    (NoArg $ Right $ \opts ->-       opts { futharkAction = SeqMemAction $ const impCodeGenAction })-    "Translate program into the imperative IL and write it on standard output."-  , Option [] ["compile-imperative-kernels"]-    (NoArg $ Right $ \opts ->-       opts { futharkAction = KernelsMemAction $ const kernelImpCodeGenAction })-    "Translate program into the imperative IL with kernels and write it on standard output."-  , Option [] ["compile-opencl"]-    (NoArg $ Right $ \opts ->-       opts { futharkAction = KernelsMemAction $ compileOpenCLAction newFutharkConfig ToExecutable })-    "Compile the program using the OpenCL backend."-  , Option [] ["compile-c"]-    (NoArg $ Right $ \opts ->-       opts { futharkAction = SeqMemAction $ compileCAction newFutharkConfig ToExecutable })-    "Compile the program using the C backend."-  , Option "p" ["print"]-    (NoArg $ Right $ \opts ->-        opts { futharkAction = PolyAction $ AllActions printAction printAction printAction printAction printAction })-    "Prettyprint the resulting internal representation on standard output (default action)."-  , Option "m" ["metrics"]-    (NoArg $ Right $ \opts ->-        opts { futharkAction = PolyAction $ AllActions metricsAction metricsAction metricsAction metricsAction metricsAction })-    "Print AST metrics of the resulting internal representation on standard output."-  , Option [] ["defunctorise"]-    (NoArg $ Right $ \opts -> opts { futharkPipeline = Defunctorise })-    "Partially evaluate all module constructs and print the residual program."-  , Option [] ["monomorphise"]-    (NoArg $ Right $ \opts -> opts { futharkPipeline = Monomorphise })-    "Monomorphise the program."-  , Option [] ["defunctionalise"]-    (NoArg $ Right $ \opts -> opts { futharkPipeline = Defunctionalise })-    "Defunctionalise the program."-  , Option [] ["ast"]-    (NoArg $ Right $ \opts -> opts { futharkPrintAST = True })-    "Output ASTs instead of prettyprinted programs."-  , Option [] ["safe"]-    (NoArg $ Right $ changeFutharkConfig $ \opts -> opts { futharkSafe = True })-    "Ignore 'unsafe'."-  , typedPassOption soacsProg Seq firstOrderTransform "f"-  , soacsPassOption fuseSOACs "o"-  , soacsPassOption inlineFunctions []-  , kernelsPassOption babysitKernels []-  , kernelsPassOption tileLoops []-  , kernelsPassOption unstream []-  , kernelsPassOption sink []-  , typedPassOption soacsProg Kernels extractKernels []--  , iplOption []-  , allocateOption "a"--  , kernelsMemPassOption doubleBuffer []-  , kernelsMemPassOption expandAllocations []--  , cseOption []-  , simplifyOption "e"--  , soacsPipelineOption "Run the default optimised pipeline"-    standardPipeline "s" ["standard"]-  , pipelineOption getSOACSProg "Kernels" Kernels-    "Run the default optimised kernels pipeline"-    kernelsPipeline [] ["kernels"]-  , pipelineOption getSOACSProg "KernelsMem" KernelsMem-    "Run the full GPU compilation pipeline"-    gpuPipeline [] ["gpu"]-  , pipelineOption getSOACSProg "KernelsMem" SeqMem-    "Run the sequential CPU compilation pipeline"-    sequentialCpuPipeline [] ["cpu"]+  [ Option+      "v"+      ["verbose"]+      (OptArg (Right . changeFutharkConfig . incVerbosity) "FILE")+      "Print verbose output on standard error; wrong program to FILE.",+    Option+      []+      ["Werror"]+      (NoArg $ Right $ changeFutharkConfig $ \opts -> opts {futharkWerror = True})+      "Treat warnings as errors.",+    Option+      "t"+      ["type-check"]+      ( NoArg $+          Right $ \opts ->+            opts {futharkPipeline = TypeCheck}+      )+      "Print on standard output the type-checked program.",+    Option+      []+      ["pretty-print"]+      ( NoArg $+          Right $ \opts ->+            opts {futharkPipeline = PrettyPrint}+      )+      "Parse and pretty-print the AST of the given program.",+    Option+      []+      ["compile-imperative"]+      ( NoArg $+          Right $ \opts ->+            opts {futharkAction = SeqMemAction $ const impCodeGenAction}+      )+      "Translate program into the imperative IL and write it on standard output.",+    Option+      []+      ["compile-imperative-kernels"]+      ( NoArg $+          Right $ \opts ->+            opts {futharkAction = KernelsMemAction $ const kernelImpCodeGenAction}+      )+      "Translate program into the imperative IL with kernels and write it on standard output.",+    Option+      []+      ["compile-opencl"]+      ( NoArg $+          Right $ \opts ->+            opts {futharkAction = KernelsMemAction $ compileOpenCLAction newFutharkConfig ToExecutable}+      )+      "Compile the program using the OpenCL backend.",+    Option+      []+      ["compile-c"]+      ( NoArg $+          Right $ \opts ->+            opts {futharkAction = SeqMemAction $ compileCAction newFutharkConfig ToExecutable}+      )+      "Compile the program using the C backend.",+    Option+      "p"+      ["print"]+      (NoArg $ Right $ \opts -> opts {futharkAction = PolyAction printAction})+      "Prettyprint the resulting internal representation on standard output (default action).",+    Option+      "m"+      ["metrics"]+      (NoArg $ Right $ \opts -> opts {futharkAction = PolyAction metricsAction})+      "Print AST metrics of the resulting internal representation on standard output.",+    Option+      []+      ["sexp"]+      (NoArg $ Right $ \opts -> opts {futharkAction = PolyAction sexpAction})+      "Print the resulting IR as S-expressions to standard output.",+    Option+      []+      ["defunctorise"]+      (NoArg $ Right $ \opts -> opts {futharkPipeline = Defunctorise})+      "Partially evaluate all module constructs and print the residual program.",+    Option+      []+      ["monomorphise"]+      (NoArg $ Right $ \opts -> opts {futharkPipeline = Monomorphise})+      "Monomorphise the program.",+    Option+      []+      ["defunctionalise"]+      (NoArg $ Right $ \opts -> opts {futharkPipeline = Defunctionalise})+      "Defunctionalise the program.",+    Option+      []+      ["ast"]+      (NoArg $ Right $ \opts -> opts {futharkPrintAST = True})+      "Output ASTs instead of prettyprinted programs.",+    Option+      []+      ["safe"]+      (NoArg $ Right $ changeFutharkConfig $ \opts -> opts {futharkSafe = True})+      "Ignore 'unsafe'.",+    typedPassOption soacsProg Seq firstOrderTransform "f",+    soacsPassOption fuseSOACs "o",+    soacsPassOption inlineFunctions [],+    kernelsPassOption babysitKernels [],+    kernelsPassOption tileLoops [],+    kernelsPassOption unstream [],+    kernelsPassOption sink [],+    typedPassOption soacsProg Kernels extractKernels [],+    iplOption [],+    allocateOption "a",+    kernelsMemPassOption doubleBuffer [],+    kernelsMemPassOption expandAllocations [],+    cseOption [],+    simplifyOption "e",+    soacsPipelineOption+      "Run the default optimised pipeline"+      standardPipeline+      "s"+      ["standard"],+    pipelineOption+      getSOACSProg+      "Kernels"+      Kernels+      "Run the default optimised kernels pipeline"+      kernelsPipeline+      []+      ["kernels"],+    pipelineOption+      getSOACSProg+      "KernelsMem"+      KernelsMem+      "Run the full GPU compilation pipeline"+      gpuPipeline+      []+      ["gpu"],+    pipelineOption+      getSOACSProg+      "KernelsMem"+      SeqMem+      "Run the sequential CPU compilation pipeline"+      sequentialCpuPipeline+      []+      ["cpu"]   ]  incVerbosity :: Maybe FilePath -> FutharkConfig -> FutharkConfig incVerbosity file cfg =-  cfg { futharkVerbose = (v, file `mplus` snd (futharkVerbose cfg)) }-  where v = case fst $ futharkVerbose cfg of-              NotVerbose -> Verbose-              Verbose -> VeryVerbose-              VeryVerbose -> VeryVerbose+  cfg {futharkVerbose = (v, file `mplus` snd (futharkVerbose cfg))}+  where+    v = case fst $ futharkVerbose cfg of+      NotVerbose -> Verbose+      Verbose -> VeryVerbose+      VeryVerbose -> VeryVerbose  -- | Entry point.  Non-interactive, except when reading interpreter -- input from standard input. main :: String -> [String] -> IO () main = mainWithOptions newConfig commandLineOptions "options... program" compile-  where compile [file] config =-          Just $ do-            res <- runFutharkM (m file config) $-                   fst $ futharkVerbose $ futharkConfig config-            case res of-              Left err -> do-                dumpError (futharkConfig config) err-                exitWith $ ExitFailure 2-              Right () -> return ()-        compile _      _      =-          Nothing-        m file config = do-          let p :: (Show a, PP.Pretty a) => [a] -> IO ()-              p = mapM_ putStrLn .-                  intersperse "" .-                  map (if futharkPrintAST config then show else pretty)+  where+    compile [file] config =+      Just $ do+        res <-+          runFutharkM (m file config) $+            fst $ futharkVerbose $ futharkConfig config+        case res of+          Left err -> do+            dumpError (futharkConfig config) err+            exitWith $ ExitFailure 2+          Right () -> return ()+    compile _ _ =+      Nothing+    m file config = do+      let p :: (Show a, PP.Pretty a) => [a] -> IO ()+          p =+            mapM_ putStrLn+              . intersperse ""+              . map (if futharkPrintAST config then show else pretty) -          case futharkPipeline config of-            PrettyPrint -> liftIO $ do-              maybe_prog <- parseFuthark file <$> T.readFile file-              case maybe_prog of-                Left err  -> fail $ show err-                Right prog | futharkPrintAST config -> print prog-                           | otherwise -> putStrLn $ pretty prog-            TypeCheck -> do-              (_, imports, _) <- readProgram file-              liftIO $ forM_ (map snd imports) $ \fm ->-                putStrLn $ if futharkPrintAST config-                           then show $ fileProg fm-                           else pretty $ fileProg fm-            Defunctorise -> do-              (_, imports, src) <- readProgram file-              liftIO $ p $ evalState (Defunctorise.transformProg imports) src-            Monomorphise -> do-              (_, imports, src) <- readProgram file-              liftIO $ p $-                flip evalState src $+      case futharkPipeline config of+        PrettyPrint -> liftIO $ do+          maybe_prog <- parseFuthark file <$> T.readFile file+          case maybe_prog of+            Left err -> fail $ show err+            Right prog+              | futharkPrintAST config -> print prog+              | otherwise -> putStrLn $ pretty prog+        TypeCheck -> do+          (_, imports, _) <- readProgram file+          liftIO $+            forM_ (map snd imports) $ \fm ->+              putStrLn $+                if futharkPrintAST config+                  then show $ fileProg fm+                  else pretty $ fileProg fm+        Defunctorise -> do+          (_, imports, src) <- readProgram file+          liftIO $ p $ evalState (Defunctorise.transformProg imports) src+        Monomorphise -> do+          (_, imports, src) <- readProgram file+          liftIO $+            p $+              flip evalState src $                 Defunctorise.transformProg imports-                >>= Monomorphise.transformProg-            Defunctionalise -> do-              (_, imports, src) <- readProgram file-              liftIO $ p $-                flip evalState src $+                  >>= Monomorphise.transformProg+        Defunctionalise -> do+          (_, imports, src) <- readProgram file+          liftIO $+            p $+              flip evalState src $                 Defunctorise.transformProg imports-                >>= Monomorphise.transformProg-                >>= Defunctionalise.transformProg-            Pipeline{} -> do+                  >>= Monomorphise.transformProg+                  >>= Defunctionalise.transformProg+        Pipeline {} ->+          case splitExtensions file of+            (base, ".fut") -> do               prog <- runPipelineOnProgram (futharkConfig config) id file-              runPolyPasses config (file `replaceExtension` "") (SOACS prog)+              runPolyPasses config base (SOACS prog)+            (base, ".sexp") -> do+              input <- liftIO $ ByteString.readFile file+              prog <- case Sexp.decode @(Prog SOACS.SOACS) input of+                Right prog' -> return $ SOACS prog'+                Left _ ->+                  case Sexp.decode @(Prog Kernels.Kernels) input of+                    Right prog' -> return $ Kernels prog'+                    Left _ ->+                      case Sexp.decode @(Prog Seq.Seq) input of+                        Right prog' -> return $ Seq prog'+                        Left _ ->+                          case Sexp.decode @(Prog KernelsMem.KernelsMem) input of+                            Right prog' -> return $ KernelsMem prog'+                            Left _ ->+                              case Sexp.decode @(Prog SeqMem.SeqMem) input of+                                Right prog' -> return $ SeqMem prog'+                                Left e -> externalErrorS $ "Couldn't parse sexp input: " ++ show e+              runPolyPasses config base prog+            (_, ext) ->+              externalErrorS $ unwords ["Unsupported extension", show ext, ". Supported extensions: sexp, fut"]  runPolyPasses :: Config -> FilePath -> UntypedPassState -> FutharkM () runPolyPasses config base initial_prog = do-    end_prog <- foldM (runPolyPass pipeline_config) initial_prog-                (getFutharkPipeline config)-    logMsg $ "Running action " ++ untypedActionName (futharkAction config)-    case (end_prog, futharkAction config) of-      (SOACS prog, SOACSAction action) ->-        actionProcedure action prog-      (Kernels prog, KernelsAction action) ->-        actionProcedure action prog-      (SeqMem prog, SeqMemAction action) ->-        actionProcedure (action base) prog-      (KernelsMem prog, KernelsMemAction action) ->-        actionProcedure (action base) prog--      (SOACS soacs_prog, PolyAction acs) ->-        actionProcedure (actionSOACS acs) soacs_prog-      (Kernels kernels_prog, PolyAction acs) ->-        actionProcedure (actionKernels acs) kernels_prog-      (Seq seq_prog, PolyAction acs) ->-        actionProcedure (actionSeq acs) seq_prog-      (KernelsMem mem_prog, PolyAction acs) ->-        actionProcedure (actionKernelsMem acs) mem_prog-      (SeqMem mem_prog, PolyAction acs) ->-        actionProcedure (actionSeqMem acs) mem_prog--      (_, action) ->-        externalErrorS $ "Action " <>-        untypedActionName action <>-        " expects " ++ representation action ++ " representation, but got " ++-        representation end_prog ++ "."-    logMsg ("Done." :: String)-  where pipeline_config =-          PipelineConfig { pipelineVerbose = fst (futharkVerbose $ futharkConfig config) > NotVerbose-                         , pipelineValidate = True-                         }+  end_prog <-+    foldM+      (runPolyPass pipeline_config)+      initial_prog+      (getFutharkPipeline config)+  logMsg $ "Running action " ++ untypedActionName (futharkAction config)+  case (end_prog, futharkAction config) of+    (SOACS prog, SOACSAction action) ->+      actionProcedure action prog+    (Kernels prog, KernelsAction action) ->+      actionProcedure action prog+    (SeqMem prog, SeqMemAction action) ->+      actionProcedure (action base) prog+    (KernelsMem prog, KernelsMemAction action) ->+      actionProcedure (action base) prog+    (SOACS soacs_prog, PolyAction acs) ->+      actionProcedure acs soacs_prog+    (Kernels kernels_prog, PolyAction acs) ->+      actionProcedure acs kernels_prog+    (Seq seq_prog, PolyAction acs) ->+      actionProcedure acs seq_prog+    (KernelsMem mem_prog, PolyAction acs) ->+      actionProcedure acs mem_prog+    (SeqMem mem_prog, PolyAction acs) ->+      actionProcedure acs mem_prog+    (_, action) ->+      externalErrorS $+        "Action "+          <> untypedActionName action+          <> " expects "+          ++ representation action+          ++ " representation, but got "+          ++ representation end_prog+          ++ "."+  logMsg ("Done." :: String)+  where+    pipeline_config =+      PipelineConfig+        { pipelineVerbose = fst (futharkVerbose $ futharkConfig config) > NotVerbose,+          pipelineValidate = True+        } -runPolyPass :: PipelineConfig-            -> UntypedPassState -> UntypedPass -> FutharkM UntypedPassState+runPolyPass ::+  PipelineConfig ->+  UntypedPassState ->+  UntypedPass ->+  FutharkM UntypedPassState runPolyPass pipeline_config s (UntypedPass f) =   f s pipeline_config
src/Futhark/CLI/Doc.hs view
@@ -1,105 +1,120 @@-{-# LANGUAGE TupleSections #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}+ -- | @futhark doc@ module Futhark.CLI.Doc (main) where  import Control.Monad.State import Data.FileEmbed import Data.List (nubBy)-import System.FilePath-import System.Directory (createDirectoryIfMissing)-import System.Console.GetOpt-import System.IO-import System.Exit import qualified Data.Text.Lazy as T import qualified Data.Text.Lazy.IO as T-import Text.Blaze.Html.Renderer.Text-+import Futhark.Compiler (Imports, dumpError, fileProg, newFutharkConfig, readLibrary) import Futhark.Doc.Generator-import Futhark.Compiler (readLibrary, dumpError, newFutharkConfig, Imports, fileProg)-import Futhark.Pipeline (runFutharkM, FutharkM, Verbosity(..))-import Language.Futhark.Syntax (progDoc, DocComment(..))-import Futhark.Util.Options+import Futhark.Pipeline (FutharkM, Verbosity (..), runFutharkM) import Futhark.Util (directoryContents, trim)+import Futhark.Util.Options+import Language.Futhark.Syntax (DocComment (..), progDoc)+import System.Console.GetOpt+import System.Directory (createDirectoryIfMissing)+import System.Exit+import System.FilePath+import System.IO+import Text.Blaze.Html.Renderer.Text  -- | Run @futhark doc@. main :: String -> [String] -> IO () main = mainWithOptions initialDocConfig commandLineOptions "options... -o outdir programs..." f-  where f [dir] config = Just $ do-          res <- runFutharkM (m config dir) Verbose-          case res of-            Left err -> liftIO $ do-              dumpError newFutharkConfig err-              exitWith $ ExitFailure 2-            Right () ->-              return ()-        f _ _ = Nothing+  where+    f [dir] config = Just $ do+      res <- runFutharkM (m config dir) Verbose+      case res of+        Left err -> liftIO $ do+          dumpError newFutharkConfig err+          exitWith $ ExitFailure 2+        Right () ->+          return ()+    f _ _ = Nothing -        m :: DocConfig -> FilePath -> FutharkM ()-        m config dir =-          case docOutput config of-            Nothing -> liftIO $ do-              hPutStrLn stderr "Must specify output directory with -o."-              exitWith $ ExitFailure 1-            Just outdir -> do-              files <- liftIO $ futFiles dir-              when (docVerbose config) $ liftIO $ do-                mapM_ (hPutStrLn stderr . ("Found source file "<>)) files-                hPutStrLn stderr "Reading files..."-              (_w, imports, _vns) <- readLibrary files-              liftIO $ printDecs config outdir files $ nubBy sameImport imports+    m :: DocConfig -> FilePath -> FutharkM ()+    m config dir =+      case docOutput config of+        Nothing -> liftIO $ do+          hPutStrLn stderr "Must specify output directory with -o."+          exitWith $ ExitFailure 1+        Just outdir -> do+          files <- liftIO $ futFiles dir+          when (docVerbose config) $+            liftIO $ do+              mapM_ (hPutStrLn stderr . ("Found source file " <>)) files+              hPutStrLn stderr "Reading files..."+          (_w, imports, _vns) <- readLibrary files+          liftIO $ printDecs config outdir files $ nubBy sameImport imports -        sameImport (x, _) (y, _) = x == y+    sameImport (x, _) (y, _) = x == y  futFiles :: FilePath -> IO [FilePath] futFiles dir = filter isFut <$> directoryContents dir-  where isFut = (==".fut") . takeExtension+  where+    isFut = (== ".fut") . takeExtension  printDecs :: DocConfig -> FilePath -> [FilePath] -> Imports -> IO () printDecs cfg dir files imports = do   let direct_imports = map (normalise . dropExtension) files-      (file_htmls, _warnings) = renderFiles direct_imports $-                                filter (not . ignored) imports+      (file_htmls, _warnings) =+        renderFiles direct_imports $+          filter (not . ignored) imports   mapM_ (write . fmap renderHtml) file_htmls   write ("style.css", cssFile)--  where write :: (String, T.Text) -> IO ()-        write (name, content) = do let file = dir </> makeRelative "/" name-                                   when (docVerbose cfg) $-                                     hPutStrLn stderr $ "Writing " <> file-                                   createDirectoryIfMissing True $ takeDirectory file-                                   T.writeFile file content+  where+    write :: (String, T.Text) -> IO ()+    write (name, content) = do+      let file = dir </> makeRelative "/" name+      when (docVerbose cfg) $+        hPutStrLn stderr $ "Writing " <> file+      createDirectoryIfMissing True $ takeDirectory file+      T.writeFile file content -        -- Some files are not worth documenting; typically because-        -- they contain tests.  The current crude mechanism is to-        -- recognise them by a file comment containing "ignore".-        ignored (_, fm) =-          case progDoc (fileProg fm) of-            Just (DocComment s _) -> trim s == "ignore"-            _                     -> False+    -- Some files are not worth documenting; typically because+    -- they contain tests.  The current crude mechanism is to+    -- recognise them by a file comment containing "ignore".+    ignored (_, fm) =+      case progDoc (fileProg fm) of+        Just (DocComment s _) -> trim s == "ignore"+        _ -> False  cssFile :: T.Text cssFile = $(embedStringFile "rts/futhark-doc/style.css") -data DocConfig = DocConfig { docOutput :: Maybe FilePath-                           , docVerbose :: Bool-                           }+data DocConfig = DocConfig+  { docOutput :: Maybe FilePath,+    docVerbose :: Bool+  }  initialDocConfig :: DocConfig-initialDocConfig = DocConfig { docOutput = Nothing-                             , docVerbose = False-                             }+initialDocConfig =+  DocConfig+    { docOutput = Nothing,+      docVerbose = False+    }  type DocOption = OptDescr (Either (IO ()) (DocConfig -> DocConfig))  commandLineOptions :: [DocOption]-commandLineOptions = [ Option "o" ["output-directory"]-                       (ReqArg (\dirname -> Right $ \config -> config { docOutput = Just dirname })-                       "DIR")-                       "Directory in which to put generated documentation."-                     , Option "v" ["verbose"]-                       (NoArg $ Right $ \config -> config { docVerbose = True })-                       "Print status messages on stderr."-                     ]+commandLineOptions =+  [ Option+      "o"+      ["output-directory"]+      ( ReqArg+          (\dirname -> Right $ \config -> config {docOutput = Just dirname})+          "DIR"+      )+      "Directory in which to put generated documentation.",+    Option+      "v"+      ["verbose"]+      (NoArg $ Right $ \config -> config {docVerbose = True})+      "Print status messages on stderr."+  ]
src/Futhark/CLI/Misc.hs view
@@ -1,22 +1,22 @@ {-# LANGUAGE FlexibleContexts #-}+ -- | Various small subcommands that are too simple to deserve their own file. module Futhark.CLI.Misc-  ( mainImports-  , mainDataget+  ( mainImports,+    mainDataget,   ) where +import Control.Monad.State import qualified Data.ByteString.Lazy as BS-import Data.List (isPrefixOf, isInfixOf, nubBy) import Data.Function (on)-import Control.Monad.State-import System.FilePath-import System.IO-import System.Exit-+import Data.List (isInfixOf, isPrefixOf, nubBy) import Futhark.Compiler-import Futhark.Util.Options import Futhark.Test+import Futhark.Util.Options+import System.Exit+import System.FilePath+import System.IO  -- | @futhark imports@ mainImports :: String -> [String] -> IO ()@@ -24,9 +24,12 @@   case args of     [file] -> Just $ do       (_, prog_imports, _) <- readProgramOrDie file-      liftIO $ putStr $ unlines $ map (++ ".fut")-        $ filter (\f -> not ("prelude/" `isPrefixOf` f))-        $ map fst prog_imports+      liftIO $+        putStr $+          unlines $+            map (++ ".fut") $+              filter (\f -> not ("prelude/" `isPrefixOf` f)) $+                map fst prog_imports     _ -> Nothing  -- | @futhark dataget@@@ -35,27 +38,28 @@   case args of     [file, dataset] -> Just $ dataget file dataset     _ -> Nothing-  where dataget prog dataset = do-          let dir = takeDirectory prog--          runs <- testSpecRuns <$> testSpecFromFileOrDie prog--          let exact = filter ((dataset==) . runDescription) runs-              infixes = filter ((dataset `isInfixOf`) . runDescription) runs+  where+    dataget prog dataset = do+      let dir = takeDirectory prog -          case nubBy ((==) `on` runDescription) $-               if null exact then infixes else exact of-            [x] -> BS.putStr =<< getValuesBS dir (runInput x)+      runs <- testSpecRuns <$> testSpecFromFileOrDie prog -            [] -> do hPutStr stderr $ "No dataset '" ++ dataset ++ "'.\n"-                     hPutStr stderr "Available datasets:\n"-                     mapM_ (hPutStrLn stderr . ("  "++) . runDescription) runs-                     exitFailure+      let exact = filter ((dataset ==) . runDescription) runs+          infixes = filter ((dataset `isInfixOf`) . runDescription) runs -            runs' -> do hPutStr stderr $ "Dataset '" ++ dataset ++ "' ambiguous:\n"-                        mapM_ (hPutStrLn stderr . ("  "++) . runDescription) runs'-                        exitFailure+      case nubBy ((==) `on` runDescription) $+        if null exact then infixes else exact of+        [x] -> BS.putStr =<< getValuesBS dir (runInput x)+        [] -> do+          hPutStr stderr $ "No dataset '" ++ dataset ++ "'.\n"+          hPutStr stderr "Available datasets:\n"+          mapM_ (hPutStrLn stderr . ("  " ++) . runDescription) runs+          exitFailure+        runs' -> do+          hPutStr stderr $ "Dataset '" ++ dataset ++ "' ambiguous:\n"+          mapM_ (hPutStrLn stderr . ("  " ++) . runDescription) runs'+          exitFailure -        testSpecRuns = testActionRuns . testAction-        testActionRuns CompileTimeFailure{} = []-        testActionRuns (RunCases ios _ _) = concatMap iosTestRuns ios+    testSpecRuns = testActionRuns . testAction+    testActionRuns CompileTimeFailure {} = []+    testActionRuns (RunCases ios _ _) = concatMap iosTestRuns ios
src/Futhark/CLI/OpenCL.hs view
@@ -1,14 +1,19 @@ {-# LANGUAGE FlexibleContexts #-}+ -- | @futhark opencl@ module Futhark.CLI.OpenCL (main) where  import Futhark.Actions (compileOpenCLAction)-import Futhark.Passes (gpuPipeline) import Futhark.Compiler.CLI+import Futhark.Passes (gpuPipeline)  -- | Run @futhark opencl@ main :: String -> [String] -> IO ()-main = compilerMain () []-       "Compile OpenCL" "Generate OpenCL/C code from optimised Futhark program."-       gpuPipeline $ \fcfg () mode outpath prog ->-  actionProcedure (compileOpenCLAction fcfg mode outpath) prog+main = compilerMain+  ()+  []+  "Compile OpenCL"+  "Generate OpenCL/C code from optimised Futhark program."+  gpuPipeline+  $ \fcfg () mode outpath prog ->+    actionProcedure (compileOpenCLAction fcfg mode outpath) prog
src/Futhark/CLI/Pkg.hs view
@@ -1,37 +1,35 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+ -- | @futhark pkg@ module Futhark.CLI.Pkg (main) where +import qualified Codec.Archive.Zip as Zip import Control.Monad.IO.Class-import Control.Monad.State import Control.Monad.Reader-import Data.Maybe+import Control.Monad.State+import qualified Data.ByteString.Lazy as LBS+import Data.List (intercalate, isPrefixOf) import qualified Data.Map as M+import Data.Maybe+import Data.Monoid import qualified Data.Text as T import qualified Data.Text.IO as T-import qualified Data.ByteString.Lazy as LBS-import Data.List (isPrefixOf, intercalate)-import Data.Monoid+import Futhark.Pkg.Info+import Futhark.Pkg.Solve+import Futhark.Pkg.Types+import Futhark.Util (directoryContents, maxinum)+import Futhark.Util.Log+import Futhark.Util.Options+import System.Console.GetOpt import System.Directory-import System.FilePath-import qualified System.FilePath.Posix as Posix import System.Environment import System.Exit+import System.FilePath+import qualified System.FilePath.Posix as Posix import System.IO-import System.Console.GetOpt--import qualified Codec.Archive.Zip as Zip- import Prelude -import Futhark.Util.Options-import Futhark.Pkg.Types-import Futhark.Pkg.Info-import Futhark.Pkg.Solve-import Futhark.Util (directoryContents, maxinum)-import Futhark.Util.Log- --- Installing packages  installInDir :: BuildList -> FilePath -> PkgM ()@@ -39,23 +37,25 @@   let putEntry from_dir pdir entry         -- The archive may contain all kinds of other stuff that we don't want.         | not (isInPkgDir from_dir $ Zip.eRelativePath entry)-          || hasTrailingPathSeparator (Zip.eRelativePath entry) = return Nothing+            || hasTrailingPathSeparator (Zip.eRelativePath entry) =+          return Nothing         | otherwise = do-        -- Since we are writing to paths indicated in a zipfile we-        -- downloaded from the wild Internet, we are going to be a-        -- little bit paranoid.  Specifically, we want to avoid-        -- writing outside of the 'lib/' directory.  We do this by-        -- bailing out if the path contains any '..' components.  We-        -- have to use System.FilePath.Posix, because the zip library-        -- claims to encode filepaths with '/' directory seperators no-        -- matter the host OS.-        when (".." `elem` Posix.splitPath (Zip.eRelativePath entry)) $-          fail $ "Zip archive for " <> pdir <> " contains suspicious path: " <>-          Zip.eRelativePath entry-        let f = pdir </> makeRelative from_dir (Zip.eRelativePath entry)-        createDirectoryIfMissing True $ takeDirectory f-        LBS.writeFile f $ Zip.fromEntry entry-        return $ Just f+          -- Since we are writing to paths indicated in a zipfile we+          -- downloaded from the wild Internet, we are going to be a+          -- little bit paranoid.  Specifically, we want to avoid+          -- writing outside of the 'lib/' directory.  We do this by+          -- bailing out if the path contains any '..' components.  We+          -- have to use System.FilePath.Posix, because the zip library+          -- claims to encode filepaths with '/' directory seperators no+          -- matter the host OS.+          when (".." `elem` Posix.splitPath (Zip.eRelativePath entry)) $+            fail $+              "Zip archive for " <> pdir <> " contains suspicious path: "+                <> Zip.eRelativePath entry+          let f = pdir </> makeRelative from_dir (Zip.eRelativePath entry)+          createDirectoryIfMissing True $ takeDirectory f+          LBS.writeFile f $ Zip.fromEntry entry+          return $ Just f        isInPkgDir from_dir f =         Posix.splitPath from_dir `isPrefixOf` Posix.splitPath f@@ -67,8 +67,11 @@      -- Compute the directory in the zipball that should contain the     -- package files.-    let noPkgDir = fail $ "futhark.pkg for " ++ T.unpack p ++ "-" ++-                   T.unpack (prettySemVer v) ++ " does not define a package path."+    let noPkgDir =+          fail $+            "futhark.pkg for " ++ T.unpack p ++ "-"+              ++ T.unpack (prettySemVer v)+              ++ " does not define a package path."     from_dir <- maybe noPkgDir (return . (pkgRevZipballDir info <>)) $ pkgDir m      -- The directory in the local file system that will contain the@@ -86,8 +89,10 @@       catMaybes <$> liftIO (mapM (putEntry from_dir pdir) $ Zip.zEntries a)      when (null written) $-      fail $ "Zip archive for package " ++ T.unpack p ++-      " does not contain any files in " ++ from_dir+      fail $+        "Zip archive for package " ++ T.unpack p+          ++ " does not contain any files in "+          ++ from_dir  libDir, libNewDir, libOldDir :: FilePath (libDir, libNewDir, libOldDir) = ("lib", "lib~new", "lib~old")@@ -123,16 +128,18 @@   libdir_exists <- liftIO $ doesDirectoryExist libDir    -- 1-  liftIO $ do removePathForcibly libNewDir-              createDirectoryIfMissing False libNewDir+  liftIO $ do+    removePathForcibly libNewDir+    createDirectoryIfMissing False libNewDir    -- 2   installInDir bl libNewDir    -- 3-  when libdir_exists $ liftIO $ do-    removePathForcibly libOldDir-    renameDirectory libDir libOldDir+  when libdir_exists $+    liftIO $ do+      removePathForcibly libOldDir+      renameDirectory libDir libOldDir    -- 4   liftIO $ renameDirectory libNewDir libDir@@ -158,20 +165,23 @@    case (file_exists, dir_exists) of     (True, _) -> liftIO $ parsePkgManifestFromFile futharkPkg-    (_, True) -> fail $ futharkPkg <>-                 " exists, but it is a directory!  What in Odin's beard..."-    _         -> liftIO $ do T.putStrLn $ T.pack futharkPkg <> " not found - pretending it's empty."-                             return $ newPkgManifest Nothing+    (_, True) ->+      fail $+        futharkPkg+          <> " exists, but it is a directory!  What in Odin's beard..."+    _ -> liftIO $ do+      T.putStrLn $ T.pack futharkPkg <> " not found - pretending it's empty."+      return $ newPkgManifest Nothing  putPkgManifest :: PkgManifest -> PkgM () putPkgManifest = liftIO . T.writeFile futharkPkg . prettyPkgManifest  --- The CLI -newtype PkgConfig = PkgConfig { pkgVerbose :: Bool }+newtype PkgConfig = PkgConfig {pkgVerbose :: Bool}  -- | The monad in which futhark-pkg runs.-newtype PkgM a = PkgM { unPkgM :: ReaderT PkgConfig (StateT (PkgRegistry PkgM) IO) a }+newtype PkgM a = PkgM {unPkgM :: ReaderT PkgConfig (StateT (PkgRegistry PkgM) IO) a}   deriving (Functor, Applicative, MonadIO, MonadReader PkgConfig)  instance Monad PkgM where@@ -196,12 +206,21 @@ runPkgM :: PkgConfig -> PkgM a -> IO a runPkgM cfg (PkgM m) = evalStateT (runReaderT m cfg) mempty -cmdMain :: String -> ([String] -> PkgConfig -> Maybe (IO ()))-        -> String -> [String] -> IO ()+cmdMain ::+  String ->+  ([String] -> PkgConfig -> Maybe (IO ())) ->+  String ->+  [String] ->+  IO () cmdMain = mainWithOptions (PkgConfig False) options-  where options = [ Option "v" ["verbose"]-                    (NoArg $ Right $ \cfg -> cfg { pkgVerbose = True })-                    "Write running diagnostics to stderr."]+  where+    options =+      [ Option+          "v"+          ["verbose"]+          (NoArg $ Right $ \cfg -> cfg {pkgVerbose = True})+          "Write running diagnostics to stderr."+      ]  doFmt :: String -> [String] -> IO () doFmt = mainWithOptions () [] "" $ \args () ->@@ -214,49 +233,56 @@ doCheck :: String -> [String] -> IO () doCheck = cmdMain "check" $ \args cfg ->   case args of-    [] -> Just $ runPkgM cfg $ do-      m <- getPkgManifest-      bl <- solveDeps $ pkgRevDeps m+    [] -> Just $+      runPkgM cfg $ do+        m <- getPkgManifest+        bl <- solveDeps $ pkgRevDeps m -      liftIO $ T.putStrLn "Dependencies chosen:"-      liftIO $ T.putStr $ prettyBuildList bl+        liftIO $ T.putStrLn "Dependencies chosen:"+        liftIO $ T.putStr $ prettyBuildList bl -      case commented $ manifestPkgPath m of-        Nothing -> return ()-        Just p -> do-          let pdir = "lib" </> T.unpack p+        case commented $ manifestPkgPath m of+          Nothing -> return ()+          Just p -> do+            let pdir = "lib" </> T.unpack p -          pdir_exists <- liftIO $ doesDirectoryExist pdir+            pdir_exists <- liftIO $ doesDirectoryExist pdir -          unless pdir_exists $ liftIO $ do-            T.putStrLn $ "Problem: the directory " <> T.pack pdir <> " does not exist."-            exitFailure+            unless pdir_exists $+              liftIO $ do+                T.putStrLn $ "Problem: the directory " <> T.pack pdir <> " does not exist."+                exitFailure -          anything <- liftIO $ any ((==".fut") . takeExtension) <$>-                      directoryContents ("lib" </> T.unpack p)-          unless anything $ liftIO $ do-            T.putStrLn $ "Problem: the directory " <> T.pack pdir <> " does not contain any .fut files."-            exitFailure+            anything <-+              liftIO $+                any ((== ".fut") . takeExtension)+                  <$> directoryContents ("lib" </> T.unpack p)+            unless anything $+              liftIO $ do+                T.putStrLn $ "Problem: the directory " <> T.pack pdir <> " does not contain any .fut files."+                exitFailure     _ -> Nothing  doSync :: String -> [String] -> IO () doSync = cmdMain "" $ \args cfg ->   case args of-    [] -> Just $ runPkgM cfg $ do-      m <- getPkgManifest-      bl <- solveDeps $ pkgRevDeps m-      installBuildList (commented $ manifestPkgPath m) bl+    [] -> Just $+      runPkgM cfg $ do+        m <- getPkgManifest+        bl <- solveDeps $ pkgRevDeps m+        installBuildList (commented $ manifestPkgPath m) bl     _ -> Nothing  doAdd :: String -> [String] -> IO () doAdd = cmdMain "PKGPATH" $ \args cfg ->   case args of     [p, v] | Right v' <- parseVersion $ T.pack v -> Just $ runPkgM cfg $ doAdd' (T.pack p) v'-    [p] -> Just $ runPkgM cfg $-      -- Look up the newest revision of the package.-      doAdd' (T.pack p) =<< lookupNewestRev (T.pack p)+    [p] ->+      Just $+        runPkgM cfg $+          -- Look up the newest revision of the package.+          doAdd' (T.pack p) =<< lookupNewestRev (T.pack p)     _ -> Nothing-   where     doAdd' p v = do       m <- getPkgManifest@@ -272,11 +298,11 @@       -- we add a new one.       p_info <- lookupPackageRev p v       let hash = case (_svMajor v, _svMinor v, _svPatch v) of-                   -- We do not perform hash-pinning for-                   -- (0,0,0)-versions, because these already embed a-                   -- specific revision ID into their version number.-                   (0, 0, 0) -> Nothing-                   _ -> Just $ pkgRevCommit p_info+            -- We do not perform hash-pinning for+            -- (0,0,0)-versions, because these already embed a+            -- specific revision ID into their version number.+            (0, 0, 0) -> Nothing+            _ -> Just $ pkgRevCommit p_info           req = Required p v hash           (m', prev_r) = addRequiredToManifest req m @@ -285,8 +311,13 @@           | requiredPkgRev prev_r' == v ->             liftIO $ T.putStrLn $ "Package already at version " <> prettySemVer v <> "; nothing to do."           | otherwise ->-            liftIO $ T.putStrLn $ "Replaced " <> p <> " " <>-            prettySemVer (requiredPkgRev prev_r') <> " => " <> prettySemVer v <> "."+            liftIO $+              T.putStrLn $+                "Replaced " <> p <> " "+                  <> prettySemVer (requiredPkgRev prev_r')+                  <> " => "+                  <> prettySemVer v+                  <> "."         Nothing ->           liftIO $ T.putStrLn $ "Added new required package " <> p <> " " <> prettySemVer v <> "."       putPkgManifest m'@@ -316,9 +347,10 @@   where     doCreate' p = do       exists <- liftIO $ (||) <$> doesFileExist futharkPkg <*> doesDirectoryExist futharkPkg-      when exists $ liftIO $ do-        T.putStrLn $ T.pack futharkPkg <> " already exists."-        exitFailure+      when exists $+        liftIO $ do+          T.putStrLn $ T.pack futharkPkg <> " already exists."+          exitFailure        liftIO $ createDirectoryIfMissing True $ "lib" </> T.unpack p       liftIO $ T.putStrLn $ "Created directory " <> T.pack ("lib" </> T.unpack p) <> "."@@ -329,33 +361,44 @@ doUpgrade :: String -> [String] -> IO () doUpgrade = cmdMain "" $ \args cfg ->   case args of-    [] -> Just $ runPkgM cfg $ do-      m <- getPkgManifest-      rs <- traverse (mapM (traverse upgrade)) $ manifestRequire m-      putPkgManifest m { manifestRequire = rs }-      if rs == manifestRequire m-        then liftIO $ T.putStrLn "Nothing to upgrade."-        else liftIO $ T.putStrLn "Remember to run 'futhark pkg sync'."+    [] -> Just $+      runPkgM cfg $ do+        m <- getPkgManifest+        rs <- traverse (mapM (traverse upgrade)) $ manifestRequire m+        putPkgManifest m {manifestRequire = rs}+        if rs == manifestRequire m+          then liftIO $ T.putStrLn "Nothing to upgrade."+          else liftIO $ T.putStrLn "Remember to run 'futhark pkg sync'."     _ -> Nothing-  where upgrade req = do-          v <- lookupNewestRev $ requiredPkg req-          h <- pkgRevCommit <$> lookupPackageRev (requiredPkg req) v+  where+    upgrade req = do+      v <- lookupNewestRev $ requiredPkg req+      h <- pkgRevCommit <$> lookupPackageRev (requiredPkg req) v -          when (v /= requiredPkgRev req) $-            liftIO $ T.putStrLn $ "Upgraded " <> requiredPkg req <> " " <>-            prettySemVer (requiredPkgRev req) <> " => " <> prettySemVer v <> "."+      when (v /= requiredPkgRev req) $+        liftIO $+          T.putStrLn $+            "Upgraded " <> requiredPkg req <> " "+              <> prettySemVer (requiredPkgRev req)+              <> " => "+              <> prettySemVer v+              <> "." -          return req { requiredPkgRev = v-                     , requiredHash = Just h }+      return+        req+          { requiredPkgRev = v,+            requiredHash = Just h+          }  doVersions :: String -> [String] -> IO () doVersions = cmdMain "PKGPATH" $ \args cfg ->   case args of     [p] -> Just $ runPkgM cfg $ doVersions' $ T.pack p     _ -> Nothing-  where doVersions' =-          mapM_ (liftIO . T.putStrLn . prettySemVer) . M.keys . pkgVersions-          <=< lookupPackage+  where+    doVersions' =+      mapM_ (liftIO . T.putStrLn . prettySemVer) . M.keys . pkgVersions+        <=< lookupPackage  -- | Run @futhark pkg@. main :: String -> [String] -> IO ()@@ -363,37 +406,49 @@   -- Avoid Git asking for credentials.  We prefer failure.   liftIO $ setEnv "GIT_TERMINAL_PROMPT" "0" -  let commands = [ ("add",-                    (doAdd, "Add another required package to futhark.pkg."))-                 , ("check",-                    (doCheck, "Check that futhark.pkg is satisfiable."))-                 , ("init",-                    (doInit, "Create a new futhark.pkg and a lib/ skeleton."))-                 , ("fmt",-                    (doFmt, "Reformat futhark.pkg."))-                 , ("sync",-                    (doSync, "Populate lib/ as specified by futhark.pkg."))-                 , ("remove",-                    (doRemove, "Remove a required package from futhark.pkg."))-                 , ("upgrade",-                    (doUpgrade, "Upgrade all packages to newest versions."))-                 , ("versions",-                    (doVersions, "List available versions for a package."))-                 ]+  let commands =+        [ ( "add",+            (doAdd, "Add another required package to futhark.pkg.")+          ),+          ( "check",+            (doCheck, "Check that futhark.pkg is satisfiable.")+          ),+          ( "init",+            (doInit, "Create a new futhark.pkg and a lib/ skeleton.")+          ),+          ( "fmt",+            (doFmt, "Reformat futhark.pkg.")+          ),+          ( "sync",+            (doSync, "Populate lib/ as specified by futhark.pkg.")+          ),+          ( "remove",+            (doRemove, "Remove a required package from futhark.pkg.")+          ),+          ( "upgrade",+            (doUpgrade, "Upgrade all packages to newest versions.")+          ),+          ( "versions",+            (doVersions, "List available versions for a package.")+          )+        ]       usage = "options... <" <> intercalate "|" (map fst commands) <> ">"   case args of-    cmd : args' | Just (m, _) <- lookup cmd commands ->-                    m (unwords [prog, cmd]) args'+    cmd : args'+      | Just (m, _) <- lookup cmd commands ->+        m (unwords [prog, cmd]) args'     _ -> do       let bad _ () = Just $ do             let k = maxinum (map (length . fst) commands) + 3-            usageMsg $ T.unlines $-              ["<command> ...:", "", "Commands:"] ++-              [ "   " <> T.pack cmd <> T.pack (replicate (k - length cmd) ' ') <> desc-              | (cmd, (_, desc)) <- commands ]+            usageMsg $+              T.unlines $+                ["<command> ...:", "", "Commands:"]+                  ++ [ "   " <> T.pack cmd <> T.pack (replicate (k - length cmd) ' ') <> desc+                       | (cmd, (_, desc)) <- commands+                     ]        mainWithOptions () [] usage bad prog args--  where usageMsg s = do-          T.putStrLn $ "Usage: " <> T.pack prog <> " [--version] [--help] " <> s-          exitFailure+  where+    usageMsg s = do+      T.putStrLn $ "Usage: " <> T.pack prog <> " [--version] [--help] " <> s+      exitFailure
src/Futhark/CLI/PyOpenCL.hs view
@@ -1,29 +1,34 @@ {-# LANGUAGE FlexibleContexts #-}+ -- | @futhark pyopencl@ module Futhark.CLI.PyOpenCL (main) where  import Control.Monad.IO.Class-import System.FilePath-import System.Directory--import Futhark.Passes import qualified Futhark.CodeGen.Backends.PyOpenCL as PyOpenCL import Futhark.Compiler.CLI+import Futhark.Passes+import System.Directory+import System.FilePath  -- | Run @futhark pyopencl@. main :: String -> [String] -> IO ()-main = compilerMain () []-       "Compile PyOpenCL" "Generate Python + OpenCL code from optimised Futhark program."-       gpuPipeline $ \fcfg () mode outpath prog -> do-          let class_name =-                case mode of ToLibrary -> Just $ takeBaseName outpath-                             ToExecutable -> Nothing-          pyprog <- handleWarnings fcfg $ PyOpenCL.compileProg class_name prog-+main = compilerMain+  ()+  []+  "Compile PyOpenCL"+  "Generate Python + OpenCL code from optimised Futhark program."+  gpuPipeline+  $ \fcfg () mode outpath prog -> do+    let class_name =           case mode of-            ToLibrary ->-              liftIO $ writeFile (outpath `addExtension` "py") pyprog-            ToExecutable -> liftIO $ do-              writeFile outpath pyprog-              perms <- liftIO $ getPermissions outpath-              setPermissions outpath $ setOwnerExecutable True perms+            ToLibrary -> Just $ takeBaseName outpath+            ToExecutable -> Nothing+    pyprog <- handleWarnings fcfg $ PyOpenCL.compileProg class_name prog++    case mode of+      ToLibrary ->+        liftIO $ writeFile (outpath `addExtension` "py") pyprog+      ToExecutable -> liftIO $ do+        writeFile outpath pyprog+        perms <- liftIO $ getPermissions outpath+        setPermissions outpath $ setOwnerExecutable True perms
src/Futhark/CLI/Python.hs view
@@ -1,29 +1,34 @@ {-# LANGUAGE FlexibleContexts #-}+ -- | @futhark py@ module Futhark.CLI.Python (main) where  import Control.Monad.IO.Class-import System.FilePath-import System.Directory--import Futhark.Passes import qualified Futhark.CodeGen.Backends.SequentialPython as SequentialPy import Futhark.Compiler.CLI+import Futhark.Passes+import System.Directory+import System.FilePath  -- | Run @futhark py@ main :: String -> [String] -> IO ()-main = compilerMain () []-       "Compile sequential Python" "Generate sequential Python code from optimised Futhark program."-       sequentialCpuPipeline $ \fcfg () mode outpath prog -> do-          let class_name =-                case mode of ToLibrary -> Just $ takeBaseName outpath-                             ToExecutable -> Nothing-          pyprog <- handleWarnings fcfg $ SequentialPy.compileProg class_name prog-+main = compilerMain+  ()+  []+  "Compile sequential Python"+  "Generate sequential Python code from optimised Futhark program."+  sequentialCpuPipeline+  $ \fcfg () mode outpath prog -> do+    let class_name =           case mode of-            ToLibrary ->-              liftIO $ writeFile (outpath `addExtension` "py") pyprog-            ToExecutable -> liftIO $ do-              writeFile outpath pyprog-              perms <- liftIO $ getPermissions outpath-              setPermissions outpath $ setOwnerExecutable True perms+            ToLibrary -> Just $ takeBaseName outpath+            ToExecutable -> Nothing+    pyprog <- handleWarnings fcfg $ SequentialPy.compileProg class_name prog++    case mode of+      ToLibrary ->+        liftIO $ writeFile (outpath `addExtension` "py") pyprog+      ToExecutable -> liftIO $ do+        writeFile outpath pyprog+        perms <- liftIO $ getPermissions outpath+        setPermissions outpath $ setOwnerExecutable True perms
src/Futhark/CLI/Query.hs view
@@ -1,17 +1,16 @@ -- | @futhark query@ module Futhark.CLI.Query (main) where -import Text.Read (readMaybe)- import Futhark.Compiler-import Futhark.Util.Options import Futhark.Util.Loc+import Futhark.Util.Options import Language.Futhark.Query import Language.Futhark.Syntax+import Text.Read (readMaybe)  -- | Run @futhark query@. main :: String -> [String] -> IO ()-main = mainWithOptions () [] "program line:col" $ \args () ->+main = mainWithOptions () [] "program line col" $ \args () ->   case args of     [file, line, col] -> do       line' <- readMaybe line
src/Futhark/CLI/REPL.hs view
@@ -1,58 +1,58 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE ScopedTypeVariables #-}+ -- | @futhark repl@ module Futhark.CLI.REPL (main) where -import Control.Monad.Free.Church import Control.Exception-import Data.Char-import Data.List (intercalate, intersperse)-import Data.Maybe-import Data.Version import Control.Monad-import Control.Monad.IO.Class-import Control.Monad.State import Control.Monad.Except+import Control.Monad.Free.Church+import Control.Monad.State+import Data.Char+import Data.List (intercalate, intersperse) import qualified Data.List.NonEmpty as NE+import Data.Maybe import qualified Data.Text as T import qualified Data.Text.IO as T-import NeatInterpolation (text)-import System.Directory-import System.FilePath-import System.Console.GetOpt-import Text.Read (readMaybe)-import qualified System.Console.Haskeline as Haskeline--import Language.Futhark-import Language.Futhark.Parser hiding (EOF)-import qualified Language.Futhark.TypeChecker as T-import qualified Language.Futhark.Semantic as T-import Futhark.MonadFreshNames-import Futhark.Version+import Data.Version import Futhark.Compiler+import Futhark.MonadFreshNames import Futhark.Pipeline-import Futhark.Util.Options import Futhark.Util (toPOSIX)-+import Futhark.Util.Options+import Futhark.Version+import Language.Futhark import qualified Language.Futhark.Interpreter as I+import Language.Futhark.Parser hiding (EOF)+import qualified Language.Futhark.Semantic as T+import qualified Language.Futhark.TypeChecker as T+import NeatInterpolation (text)+import System.Console.GetOpt+import qualified System.Console.Haskeline as Haskeline+import System.Directory+import System.FilePath+import Text.Read (readMaybe)  banner :: String-banner = unlines [-  "|// |\\    |   |\\  |\\   /",-  "|/  | \\   |\\  |\\  |/  /",-  "|   |  \\  |/  |   |\\  \\",-  "|   |   \\ |   |   | \\  \\"-  ]+banner =+  unlines+    [ "|// |\\    |   |\\  |\\   /",+      "|/  | \\   |\\  |\\  |/  /",+      "|   |  \\  |/  |   |\\  \\",+      "|   |   \\ |   |   | \\  \\"+    ]  -- | Run @futhark repl@. main :: String -> [String] -> IO () main = mainWithOptions interpreterConfig options "options... [program.fut]" run-  where run []     _      = Just $ repl Nothing-        run [prog] _      = Just $ repl $ Just prog-        run _      _      = Nothing+  where+    run [] _ = Just $ repl Nothing+    run [prog] _ = Just $ repl $ Just prog+    run _ _ = Nothing  data StopReason = EOF | Stop | Exit | Load FilePath @@ -77,8 +77,9 @@               liftIO $ newFutharkiState (futharkiCount s) $ Just file             case maybe_new_state of               Right new_state -> toploop new_state-              Left err -> do liftIO $ putStrLn err-                             toploop s'+              Left err -> do+                liftIO $ putStrLn err+                toploop s'           Right _ -> return ()        finish s = do@@ -99,92 +100,116 @@     Nothing -> return True -- EOF     Just 'y' -> return True     Just 'n' -> return False-    _        -> confirmQuit+    _ -> confirmQuit -newtype InterpreterConfig = InterpreterConfig { interpreterEntryPoint :: Name }+newtype InterpreterConfig = InterpreterConfig {interpreterEntryPoint :: Name}  interpreterConfig :: InterpreterConfig interpreterConfig = InterpreterConfig defaultEntryPoint  options :: [FunOptDescr InterpreterConfig]-options = [ Option "e" ["entry-point"]-          (ReqArg (\entry -> Right $ \config ->-                      config { interpreterEntryPoint = nameFromString entry })-           "NAME")-            "The entry point to execute."-          ]+options =+  [ Option+      "e"+      ["entry-point"]+      ( ReqArg+          ( \entry -> Right $ \config ->+              config {interpreterEntryPoint = nameFromString entry}+          )+          "NAME"+      )+      "The entry point to execute."+  ]  -- | Representation of breaking at a breakpoint, to allow for -- navigating through the stack frames and such.-data Breaking = Breaking { breakingStack :: NE.NonEmpty I.StackFrame-                         , breakingAt :: Int-                           -- ^ Index of the current breakpoint (with-                           -- 0 being the outermost).-                         }+data Breaking = Breaking+  { breakingStack :: NE.NonEmpty I.StackFrame,+    -- | Index of the current breakpoint (with+    -- 0 being the outermost).+    breakingAt :: Int+  } -data FutharkiState =-  FutharkiState { futharkiImports :: Imports-                , futharkiNameSource :: VNameSource-                , futharkiCount :: Int-                , futharkiEnv :: (T.Env, I.Ctx)-                , futharkiBreaking :: Maybe Breaking-                  -- ^ Are we currently stopped at a breakpoint?-                , futharkiSkipBreaks :: [Loc]-                -- ^ Skip breakpoints at these locations.-                , futharkiBreakOnNaN :: Bool-                , futharkiLoaded :: Maybe FilePath-                -- ^ The currently loaded file.-                }+data FutharkiState = FutharkiState+  { futharkiImports :: Imports,+    futharkiNameSource :: VNameSource,+    futharkiCount :: Int,+    futharkiEnv :: (T.Env, I.Ctx),+    -- | Are we currently stopped at a breakpoint?+    futharkiBreaking :: Maybe Breaking,+    -- | Skip breakpoints at these locations.+    futharkiSkipBreaks :: [Loc],+    futharkiBreakOnNaN :: Bool,+    -- | The currently loaded file.+    futharkiLoaded :: Maybe FilePath+  }  newFutharkiState :: Int -> Maybe FilePath -> IO (Either String FutharkiState) newFutharkiState count maybe_file = runExceptT $ do   (imports, src, tenv, ienv) <- case maybe_file of-     Nothing -> do       -- Load the builtins through the type checker.       (_, imports, src) <- badOnLeft show =<< runExceptT (readLibrary [])       -- Then into the interpreter.-      ienv <- foldM (\ctx -> badOnLeft show <=< runInterpreter' . I.interpretImport ctx)-              I.initialCtx $ map (fmap fileProg) imports+      ienv <-+        foldM+          (\ctx -> badOnLeft show <=< runInterpreter' . I.interpretImport ctx)+          I.initialCtx+          $ map (fmap fileProg) imports        -- Then make the prelude available in the type checker.-      (tenv, d, src') <- badOnLeft pretty $ T.checkDec imports src T.initialEnv-                         (T.mkInitialImport ".") $ mkOpen "/prelude/prelude"+      (tenv, d, src') <-+        badOnLeft pretty $+          T.checkDec+            imports+            src+            T.initialEnv+            (T.mkInitialImport ".")+            $ mkOpen "/prelude/prelude"       -- Then in the interpreter.       ienv' <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv d)       return (imports, src', tenv, ienv')-     Just file -> do       (ws, imports, src) <--        badOnLeft show =<<-        liftIO (runExceptT (readProgram file)-                 `catch` \(err::IOException) ->-                   return (externalErrorS (show err)))+        badOnLeft show+          =<< liftIO+            ( runExceptT (readProgram file)+                `catch` \(err :: IOException) ->+                  return (externalErrorS (show err))+            )       liftIO $ print ws        let imp = T.mkInitialImport "."-      ienv1 <- foldM (\ctx -> badOnLeft show <=< runInterpreter' . I.interpretImport ctx) I.initialCtx $-               map (fmap fileProg) imports-      (tenv1, d1, src') <- badOnLeft pretty $ T.checkDec imports src T.initialEnv imp $-                           mkOpen "/prelude/prelude"-      (tenv2, d2, src'') <- badOnLeft pretty $ T.checkDec imports src' tenv1 imp $-                            mkOpen $ toPOSIX $ dropExtension file+      ienv1 <-+        foldM (\ctx -> badOnLeft show <=< runInterpreter' . I.interpretImport ctx) I.initialCtx $+          map (fmap fileProg) imports+      (tenv1, d1, src') <-+        badOnLeft pretty $+          T.checkDec imports src T.initialEnv imp $+            mkOpen "/prelude/prelude"+      (tenv2, d2, src'') <-+        badOnLeft pretty $+          T.checkDec imports src' tenv1 imp $+            mkOpen $ toPOSIX $ dropExtension file       ienv2 <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv1 d1)       ienv3 <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv2 d2)       return (imports, src'', tenv2, ienv3) -  return FutharkiState { futharkiImports = imports-                       , futharkiNameSource = src-                       , futharkiCount = count-                       , futharkiEnv = (tenv, ienv)-                       , futharkiBreaking = Nothing-                       , futharkiSkipBreaks = mempty-                       , futharkiBreakOnNaN = False-                       , futharkiLoaded = maybe_file-                       }-  where badOnLeft :: (err -> String) -> Either err a -> ExceptT String IO a-        badOnLeft _ (Right x) = return x-        badOnLeft p (Left err) = throwError $ p err+  return+    FutharkiState+      { futharkiImports = imports,+        futharkiNameSource = src,+        futharkiCount = count,+        futharkiEnv = (tenv, ienv),+        futharkiBreaking = Nothing,+        futharkiSkipBreaks = mempty,+        futharkiBreakOnNaN = False,+        futharkiLoaded = maybe_file+      }+  where+    badOnLeft :: (err -> String) -> Either err a -> ExceptT String IO a+    badOnLeft _ (Right x) = return x+    badOnLeft p (Left err) = throwError $ p err  getPrompt :: FutharkiM String getPrompt = do@@ -195,10 +220,15 @@ mkOpen f = OpenDec (ModImport f NoInfo mempty) mempty  -- The ExceptT part is more of a continuation, really.-newtype FutharkiM a =-  FutharkiM { runFutharkiM :: ExceptT StopReason (StateT FutharkiState (Haskeline.InputT IO)) a }-  deriving (Functor, Applicative, Monad,-            MonadState FutharkiState, MonadIO, MonadError StopReason)+newtype FutharkiM a = FutharkiM {runFutharkiM :: ExceptT StopReason (StateT FutharkiState (Haskeline.InputT IO)) a}+  deriving+    ( Functor,+      Applicative,+      Monad,+      MonadState FutharkiState,+      MonadIO,+      MonadError StopReason+    )  readEvalPrint :: FutharkiM () readEvalPrint = do@@ -209,16 +239,17 @@     Nothing       | isJust breaking -> throwError Stop       | otherwise -> return ()-     Just (':', command) -> do       let (cmdname, rest) = T.break isSpace command           arg = T.dropWhileEnd isSpace $ T.dropWhile isSpace rest       case filter ((cmdname `T.isPrefixOf`) . fst) commands of         [] -> liftIO $ T.putStrLn $ "Unknown command '" <> cmdname <> "'"         [(_, (cmdf, _))] -> cmdf arg-        matches -> liftIO $ T.putStrLn $ "Ambiguous command; could be one of " <>-                   mconcat (intersperse ", " (map fst matches))-+        matches ->+          liftIO $+            T.putStrLn $+              "Ambiguous command; could be one of "+                <> mconcat (intersperse ", " (map fst matches))     _ -> do       -- Read a declaration or expression.       maybe_dec_or_e <- parseDecOrExpIncrM (inputLine "  ") prompt line@@ -227,12 +258,13 @@         Left err -> liftIO $ print err         Right (Left d) -> onDec d         Right (Right e) -> onExp e-  modify $ \s -> s { futharkiCount = futharkiCount s + 1 }-  where inputLine prompt = do-          inp <- FutharkiM $ lift $ lift $ Haskeline.getInputLine prompt-          case inp of-            Just s -> return $ T.pack s-            Nothing -> throwError EOF+  modify $ \s -> s {futharkiCount = futharkiCount s + 1}+  where+    inputLine prompt = do+      inp <- FutharkiM $ lift $ lift $ Haskeline.getInputLine prompt+      case inp of+        Just s -> return $ T.pack s+        Nothing -> throwError EOF  getIt :: FutharkiM (Imports, VNameSource, T.Env, I.Ctx) getIt = do@@ -256,7 +288,7 @@    case imp_r of     Left e -> liftIO $ print e-    Right (_, imports',  src') ->+    Right (_, imports', src') ->       case T.checkDec imports' src' tenv cur_import d of         Left e -> liftIO $ putStrLn $ pretty e         Right (tenv', d', src'') -> do@@ -268,28 +300,30 @@             I.interpretDec ienv' d'           case int_r of             Left err -> liftIO $ print err-            Right ienv' -> modify $ \s -> s { futharkiEnv = (tenv', ienv')-                                            , futharkiImports = imports'-                                            , futharkiNameSource = src''-                                            }+            Right ienv' -> modify $ \s ->+              s+                { futharkiEnv = (tenv', ienv'),+                  futharkiImports = imports',+                  futharkiNameSource = src''+                }  onExp :: UncheckedExp -> FutharkiM () onExp e = do   (imports, src, tenv, ienv) <- getIt   case either (Left . pretty) Right $-       T.checkExp imports src tenv e of+    T.checkExp imports src tenv e of     Left err -> liftIO $ putStrLn err     Right (tparams, e')       | null tparams -> do-          r <- runInterpreter $ I.interpretExp ienv e'-          case r of-            Left err -> liftIO $ print err-            Right v -> liftIO $ putStrLn $ pretty v-+        r <- runInterpreter $ I.interpretExp ienv e'+        case r of+          Left err -> liftIO $ print err+          Right v -> liftIO $ putStrLn $ pretty v       | otherwise -> liftIO $ do-          putStrLn $ "Inferred type of expression: " ++ pretty (typeOf e')-          putStrLn $ "The following types are ambiguous: " ++-            intercalate ", " (map (prettyName . typeParamName) tparams)+        putStrLn $ "Inferred type of expression: " ++ pretty (typeOf e')+        putStrLn $+          "The following types are ambiguous: "+            ++ intercalate ", " (map (prettyName . typeParamName) tparams)  prettyBreaking :: Breaking -> String prettyBreaking b =@@ -302,8 +336,8 @@ breakForReason s _ I.BreakNaN   | not $ futharkiBreakOnNaN s = False breakForReason s top _ =-  isNothing (futharkiBreaking s) &&-  locOf top `notElem` futharkiSkipBreaks s+  isNothing (futharkiBreaking s)+    && locOf top `notElem` futharkiSkipBreaks s  runInterpreter :: F I.ExtOp a -> FutharkiM (Either I.InterpreterError a) runInterpreter m = runF m (return . Right) intOp@@ -316,8 +350,9 @@     intOp (I.ExtOpBreak why callstack c) = do       s <- get -      let why' = case why of I.BreakPoint -> "Breakpoint"-                             I.BreakNaN -> "NaN produced"+      let why' = case why of+            I.BreakPoint -> "Breakpoint"+            I.BreakNaN -> "NaN produced"           top = NE.head callstack           ctx = I.stackFrameCtx top           tenv = I.typeCheckerEnv $ I.ctxEnv ctx@@ -332,33 +367,41 @@         -- Note the cleverness to preserve the Haskeline session (for         -- line history and such).         (stop, s') <--          FutharkiM $ lift $ lift $-          runStateT (runExceptT $ runFutharkiM $ forever readEvalPrint)-          s { futharkiEnv = (tenv, ctx)-            , futharkiCount = futharkiCount s + 1-            , futharkiBreaking = Just breaking-            }+          FutharkiM $+            lift $+              lift $+                runStateT+                  (runExceptT $ runFutharkiM $ forever readEvalPrint)+                  s+                    { futharkiEnv = (tenv, ctx),+                      futharkiCount = futharkiCount s + 1,+                      futharkiBreaking = Just breaking+                    }          case stop of           Left (Load file) -> throwError $ Load file-          _ -> do liftIO $ putStrLn "Continuing..."-                  put s { futharkiCount =-                            futharkiCount s'-                        , futharkiSkipBreaks =-                            futharkiSkipBreaks s' <> futharkiSkipBreaks s-                        , futharkiBreakOnNaN =-                            futharkiBreakOnNaN s'-                        }+          _ -> do+            liftIO $ putStrLn "Continuing..."+            put+              s+                { futharkiCount =+                    futharkiCount s',+                  futharkiSkipBreaks =+                    futharkiSkipBreaks s' <> futharkiSkipBreaks s,+                  futharkiBreakOnNaN =+                    futharkiBreakOnNaN s'+                }        c  runInterpreter' :: MonadIO m => F I.ExtOp a -> m (Either I.InterpreterError a) runInterpreter' m = runF m (return . Right) intOp-  where intOp (I.ExtOpError err) = return $ Left err-        intOp (I.ExtOpTrace w v c) = do-          liftIO $ putStrLn $ "Trace at " ++ locStr w ++ ": " ++ v-          c-        intOp (I.ExtOpBreak _ _ c) = c+  where+    intOp (I.ExtOpError err) = return $ Left err+    intOp (I.ExtOpTrace w v c) = do+      liftIO $ putStrLn $ "Trace at " ++ locStr w ++ ": " ++ v+      c+    intOp (I.ExtOpBreak _ _ c) = c  type Command = T.Text -> FutharkiM () @@ -370,11 +413,12 @@     (True, Nothing) -> liftIO $ T.putStrLn "No file specified and no file previously loaded."     (False, _) -> throwError $ Load $ T.unpack file -genTypeCommand :: Show err =>-                  (String -> T.Text -> Either err a)-               -> (Imports -> VNameSource -> T.Env -> a -> Either T.TypeError b)-               -> (b -> String)-               -> Command+genTypeCommand ::+  Show err =>+  (String -> T.Text -> Either err a) ->+  (Imports -> VNameSource -> T.Env -> a -> Either T.TypeError b) ->+  (b -> String) ->+  Command genTypeCommand f g h e = do   prompt <- getPrompt   case f prompt e of@@ -389,8 +433,9 @@  typeCommand :: Command typeCommand = genTypeCommand parseExp T.checkExp $ \(ps, e) ->-  pretty e <> concatMap ((" "<>) . pretty) ps <>-  " : " <> pretty (typeOf e)+  pretty e <> concatMap ((" " <>) . pretty) ps+    <> " : "+    <> pretty (typeOf e)  mtypeCommand :: Command mtypeCommand = genTypeCommand parseModExp T.checkModExp $ pretty . fst@@ -400,31 +445,35 @@   top <- gets $ fmap (NE.head . breakingStack) . futharkiBreaking   case top of     Nothing -> liftIO $ putStrLn "Not currently stopped at a breakpoint."-    Just top' -> do modify $ \s -> s { futharkiSkipBreaks = locOf top' : futharkiSkipBreaks s }-                    throwError Stop+    Just top' -> do+      modify $ \s -> s {futharkiSkipBreaks = locOf top' : futharkiSkipBreaks s}+      throwError Stop  nanbreakCommand :: Command nanbreakCommand _ = do-  modify $ \s -> s { futharkiBreakOnNaN = not $ futharkiBreakOnNaN s }+  modify $ \s -> s {futharkiBreakOnNaN = not $ futharkiBreakOnNaN s}   b <- gets futharkiBreakOnNaN-  liftIO $ putStrLn $-    if b-    then "Now treating NaNs as breakpoints."-    else "No longer treating NaNs as breakpoints."+  liftIO $+    putStrLn $+      if b+        then "Now treating NaNs as breakpoints."+        else "No longer treating NaNs as breakpoints."  frameCommand :: Command frameCommand which = do   maybe_stack <- gets $ fmap breakingStack . futharkiBreaking   case (maybe_stack, readMaybe $ T.unpack which) of     (Just stack, Just i)-      | frame:_ <- NE.drop i stack -> do-          let breaking = Breaking stack i-              ctx = I.stackFrameCtx frame-              tenv = I.typeCheckerEnv $ I.ctxEnv ctx-          modify $ \s -> s { futharkiEnv = (tenv, ctx)-                           , futharkiBreaking = Just breaking-                           }-          liftIO $ putStrLn $ prettyBreaking breaking+      | frame : _ <- NE.drop i stack -> do+        let breaking = Breaking stack i+            ctx = I.stackFrameCtx frame+            tenv = I.typeCheckerEnv $ I.ctxEnv ctx+        modify $ \s ->+          s+            { futharkiEnv = (tenv, ctx),+              futharkiBreaking = Just breaking+            }+        liftIO $ putStrLn $ prettyBreaking breaking     (Just _, _) ->       liftIO $ putStrLn $ "Invalid stack index: " ++ T.unpack which     (Nothing, _) ->@@ -435,15 +484,17 @@  cdCommand :: Command cdCommand dir- | T.null dir = liftIO $ putStrLn "Usage: ':cd <dir>'."- | otherwise =-    liftIO $ setCurrentDirectory (T.unpack dir)-    `catch` \(err::IOException) -> print err+  | T.null dir = liftIO $ putStrLn "Usage: ':cd <dir>'."+  | otherwise =+    liftIO $+      setCurrentDirectory (T.unpack dir)+        `catch` \(err :: IOException) -> print err  helpCommand :: Command-helpCommand _ = liftIO $ forM_ commands $ \(cmd, (_, desc)) -> do+helpCommand _ = liftIO $+  forM_ commands $ \(cmd, (_, desc)) -> do     T.putStrLn $ ":" <> cmd-    T.putStrLn $ T.replicate (1+T.length cmd) "-"+    T.putStrLn $ T.replicate (1 + T.length cmd) "-"     T.putStr desc     T.putStrLn ""     T.putStrLn ""@@ -452,7 +503,10 @@ quitCommand _ = throwError Exit  commands :: [(T.Text, (Command, T.Text))]-commands = [("load", (loadCommand, [text|+commands =+  [ ( "load",+      ( loadCommand,+        [text| Load a Futhark source file.  Usage:    > :load foo.fut@@ -464,34 +518,73 @@ second time will replace the previously loaded file.  It will also replace any declarations entered at the REPL. -|])),-            ("type", (typeCommand, [text|+|]+      )+    ),+    ( "type",+      ( typeCommand,+        [text| Show the type of an expression, which must fit on a single line.-|])),-            ("mtype", (mtypeCommand, [text|+|]+      )+    ),+    ( "mtype",+      ( mtypeCommand,+        [text| Show the type of a module expression, which must fit on a single line.-|])),-            ("unbreak", (unbreakCommand, [text|+|]+      )+    ),+    ( "unbreak",+      ( unbreakCommand,+        [text| Skip all future occurences of the current breakpoint.-|])),-            ("nanbreak", (nanbreakCommand, [text|+|]+      )+    ),+    ( "nanbreak",+      ( nanbreakCommand,+        [text| Toggle treating operators that produce new NaNs as breakpoints.  We consider a NaN to be "new" if none of the arguments to the operator in question is a NaN.-|])),-            ("frame", (frameCommand, [text|+|]+      )+    ),+    ( "frame",+      ( frameCommand,+        [text| While at a break point, jump to another stack frame, whose variables can then be inspected.  Resuming from the breakpoint will jump back to the innermost stack frame.-|])),-            ("pwd", (pwdCommand, [text|+|]+      )+    ),+    ( "pwd",+      ( pwdCommand,+        [text| Print the current working directory.-|])),-            ("cd", (cdCommand, [text|+|]+      )+    ),+    ( "cd",+      ( cdCommand,+        [text| Change the current working directory.-|])),-            ("help", (helpCommand, [text|+|]+      )+    ),+    ( "help",+      ( helpCommand,+        [text| Print a list of commands and a description of their behaviour.-|])),-            ("quit", (quitCommand, [text|+|]+      )+    ),+    ( "quit",+      ( quitCommand,+        [text| Exit REPL.-|]))]+|]+      )+    )+  ]
src/Futhark/CLI/Run.hs view
@@ -1,47 +1,47 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}+ -- | @futhark run@ module Futhark.CLI.Run (main) where -import Control.Monad.Free.Church import Control.Exception-import Data.Maybe-import qualified Data.Map as M import Control.Monad-import Control.Monad.IO.Class import Control.Monad.Except+import Control.Monad.Free.Church+import qualified Data.Map as M+import Data.Maybe import qualified Data.Text.IO as T-import System.FilePath-import System.Exit-import System.Console.GetOpt-import System.IO--import Prelude--import Language.Futhark-import Language.Futhark.Parser hiding (EOF)-import qualified Language.Futhark.TypeChecker as T-import qualified Language.Futhark.Semantic as T import Futhark.Compiler import Futhark.Pipeline-import Futhark.Util.Options import Futhark.Util (toPOSIX)-+import Futhark.Util.Options+import Language.Futhark import qualified Language.Futhark.Interpreter as I+import Language.Futhark.Parser hiding (EOF)+import qualified Language.Futhark.Semantic as T+import qualified Language.Futhark.TypeChecker as T+import System.Console.GetOpt+import System.Exit+import System.FilePath+import System.IO+import Prelude  -- | Run @futhark run@. main :: String -> [String] -> IO () main = mainWithOptions interpreterConfig options "options... <program.fut>" run-  where run [prog] config = Just $ interpret config prog-        run _      _      = Nothing+  where+    run [prog] config = Just $ interpret config prog+    run _ _ = Nothing  interpret :: InterpreterConfig -> FilePath -> IO () interpret config fp = do   pr <- newFutharkiState config fp-  (tenv, ienv) <- case pr of Left err -> do hPutStrLn stderr err-                                            exitFailure-                             Right env -> return env+  (tenv, ienv) <- case pr of+    Left err -> do+      hPutStrLn stderr err+      exitFailure+    Right env -> return env    let entry = interpreterEntryPoint config   vr <- parseValues "stdin" <$> T.getContents@@ -58,18 +58,21 @@     case M.lookup (T.Term, entry) $ T.envNameMap tenv of       Just fname         | Just (T.BoundV _ t) <- M.lookup (qualLeaf fname) $ T.envVtable tenv ->-            return (fname, toStructural $ snd $ unfoldFunType t)-      _ -> do hPutStrLn stderr $ "Invalid entry point: " ++ pretty entry-              exitFailure+          return (fname, toStructural $ snd $ unfoldFunType t)+      _ -> do+        hPutStrLn stderr $ "Invalid entry point: " ++ pretty entry+        exitFailure    case I.interpretFunction ienv (qualLeaf fname) inps of-    Left err -> do hPutStrLn stderr err-                   exitFailure+    Left err -> do+      hPutStrLn stderr err+      exitFailure     Right run -> do       run' <- runInterpreter' run       case run' of-        Left err -> do hPrint stderr err-                       exitFailure+        Left err -> do+          hPrint stderr err+          exitFailure         Right res ->           case (I.fromTuple res, isTupleRecord ret) of             (Just vs, Just ts) -> zipWithM_ putValue vs ts@@ -80,57 +83,76 @@   | I.isEmptyArray v = putStrLn $ I.prettyEmptyArray t v   | otherwise = putStrLn $ pretty v -data InterpreterConfig =-  InterpreterConfig { interpreterEntryPoint :: Name-                    , interpreterPrintWarnings :: Bool-                    }+data InterpreterConfig = InterpreterConfig+  { interpreterEntryPoint :: Name,+    interpreterPrintWarnings :: Bool+  }  interpreterConfig :: InterpreterConfig interpreterConfig = InterpreterConfig defaultEntryPoint True  options :: [FunOptDescr InterpreterConfig]-options = [ Option "e" ["entry-point"]-            (ReqArg (\entry -> Right $ \config ->-                        config { interpreterEntryPoint = nameFromString entry })-             "NAME")-            "The entry point to execute."-          , Option "w" ["no-warnings"]-            (NoArg $ Right $ \config -> config { interpreterPrintWarnings = False })-            "Do not print warnings."-          ]+options =+  [ Option+      "e"+      ["entry-point"]+      ( ReqArg+          ( \entry -> Right $ \config ->+              config {interpreterEntryPoint = nameFromString entry}+          )+          "NAME"+      )+      "The entry point to execute.",+    Option+      "w"+      ["no-warnings"]+      (NoArg $ Right $ \config -> config {interpreterPrintWarnings = False})+      "Do not print warnings."+  ] -newFutharkiState :: InterpreterConfig -> FilePath-                 -> IO (Either String (T.Env, I.Ctx))+newFutharkiState ::+  InterpreterConfig ->+  FilePath ->+  IO (Either String (T.Env, I.Ctx)) newFutharkiState cfg file = runExceptT $ do   (ws, imports, src) <--    badOnLeft show =<<-    liftIO (runExceptT (readProgram file)-            `catch` \(err::IOException) ->-               return (externalErrorS (show err)))+    badOnLeft show+      =<< liftIO+        ( runExceptT (readProgram file)+            `catch` \(err :: IOException) ->+              return (externalErrorS (show err))+        )   when (interpreterPrintWarnings cfg) $     liftIO $ hPutStr stderr $ show ws    let imp = T.mkInitialImport "."-  ienv1 <- foldM (\ctx -> badOnLeft show <=< runInterpreter' . I.interpretImport ctx) I.initialCtx $-           map (fmap fileProg) imports-  (tenv1, d1, src') <- badOnLeft pretty $ T.checkDec imports src T.initialEnv imp $-                       mkOpen "/prelude/prelude"-  (tenv2, d2, _) <- badOnLeft pretty $ T.checkDec imports src' tenv1 imp $-                    mkOpen $ toPOSIX $ dropExtension file+  ienv1 <-+    foldM (\ctx -> badOnLeft show <=< runInterpreter' . I.interpretImport ctx) I.initialCtx $+      map (fmap fileProg) imports+  (tenv1, d1, src') <-+    badOnLeft pretty $+      T.checkDec imports src T.initialEnv imp $+        mkOpen "/prelude/prelude"+  (tenv2, d2, _) <-+    badOnLeft pretty $+      T.checkDec imports src' tenv1 imp $+        mkOpen $ toPOSIX $ dropExtension file   ienv2 <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv1 d1)   ienv3 <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv2 d2)   return (tenv2, ienv3)-  where badOnLeft :: (err -> String) -> Either err a -> ExceptT String IO a-        badOnLeft _ (Right x) = return x-        badOnLeft p (Left err) = throwError $ p err+  where+    badOnLeft :: (err -> String) -> Either err a -> ExceptT String IO a+    badOnLeft _ (Right x) = return x+    badOnLeft p (Left err) = throwError $ p err  mkOpen :: FilePath -> UncheckedDec mkOpen f = OpenDec (ModImport f NoInfo mempty) mempty  runInterpreter' :: MonadIO m => F I.ExtOp a -> m (Either I.InterpreterError a) runInterpreter' m = runF m (return . Right) intOp-  where intOp (I.ExtOpError err) = return $ Left err-        intOp (I.ExtOpTrace w v c) = do-          liftIO $ putStrLn $ "Trace at " ++ locStr w ++ ": " ++ v-          c-        intOp (I.ExtOpBreak _ _ c) = c+  where+    intOp (I.ExtOpError err) = return $ Left err+    intOp (I.ExtOpTrace w v c) = do+      liftIO $ putStrLn $ "Trace at " ++ locStr w ++ ": " ++ v+      c+    intOp (I.ExtOpBreak _ _ c) = c
src/Futhark/CLI/Test.hs view
@@ -1,8 +1,11 @@-{-# LANGUAGE OverloadedStrings, FlexibleContexts, LambdaCase #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+ -- | @futhark test@ module Futhark.CLI.Test (main) where -import Control.Applicative.Lift (runErrors, failure, Errors, Lift(..))+import Control.Applicative.Lift (Errors, Lift (..), failure, runErrors) import Control.Concurrent import Control.Exception import Control.Monad@@ -15,23 +18,22 @@ import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.IO as T-import System.Console.ANSI-import System.Process.ByteString (readProcessWithExitCode)-import System.Environment-import System.Exit-import System.FilePath-import System.Console.GetOpt-import qualified System.Console.Terminal.Size as Terminal-import System.IO-import Text.Regex.TDFA- import Futhark.Analysis.Metrics import Futhark.Test import Futhark.Util (fancyTerminal)+import Futhark.Util.Console import Futhark.Util.Options import Futhark.Util.Pretty (prettyText)-import Futhark.Util.Console import Futhark.Util.Table+import System.Console.ANSI+import System.Console.GetOpt+import qualified System.Console.Terminal.Size as Terminal+import System.Environment+import System.Exit+import System.FilePath+import System.IO+import System.Process.ByteString (readProcessWithExitCode)+import Text.Regex.TDFA  --- Test execution @@ -53,8 +55,8 @@ context :: T.Text -> TestM a -> TestM a context s = withExceptT $   \case-    []      -> []-    (e:es') -> (s <> ":\n" <> e):es'+    [] -> []+    (e : es') -> (s <> ":\n" <> e) : es'  accErrors :: [TestM a] -> TestM [a] accErrors tests = do@@ -65,16 +67,18 @@ accErrors_ :: [TestM a] -> TestM () accErrors_ = void . accErrors -data TestResult = Success-                | Failure [T.Text]-                deriving (Eq, Show)+data TestResult+  = Success+  | Failure [T.Text]+  deriving (Eq, Show) -data TestCase = TestCase { _testCaseMode :: TestMode-                         , testCaseProgram :: FilePath-                         , testCaseTest :: ProgramTest-                         , _testCasePrograms :: ProgConfig-                         }-                deriving (Show)+data TestCase = TestCase+  { _testCaseMode :: TestMode,+    testCaseProgram :: FilePath,+    testCaseTest :: ProgramTest,+    _testCasePrograms :: ProgConfig+  }+  deriving (Show)  instance Eq TestCase where   x == y = testCaseProgram x == testCaseProgram y@@ -82,95 +86,119 @@ instance Ord TestCase where   x `compare` y = testCaseProgram x `compare` testCaseProgram y -data RunResult = ErrorResult Int SBS.ByteString-               | SuccessResult [Value]+data RunResult+  = ErrorResult Int SBS.ByteString+  | SuccessResult [Value]  progNotFound :: T.Text -> T.Text progNotFound s = s <> ": command not found"  optimisedProgramMetrics :: ProgConfig -> StructurePipeline -> FilePath -> TestM AstMetrics optimisedProgramMetrics programs pipeline program =-  case pipeline of SOACSPipeline ->-                     check "-s"-                   KernelsPipeline ->-                     check "--kernels"-                   SequentialCpuPipeline ->-                     check "--cpu"-                   GpuPipeline ->-                     check "--gpu"-  where check opt = do-          futhark <- io $ maybe getExecutablePath return $ configFuthark programs-          (code, output, err) <--            io $ readProcessWithExitCode futhark ["dev", opt, "--metrics", program] ""-          let output' = T.decodeUtf8 output-          case code of-            ExitSuccess-              | [(m, [])] <- reads $ T.unpack output' -> return m-              | otherwise -> throwError $ "Could not read metrics output:\n" <> output'-            ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark-            ExitFailure _ -> throwError $ T.decodeUtf8 err+  case pipeline of+    SOACSPipeline ->+      check "-s"+    KernelsPipeline ->+      check "--kernels"+    SequentialCpuPipeline ->+      check "--cpu"+    GpuPipeline ->+      check "--gpu"+  where+    check opt = do+      futhark <- io $ maybe getExecutablePath return $ configFuthark programs+      (code, output, err) <-+        io $ readProcessWithExitCode futhark ["dev", opt, "--metrics", program] ""+      let output' = T.decodeUtf8 output+      case code of+        ExitSuccess+          | [(m, [])] <- reads $ T.unpack output' -> return m+          | otherwise -> throwError $ "Could not read metrics output:\n" <> output'+        ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark+        ExitFailure _ -> throwError $ T.decodeUtf8 err  testMetrics :: ProgConfig -> FilePath -> StructureTest -> TestM () testMetrics programs program (StructureTest pipeline (AstMetrics expected)) =   context "Checking metrics" $ do     actual <- optimisedProgramMetrics programs pipeline program     accErrors_ $ map (ok actual) $ M.toList expected-  where ok (AstMetrics metrics) (name, expected_occurences) =-          case M.lookup name metrics of-            Nothing-              | expected_occurences > 0 ->-              throwError $ name <> " should have occurred " <> T.pack (show expected_occurences) <>-              " times, but did not occur at all in optimised program."-            Just actual_occurences-              | expected_occurences /= actual_occurences ->-                throwError $ name <> " should have occurred " <> T.pack (show expected_occurences) <>-              " times, but occurred " <> T.pack (show actual_occurences) <> " times."-            _ -> return ()+  where+    ok (AstMetrics metrics) (name, expected_occurences) =+      case M.lookup name metrics of+        Nothing+          | expected_occurences > 0 ->+            throwError $+              name <> " should have occurred " <> T.pack (show expected_occurences)+                <> " times, but did not occur at all in optimised program."+        Just actual_occurences+          | expected_occurences /= actual_occurences ->+            throwError $+              name <> " should have occurred " <> T.pack (show expected_occurences)+                <> " times, but occurred "+                <> T.pack (show actual_occurences)+                <> " times."+        _ -> return ()  testWarnings :: [WarningTest] -> SBS.ByteString -> TestM () testWarnings warnings futerr = accErrors_ $ map testWarning warnings-  where testWarning (ExpectedWarning regex_s regex)-          | not (match regex $ T.unpack $ T.decodeUtf8 futerr) =-            throwError $ "Expected warning:\n  " <> regex_s <>-            "\nGot warnings:\n  " <> T.decodeUtf8 futerr-          | otherwise = return ()+  where+    testWarning (ExpectedWarning regex_s regex)+      | not (match regex $ T.unpack $ T.decodeUtf8 futerr) =+        throwError $+          "Expected warning:\n  " <> regex_s+            <> "\nGot warnings:\n  "+            <> T.decodeUtf8 futerr+      | otherwise = return ()  runTestCase :: TestCase -> TestM () runTestCase (TestCase mode program testcase progs) = do   futhark <- io $ maybe getExecutablePath return $ configFuthark progs   case testAction testcase of-     CompileTimeFailure expected_error ->-      context (mconcat ["Type-checking with '", T.pack futhark,-                        " check ", T.pack program, "'"]) $ do-        (code, _, err) <--          io $ readProcessWithExitCode futhark ["check", program] ""-        case code of-         ExitSuccess -> throwError "Expected failure\n"-         ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark-         ExitFailure 1 -> throwError $ T.decodeUtf8 err-         ExitFailure _ -> checkError expected_error err--    RunCases{} | mode == TypeCheck -> do+      context+        ( mconcat+            [ "Type-checking with '",+              T.pack futhark,+              " check ",+              T.pack program,+              "'"+            ]+        )+        $ do+          (code, _, err) <-+            io $ readProcessWithExitCode futhark ["check", program] ""+          case code of+            ExitSuccess -> throwError "Expected failure\n"+            ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark+            ExitFailure 1 -> throwError $ T.decodeUtf8 err+            ExitFailure _ -> checkError expected_error err+    RunCases {} | mode == TypeCheck -> do       let options = ["check", program] ++ configExtraCompilerOptions progs-      context (mconcat ["Type-checking with '", T.pack futhark,-                        " check ", T.pack program, "'"]) $ do-        (code, _, err) <- io $ readProcessWithExitCode futhark options ""--        case code of-         ExitSuccess -> return ()-         ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark-         ExitFailure _ -> throwError $ T.decodeUtf8 err+      context+        ( mconcat+            [ "Type-checking with '",+              T.pack futhark,+              " check ",+              T.pack program,+              "'"+            ]+        )+        $ do+          (code, _, err) <- io $ readProcessWithExitCode futhark options "" +          case code of+            ExitSuccess -> return ()+            ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark+            ExitFailure _ -> throwError $ T.decodeUtf8 err     RunCases ios structures warnings -> do       -- Compile up-front and reuse same executable for several entry points.       let backend = configBackend progs           extra_options = configExtraCompilerOptions progs       unless (mode == Compile) $         context "Generating reference outputs" $-        -- We probably get the concurrency at the test program level,-        -- so force just one data set at a time here.-        ensureReferenceOutput (Just 1) futhark "c" program ios+          -- We probably get the concurrency at the test program level,+          -- so force just one data set at a time here.+          ensureReferenceOutput (Just 1) futhark "c" program ios       unless (mode == Interpreted) $         context ("Compiling with --backend=" <> T.pack backend) $ do           compileTestProgram extra_options futhark backend program warnings@@ -178,34 +206,40 @@           unless (mode == Compile) $ do             (tuning_opts, _) <-               liftIO $ determineTuning (configTuning progs) program-            let progs' = progs { configExtraOptions =-                                 tuning_opts ++ configExtraOptions progs }+            let progs' =+                  progs+                    { configExtraOptions =+                        tuning_opts ++ configExtraOptions progs+                    }             context "Running compiled program" $               accErrors_ $ map (runCompiledEntry program progs') ios       unless (mode == Compile || mode == Compiled) $         context "Interpreting" $           accErrors_ $ map (runInterpretedEntry futhark program) ios -runInterpretedEntry :: String -> FilePath -> InputOutputs -> TestM()+runInterpretedEntry :: String -> FilePath -> InputOutputs -> TestM () runInterpretedEntry futhark program (InputOutputs entry run_cases) =   let dir = takeDirectory program       runInterpretedCase run@(TestRun _ inputValues _ index _) =         unless ("compiled" `elem` runTags run) $-          context ("Entry point: " <> entry-                   <> "; dataset: " <> T.pack (runDescription run)) $ do--            input <- T.unlines . map prettyText <$> getValues dir inputValues-            expectedResult' <- getExpectedResult program entry run-            (code, output, err) <--              io $ readProcessWithExitCode futhark ["run", "-e", T.unpack entry, program] $-              T.encodeUtf8 input-            case code of-              ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark--              _               -> compareResult entry index program expectedResult'-                                 =<< runResult program code output err--  in accErrors_ $ map runInterpretedCase run_cases+          context+            ( "Entry point: " <> entry+                <> "; dataset: "+                <> T.pack (runDescription run)+            )+            $ do+              input <- T.unlines . map prettyText <$> getValues dir inputValues+              expectedResult' <- getExpectedResult program entry run+              (code, output, err) <-+                io $+                  readProcessWithExitCode futhark ["run", "-e", T.unpack entry, program] $+                    T.encodeUtf8 input+              case code of+                ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark+                _ ->+                  compareResult entry index program expectedResult'+                    =<< runResult program code output err+   in accErrors_ $ map runInterpretedCase run_cases  runCompiledEntry :: FilePath -> ProgConfig -> InputOutputs -> TestM () runCompiledEntry program progs (InputOutputs entry run_cases) =@@ -220,29 +254,34 @@       extra_options = configExtraOptions progs        runCompiledCase run@(TestRun _ inputValues _ index _) =-        context ("Entry point: " <> entry-                 <> "; dataset: " <> T.pack (runDescription run)) $ do-          expected <- getExpectedResult program entry run-          (progCode, output, progerr) <--            runProgram runner extra_options program entry inputValues-          compareResult entry index program expected-            =<< runResult program progCode output progerr--  in context ("Running " <> T.pack (unwords $ binpath : entry_options ++ extra_options)) $-     accErrors_ $ map runCompiledCase run_cases+        context+          ( "Entry point: " <> entry+              <> "; dataset: "+              <> T.pack (runDescription run)+          )+          $ do+            expected <- getExpectedResult program entry run+            (progCode, output, progerr) <-+              runProgram runner extra_options program entry inputValues+            compareResult entry index program expected+              =<< runResult program progCode output progerr+   in context ("Running " <> T.pack (unwords $ binpath : entry_options ++ extra_options)) $+        accErrors_ $ map runCompiledCase run_cases  checkError :: ExpectedError -> SBS.ByteString -> TestM () checkError (ThisError regex_s regex) err   | not (match regex $ T.unpack $ T.decodeUtf8 err) =-     throwError $ "Expected error:\n  " <> regex_s <>-     "\nGot error:\n  " <> T.decodeUtf8 err+    throwError $+      "Expected error:\n  " <> regex_s+        <> "\nGot error:\n  "+        <> T.decodeUtf8 err checkError _ _ =   return ()  runResult :: FilePath -> ExitCode -> SBS.ByteString -> SBS.ByteString -> TestM RunResult runResult program ExitSuccess stdout_s _ =   case valuesFromByteString "stdout" $ LBS.fromStrict stdout_s of-    Left e   -> do+    Left e -> do       let actualf = program `addExtension` "actual"       io $ SBS.writeFile actualf stdout_s       throwError $ T.pack e <> "\n(See " <> T.pack actualf <> ")"@@ -255,28 +294,41 @@   (_, futerr) <- compileProgram extra_options futhark backend program   testWarnings warnings futerr -compareResult :: T.Text -> Int -> FilePath -> ExpectedResult [Value] -> RunResult-              -> TestM ()-compareResult _ _ _ (Succeeds Nothing) SuccessResult{} =+compareResult ::+  T.Text ->+  Int ->+  FilePath ->+  ExpectedResult [Value] ->+  RunResult ->+  TestM ()+compareResult _ _ _ (Succeeds Nothing) SuccessResult {} =   return () compareResult entry index program (Succeeds (Just expectedResult)) (SuccessResult actualResult) =   case compareValues1 actualResult expectedResult of     Just mismatch -> do       let actualf = program <.> T.unpack entry <.> show index <.> "actual"           expectedf = program <.> T.unpack entry <.> show index <.> "expected"-      io $ SBS.writeFile actualf $-        T.encodeUtf8 $ T.unlines $ map prettyText actualResult-      io $ SBS.writeFile expectedf $-        T.encodeUtf8 $ T.unlines $ map prettyText expectedResult-      throwError $ T.pack actualf <> " and " <> T.pack expectedf <>-        " do not match:\n" <> T.pack (show mismatch) <> "\n"+      io $+        SBS.writeFile actualf $+          T.encodeUtf8 $ T.unlines $ map prettyText actualResult+      io $+        SBS.writeFile expectedf $+          T.encodeUtf8 $ T.unlines $ map prettyText expectedResult+      throwError $+        T.pack actualf <> " and " <> T.pack expectedf+          <> " do not match:\n"+          <> T.pack (show mismatch)+          <> "\n"     Nothing ->       return () compareResult _ _ _ (RunTimeFailure expectedError) (ErrorResult _ actualError) =   checkError expectedError actualError compareResult _ _ _ (Succeeds _) (ErrorResult code err) =-  throwError $ "Program failed with error code " <>-  T.pack (show code) <> " and stderr:\n  " <> T.decodeUtf8 err+  throwError $+    "Program failed with error code "+      <> T.pack (show code)+      <> " and stderr:\n  "+      <> T.decodeUtf8 err compareResult _ _ _ (RunTimeFailure f) (SuccessResult _) =   throwError $ "Program succeeded, but expected failure:\n  " <> T.pack (show f) @@ -284,21 +336,23 @@ --- Test manager --- -data TestStatus = TestStatus { testStatusRemain :: [TestCase]-                             , testStatusRun :: [TestCase]-                             , testStatusTotal :: Int-                             , testStatusFail :: Int-                             , testStatusPass :: Int-                             , testStatusRuns :: Int-                             , testStatusRunsRemain :: Int-                             , testStatusRunPass :: Int-                             , testStatusRunFail :: Int-                             }+data TestStatus = TestStatus+  { testStatusRemain :: [TestCase],+    testStatusRun :: [TestCase],+    testStatusTotal :: Int,+    testStatusFail :: Int,+    testStatusPass :: Int,+    testStatusRuns :: Int,+    testStatusRunsRemain :: Int,+    testStatusRunPass :: Int,+    testStatusRunFail :: Int+  }  catching :: IO TestResult -> IO TestResult catching m = m `catch` save-  where save :: SomeException -> IO TestResult-        save e = return $ Failure [T.pack $ show e]+  where+    save :: SomeException -> IO TestResult+    save e = return $ Failure [T.pack $ show e]  doTest :: TestCase -> IO TestResult doTest = catching . runTestM . runTestCase@@ -307,8 +361,9 @@ makeTestCase config mode (file, spec) =   TestCase mode file spec $ configPrograms config -data ReportMsg = TestStarted TestCase-               | TestDone TestCase TestResult+data ReportMsg+  = TestStarted TestCase+  | TestDone TestCase TestResult  runTest :: MVar TestCase -> MVar ReportMsg -> IO () runTest testmvar resmvar = forever $ do@@ -324,39 +379,42 @@ -- | Exclude those test cases that have tags we do not wish to run. excludeCases :: TestConfig -> TestCase -> TestCase excludeCases config tcase =-  tcase { testCaseTest = onTest $ testCaseTest tcase }-  where onTest (ProgramTest desc tags action) =-          ProgramTest desc tags $ onAction action-        onAction (RunCases ios stest wtest) =-          RunCases (map onIOs ios) stest wtest-        onAction action = action-        onIOs (InputOutputs entry runs) =-          InputOutputs entry $ filter (not . any excluded . runTags) runs-        excluded = (`elem` configExclude config) . T.pack+  tcase {testCaseTest = onTest $ testCaseTest tcase}+  where+    onTest (ProgramTest desc tags action) =+      ProgramTest desc tags $ onAction action+    onAction (RunCases ios stest wtest) =+      RunCases (map onIOs ios) stest wtest+    onAction action = action+    onIOs (InputOutputs entry runs) =+      InputOutputs entry $ filter (not . any excluded . runTags) runs+    excluded = (`elem` configExclude config) . T.pack  statusTable :: TestStatus -> String statusTable ts = buildTable rows 1-  where rows =-          [ [ mkEntry "", passed, failed, mkEntry "remaining"]-          , map mkEntry ["programs", passedProgs, failedProgs, remainProgs']-          , map mkEntry ["runs", passedRuns, failedRuns, remainRuns']-          ]-        passed       = ("passed", [SetColor Foreground Vivid Green])-        failed       = ("failed", [SetColor Foreground Vivid Red])-        passedProgs  = show $ testStatusPass ts-        failedProgs  = show $ testStatusFail ts-        totalProgs   = show $ testStatusTotal ts-        totalRuns    = show $ testStatusRuns ts-        passedRuns   = show $ testStatusRunPass ts-        failedRuns   = show $ testStatusRunFail ts-        remainProgs  = show . length $ testStatusRemain ts-        remainProgs' = remainProgs ++ "/" ++ totalProgs-        remainRuns   = show $ testStatusRunsRemain ts-        remainRuns'  = remainRuns ++ "/" ++ totalRuns+  where+    rows =+      [ [mkEntry "", passed, failed, mkEntry "remaining"],+        map mkEntry ["programs", passedProgs, failedProgs, remainProgs'],+        map mkEntry ["runs", passedRuns, failedRuns, remainRuns']+      ]+    passed = ("passed", [SetColor Foreground Vivid Green])+    failed = ("failed", [SetColor Foreground Vivid Red])+    passedProgs = show $ testStatusPass ts+    failedProgs = show $ testStatusFail ts+    totalProgs = show $ testStatusTotal ts+    totalRuns = show $ testStatusRuns ts+    passedRuns = show $ testStatusRunPass ts+    failedRuns = show $ testStatusRunFail ts+    remainProgs = show . length $ testStatusRemain ts+    remainProgs' = remainProgs ++ "/" ++ totalProgs+    remainRuns = show $ testStatusRunsRemain ts+    remainRuns' = remainRuns ++ "/" ++ totalRuns  tableLines :: Int tableLines = 1 + (length . lines $ blankTable)-  where blankTable = statusTable $ TestStatus [] [] 0 0 0 0 0 0 0+  where+    blankTable = statusTable $ TestStatus [] [] 0 0 0 0 0 0 0  spaceTable :: IO () spaceTable = putStr $ replicate tableLines '\n'@@ -367,23 +425,29 @@   putStrLn $ statusTable ts   clearLine   w <- maybe 80 Terminal.width <$> Terminal.size-  putStrLn $ atMostChars (w-length labelstr) running-  where running = labelstr ++ (unwords . reverse . map testCaseProgram . testStatusRun) ts-        labelstr = "Now testing: "+  putStrLn $ atMostChars (w - length labelstr) running+  where+    running = labelstr ++ (unwords . reverse . map testCaseProgram . testStatusRun) ts+    labelstr = "Now testing: "  moveCursorToTableTop :: IO () moveCursorToTableTop = cursorUpLine tableLines  atMostChars :: Int -> String -> String-atMostChars n s | length s > n = take (n-3) s ++ "..."-                | otherwise    = s+atMostChars n s+  | length s > n = take (n -3) s ++ "..."+  | otherwise = s  reportText :: TestStatus -> IO () reportText ts =-  putStr $ "(" ++ show (testStatusFail ts)  ++ " failed, " ++-                  show (testStatusPass ts)  ++ " passed, " ++-                  show num_remain           ++ " to go).\n"-    where num_remain  = length $ testStatusRemain ts+  putStr $+    "(" ++ show (testStatusFail ts) ++ " failed, "+      ++ show (testStatusPass ts)+      ++ " passed, "+      ++ show num_remain+      ++ " to go).\n"+  where+    num_remain = length $ testStatusRemain ts  runTests :: TestConfig -> [FilePath] -> IO () runTests config paths = do@@ -393,8 +457,9 @@   hSetBuffering stdout LineBuffering    let mode = configTestMode config-  all_tests <- map (makeTestCase config mode) <$>-               testSpecsFromPathsOrDie paths+  all_tests <-+    map (makeTestCase config mode)+      <$> testSpecsFromPathsOrDie paths   testmvar <- newEmptyMVar   reportmvar <- newEmptyMVar   concurrency <- maybe getNumCapabilities pure $ configConcurrency config@@ -405,16 +470,20 @@    let fancy = not (configLineOutput config) && fancyTerminal -      report | fancy = reportTable-             | otherwise = reportText-      clear | fancy = clearFromCursorToScreenEnd-            | otherwise = putStr "\n"+      report+        | fancy = reportTable+        | otherwise = reportText+      clear+        | fancy = clearFromCursorToScreenEnd+        | otherwise = putStr "\n"        numTestCases tc =         case testAction $ testCaseTest tc of           CompileTimeFailure _ -> 1-          RunCases ios sts wts -> (length . concat) (iosTestRuns <$> ios)-                                  + length sts + length wts+          RunCases ios sts wts ->+            (length . concat) (iosTestRuns <$> ios)+              + length sts+              + length wts        getResults ts         | null (testStatusRemain ts) = report ts >> return ts@@ -427,174 +496,222 @@                 putStr $ "Started testing " <> testCaseProgram test <> " "               getResults $ ts {testStatusRun = test : testStatusRun ts}             TestDone test res -> do-              let ts' = ts { testStatusRemain = test `delete` testStatusRemain ts-                           , testStatusRun    = test `delete` testStatusRun ts-                           , testStatusRunsRemain = testStatusRunsRemain ts-                                                    - numTestCases test-                           }+              let ts' =+                    ts+                      { testStatusRemain = test `delete` testStatusRemain ts,+                        testStatusRun = test `delete` testStatusRun ts,+                        testStatusRunsRemain =+                          testStatusRunsRemain ts+                            - numTestCases test+                      }               case res of                 Success -> do-                  let ts'' = ts' { testStatusRunPass =-                                     testStatusRunPass ts' + numTestCases test-                                 }+                  let ts'' =+                        ts'+                          { testStatusRunPass =+                              testStatusRunPass ts' + numTestCases test+                          }                   unless fancy $                     putStr $ "Finished testing " <> testCaseProgram test <> " "-                  getResults $ ts'' { testStatusPass = testStatusPass ts + 1}+                  getResults $ ts'' {testStatusPass = testStatusPass ts + 1}                 Failure s -> do                   when fancy moveCursorToTableTop                   clear                   T.putStr $ (T.pack (inRed $ testCaseProgram test) <> ":\n") <> T.unlines s                   when fancy spaceTable-                  getResults $ ts' { testStatusFail = testStatusFail ts' + 1-                                   , testStatusRunPass = testStatusRunPass ts'-                                                         + numTestCases test - length s--                                   , testStatusRunFail = testStatusRunFail ts'-                                                         + length s-                                   }+                  getResults $+                    ts'+                      { testStatusFail = testStatusFail ts' + 1,+                        testStatusRunPass =+                          testStatusRunPass ts'+                            + numTestCases test - length s,+                        testStatusRunFail =+                          testStatusRunFail ts'+                            + length s+                      }    when fancy spaceTable -  ts <- getResults TestStatus { testStatusRemain = included-                              , testStatusRun    = []-                              , testStatusTotal  = length included-                              , testStatusFail   = 0-                              , testStatusPass   = 0-                              , testStatusRuns  = sum $ map numTestCases included-                              , testStatusRunsRemain = sum $ map numTestCases included-                              , testStatusRunPass = 0-                              , testStatusRunFail = 0-                              }+  ts <-+    getResults+      TestStatus+        { testStatusRemain = included,+          testStatusRun = [],+          testStatusTotal = length included,+          testStatusFail = 0,+          testStatusPass = 0,+          testStatusRuns = sum $ map numTestCases included,+          testStatusRunsRemain = sum $ map numTestCases included,+          testStatusRunPass = 0,+          testStatusRunFail = 0+        }    -- Removes "Now testing" output.   when fancy $ cursorUpLine 1 >> clearLine -  let excluded_str | null excluded = ""-                   | otherwise = " (" ++ show (length excluded) ++ " program(s) excluded).\n"+  let excluded_str+        | null excluded = ""+        | otherwise = " (" ++ show (length excluded) ++ " program(s) excluded).\n"   putStr excluded_str-  exitWith $ case testStatusFail ts of 0 -> ExitSuccess-                                       _ -> ExitFailure 1+  exitWith $ case testStatusFail ts of+    0 -> ExitSuccess+    _ -> ExitFailure 1  --- --- Configuration and command line parsing ---  data TestConfig = TestConfig-                  { configTestMode :: TestMode-                  , configPrograms :: ProgConfig-                  , configExclude :: [T.Text]-                  , configLineOutput :: Bool-                  , configConcurrency :: Maybe Int-                  }+  { configTestMode :: TestMode,+    configPrograms :: ProgConfig,+    configExclude :: [T.Text],+    configLineOutput :: Bool,+    configConcurrency :: Maybe Int+  }  defaultConfig :: TestConfig-defaultConfig = TestConfig { configTestMode = Everything-                           , configExclude = [ "disable" ]-                           , configPrograms =-                             ProgConfig-                             { configBackend = "c"-                             , configFuthark = Nothing-                             , configRunner = ""-                             , configExtraOptions = []-                             , configExtraCompilerOptions = []-                             , configTuning = Just "tuning"-                             }-                           , configLineOutput = False-                           , configConcurrency = Nothing-                           }+defaultConfig =+  TestConfig+    { configTestMode = Everything,+      configExclude = ["disable"],+      configPrograms =+        ProgConfig+          { configBackend = "c",+            configFuthark = Nothing,+            configRunner = "",+            configExtraOptions = [],+            configExtraCompilerOptions = [],+            configTuning = Just "tuning"+          },+      configLineOutput = False,+      configConcurrency = Nothing+    }  data ProgConfig = ProgConfig-                  { configBackend :: String-                  , configFuthark :: Maybe FilePath-                  , configRunner :: FilePath-                  , configExtraCompilerOptions :: [String]-                  , configTuning :: Maybe String-                  , configExtraOptions :: [String]-                  -- ^ Extra options passed to the programs being run.-                  }-                  deriving (Show)+  { configBackend :: String,+    configFuthark :: Maybe FilePath,+    configRunner :: FilePath,+    configExtraCompilerOptions :: [String],+    configTuning :: Maybe String,+    -- | Extra options passed to the programs being run.+    configExtraOptions :: [String]+  }+  deriving (Show)  changeProgConfig :: (ProgConfig -> ProgConfig) -> TestConfig -> TestConfig-changeProgConfig f config = config { configPrograms = f $ configPrograms config }+changeProgConfig f config = config {configPrograms = f $ configPrograms config}  setBackend :: FilePath -> ProgConfig -> ProgConfig setBackend backend config =-  config { configBackend = backend }+  config {configBackend = backend}  setFuthark :: FilePath -> ProgConfig -> ProgConfig setFuthark futhark config =-  config { configFuthark = Just futhark }+  config {configFuthark = Just futhark}  setRunner :: FilePath -> ProgConfig -> ProgConfig setRunner runner config =-  config { configRunner = runner }+  config {configRunner = runner}  addCompilerOption :: String -> ProgConfig -> ProgConfig addCompilerOption option config =-  config { configExtraCompilerOptions = configExtraCompilerOptions config ++ [option] }+  config {configExtraCompilerOptions = configExtraCompilerOptions config ++ [option]}  addOption :: String -> ProgConfig -> ProgConfig addOption option config =-  config { configExtraOptions = configExtraOptions config ++ [option] }+  config {configExtraOptions = configExtraOptions config ++ [option]} -data TestMode = TypeCheck-              | Compile-              | Compiled-              | Interpreted-              | Everything-              deriving (Eq, Show)+data TestMode+  = TypeCheck+  | Compile+  | Compiled+  | Interpreted+  | Everything+  deriving (Eq, Show)  commandLineOptions :: [FunOptDescr TestConfig]-commandLineOptions = [-    Option "t" ["typecheck"]-    (NoArg $ Right $ \config -> config { configTestMode = TypeCheck })-    "Only perform type-checking"-  , Option "i" ["interpreted"]-    (NoArg $ Right $ \config -> config { configTestMode = Interpreted })-    "Only interpret"-  , Option "c" ["compiled"]-    (NoArg $ Right $ \config -> config { configTestMode = Compiled })-    "Only run compiled code"-  , Option "C" ["compile"]-    (NoArg $ Right $ \config -> config { configTestMode = Compile })-    "Only compile, do not run."-  , Option [] ["no-terminal", "notty"]-    (NoArg $ Right $ \config -> config { configLineOutput = True })-    "Provide simpler line-based output."-  , Option [] ["backend"]-    (ReqArg (Right . changeProgConfig . setBackend) "BACKEND")-    "Backend used for compilation (defaults to 'c')."-  , Option [] ["futhark"]-    (ReqArg (Right . changeProgConfig . setFuthark) "PROGRAM")-    "Program to run for subcommands (defaults to same binary as 'futhark test')."-  , Option [] ["runner"]-    (ReqArg (Right . changeProgConfig . setRunner) "PROGRAM")-    "The program used to run the Futhark-generated programs (defaults to nothing)."-  , Option [] ["exclude"]-    (ReqArg (\tag ->-               Right $ \config ->-               config { configExclude = T.pack tag : configExclude config })-     "TAG")-    "Exclude test programs that define this tag."-  , Option "p" ["pass-option"]-    (ReqArg (Right . changeProgConfig . addOption) "OPT")-    "Pass this option to programs being run."-  , Option [] ["pass-compiler-option"]-    (ReqArg (Right . changeProgConfig . addCompilerOption) "OPT")-    "Pass this option to the compiler (or typechecker if in -t mode)."-  , Option [] ["no-tuning"]-    (NoArg $ Right $ changeProgConfig $ \config -> config { configTuning = Nothing })-    "Do not load tuning files."-  , Option [] ["concurrency"]-    (ReqArg (\n ->-               case reads n of-                 [(n', "")]-                   | n' > 0 ->-                   Right $ \config -> config { configConcurrency = Just n' }-                 _ ->-                   Left $ error $ "'" ++ n ++ "' is not a positive integer.")-    "NUM")-    "Number of tests to run concurrently."+commandLineOptions =+  [ Option+      "t"+      ["typecheck"]+      (NoArg $ Right $ \config -> config {configTestMode = TypeCheck})+      "Only perform type-checking",+    Option+      "i"+      ["interpreted"]+      (NoArg $ Right $ \config -> config {configTestMode = Interpreted})+      "Only interpret",+    Option+      "c"+      ["compiled"]+      (NoArg $ Right $ \config -> config {configTestMode = Compiled})+      "Only run compiled code",+    Option+      "C"+      ["compile"]+      (NoArg $ Right $ \config -> config {configTestMode = Compile})+      "Only compile, do not run.",+    Option+      []+      ["no-terminal", "notty"]+      (NoArg $ Right $ \config -> config {configLineOutput = True})+      "Provide simpler line-based output.",+    Option+      []+      ["backend"]+      (ReqArg (Right . changeProgConfig . setBackend) "BACKEND")+      "Backend used for compilation (defaults to 'c').",+    Option+      []+      ["futhark"]+      (ReqArg (Right . changeProgConfig . setFuthark) "PROGRAM")+      "Program to run for subcommands (defaults to same binary as 'futhark test').",+    Option+      []+      ["runner"]+      (ReqArg (Right . changeProgConfig . setRunner) "PROGRAM")+      "The program used to run the Futhark-generated programs (defaults to nothing).",+    Option+      []+      ["exclude"]+      ( ReqArg+          ( \tag ->+              Right $ \config ->+                config {configExclude = T.pack tag : configExclude config}+          )+          "TAG"+      )+      "Exclude test programs that define this tag.",+    Option+      "p"+      ["pass-option"]+      (ReqArg (Right . changeProgConfig . addOption) "OPT")+      "Pass this option to programs being run.",+    Option+      []+      ["pass-compiler-option"]+      (ReqArg (Right . changeProgConfig . addCompilerOption) "OPT")+      "Pass this option to the compiler (or typechecker if in -t mode).",+    Option+      []+      ["no-tuning"]+      (NoArg $ Right $ changeProgConfig $ \config -> config {configTuning = Nothing})+      "Do not load tuning files.",+    Option+      []+      ["concurrency"]+      ( ReqArg+          ( \n ->+              case reads n of+                [(n', "")]+                  | n' > 0 ->+                    Right $ \config -> config {configConcurrency = Just n'}+                _ ->+                  Left $ error $ "'" ++ n ++ "' is not a positive integer."+          )+          "NUM"+      )+      "Number of tests to run concurrently."   ]  -- | Run @futhark test@.
src/Futhark/CodeGen/Backends/CCUDA.hs view
@@ -1,27 +1,31 @@ {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TupleSections #-}+ -- | Code generation for CUDA. module Futhark.CodeGen.Backends.CCUDA-  ( compileProg-  , GC.CParts(..)-  , GC.asLibrary-  , GC.asExecutable-  ) where+  ( compileProg,+    GC.CParts (..),+    GC.asLibrary,+    GC.asExecutable,+  )+where  import Control.Monad import Data.List (intercalate) import Data.Maybe (catMaybes)-import qualified Language.C.Quote.OpenCL as C-+import Futhark.CodeGen.Backends.CCUDA.Boilerplate+import Futhark.CodeGen.Backends.COpenCL.Boilerplate (commonOptions) import qualified Futhark.CodeGen.Backends.GenericC as GC+import Futhark.CodeGen.Backends.GenericC.Options+import Futhark.CodeGen.ImpCode.OpenCL import qualified Futhark.CodeGen.ImpGen.CUDA as ImpGen-import Futhark.IR.KernelsMem-  hiding (GetSize, CmpSizeLe, GetSizeMax)+import Futhark.IR.KernelsMem hiding+  ( CmpSizeLe,+    GetSize,+    GetSizeMax,+  ) import Futhark.MonadFreshNames-import Futhark.CodeGen.ImpCode.OpenCL-import Futhark.CodeGen.Backends.COpenCL.Boilerplate (commonOptions)-import Futhark.CodeGen.Backends.CCUDA.Boilerplate-import Futhark.CodeGen.Backends.GenericC.Options+import qualified Language.C.Quote.OpenCL as C  -- | Compile the program to C with calls to CUDA. compileProg :: MonadFreshNames m => Prog KernelsMem -> m (ImpGen.Warnings, GC.CParts)@@ -29,73 +33,111 @@   (ws, Program cuda_code cuda_prelude kernels _ sizes failures prog') <-     ImpGen.compileProg prog   let cost_centres =-        [copyDevToDev, copyDevToHost, copyHostToDev,-         copyScalarToDev, copyScalarFromDev]-      extra = generateBoilerplate cuda_code cuda_prelude-              cost_centres kernels sizes failures-  (ws,) <$>-    GC.compileProg "cuda" operations extra cuda_includes-    [Space "device", DefaultSpace] cliOptions prog'+        [ copyDevToDev,+          copyDevToHost,+          copyHostToDev,+          copyScalarToDev,+          copyScalarFromDev+        ]+      extra =+        generateBoilerplate+          cuda_code+          cuda_prelude+          cost_centres+          kernels+          sizes+          failures+  (ws,)+    <$> GC.compileProg+      "cuda"+      operations+      extra+      cuda_includes+      [Space "device", DefaultSpace]+      cliOptions+      prog'   where     operations :: GC.Operations OpenCL ()-    operations = GC.defaultOperations-                 { GC.opsWriteScalar = writeCUDAScalar-                 , GC.opsReadScalar  = readCUDAScalar-                 , GC.opsAllocate    = allocateCUDABuffer-                 , GC.opsDeallocate  = deallocateCUDABuffer-                 , GC.opsCopy        = copyCUDAMemory-                 , GC.opsStaticArray = staticCUDAArray-                 , GC.opsMemoryType  = cudaMemoryType-                 , GC.opsCompiler    = callKernel-                 , GC.opsFatMemory   = True-                 }-    cuda_includes = unlines [ "#include <cuda.h>"-                            , "#include <cuda_runtime.h>"-                            , "#include <nvrtc.h>"-                            ]+    operations =+      GC.defaultOperations+        { GC.opsWriteScalar = writeCUDAScalar,+          GC.opsReadScalar = readCUDAScalar,+          GC.opsAllocate = allocateCUDABuffer,+          GC.opsDeallocate = deallocateCUDABuffer,+          GC.opsCopy = copyCUDAMemory,+          GC.opsStaticArray = staticCUDAArray,+          GC.opsMemoryType = cudaMemoryType,+          GC.opsCompiler = callKernel,+          GC.opsFatMemory = True,+          GC.opsCritical =+            ( [C.citems|CUDA_SUCCEED(cuCtxPushCurrent(ctx->cuda.cu_ctx));|],+              [C.citems|CUDA_SUCCEED(cuCtxPopCurrent(&ctx->cuda.cu_ctx));|]+            )+        }+    cuda_includes =+      unlines+        [ "#include <cuda.h>",+          "#include <cuda_runtime.h>",+          "#include <nvrtc.h>"+        ]  cliOptions :: [Option] cliOptions =-  commonOptions ++-  [ Option { optionLongName = "dump-cuda"-           , optionShortName = Nothing-           , optionArgument = RequiredArgument "FILE"-           , optionAction = [C.cstm|{futhark_context_config_dump_program_to(cfg, optarg);+  commonOptions+    ++ [ Option+           { optionLongName = "dump-cuda",+             optionShortName = Nothing,+             optionArgument = RequiredArgument "FILE",+             optionDescription = "Dump the embedded CUDA kernels to the indicated file.",+             optionAction =+               [C.cstm|{futhark_context_config_dump_program_to(cfg, optarg);                                      entry_point = NULL;}|]-           }-  , Option { optionLongName = "load-cuda"-           , optionShortName = Nothing-           , optionArgument = RequiredArgument "FILE"-           , optionAction = [C.cstm|futhark_context_config_load_program_from(cfg, optarg);|]-           }-  , Option { optionLongName = "dump-ptx"-           , optionShortName = Nothing-           , optionArgument = RequiredArgument "FILE"-           , optionAction = [C.cstm|{futhark_context_config_dump_ptx_to(cfg, optarg);+           },+         Option+           { optionLongName = "load-cuda",+             optionShortName = Nothing,+             optionArgument = RequiredArgument "FILE",+             optionDescription = "Instead of using the embedded CUDA kernels, load them from the indicated file.",+             optionAction = [C.cstm|futhark_context_config_load_program_from(cfg, optarg);|]+           },+         Option+           { optionLongName = "dump-ptx",+             optionShortName = Nothing,+             optionArgument = RequiredArgument "FILE",+             optionDescription = "Dump the PTX-compiled version of the embedded kernels to the indicated file.",+             optionAction =+               [C.cstm|{futhark_context_config_dump_ptx_to(cfg, optarg);                                      entry_point = NULL;}|]-           }-  , Option { optionLongName = "load-ptx"-           , optionShortName = Nothing-           , optionArgument = RequiredArgument "FILE"-           , optionAction = [C.cstm|futhark_context_config_load_ptx_from(cfg, optarg);|]-           }-  , Option { optionLongName = "nvrtc-option"-           , optionShortName = Nothing-           , optionArgument = RequiredArgument "OPT"-           , optionAction = [C.cstm|futhark_context_config_add_nvrtc_option(cfg, optarg);|]-           }-  , Option { optionLongName = "profile"-           , optionShortName = Just 'P'-           , optionArgument = NoArgument-           , optionAction = [C.cstm|futhark_context_config_set_profiling(cfg, 1);|]+           },+         Option+           { optionLongName = "load-ptx",+             optionShortName = Nothing,+             optionArgument = RequiredArgument "FILE",+             optionDescription = "Load PTX code from the indicated file.",+             optionAction = [C.cstm|futhark_context_config_load_ptx_from(cfg, optarg);|]+           },+         Option+           { optionLongName = "nvrtc-option",+             optionShortName = Nothing,+             optionArgument = RequiredArgument "OPT",+             optionDescription = "Add an additional build option to the string passed to NVRTC.",+             optionAction = [C.cstm|futhark_context_config_add_nvrtc_option(cfg, optarg);|]+           },+         Option+           { optionLongName = "profile",+             optionShortName = Just 'P',+             optionArgument = NoArgument,+             optionDescription = "Gather profiling data while executing and print out a summary at the end.",+             optionAction = [C.cstm|futhark_context_config_set_profiling(cfg, 1);|]            }-  ]+       ]  writeCUDAScalar :: GC.WriteScalar OpenCL () writeCUDAScalar mem idx t "device" _ val = do   val' <- newVName "write_tmp"   let (bef, aft) = profilingEnclosure copyScalarToDev-  GC.item [C.citem|{$ty:t $id:val' = $exp:val;+  GC.item+    [C.citem|{$ty:t $id:val' = $exp:val;                   $items:bef                   CUDA_SUCCEED(                     cuMemcpyHtoD($exp:mem + $exp:idx * sizeof($ty:t),@@ -110,7 +152,8 @@ readCUDAScalar mem idx t "device" _ = do   val <- newVName "read_res"   let (bef, aft) = profilingEnclosure copyScalarFromDev-  mapM_ GC.item+  mapM_+    GC.item     [C.citems|        $ty:t $id:val;        {@@ -143,7 +186,8 @@ copyCUDAMemory dstmem dstidx dstSpace srcmem srcidx srcSpace nbytes = do   let (fn, prof) = memcpyFun dstSpace srcSpace       (bef, aft) = profilingEnclosure prof-  GC.item [C.citem|{+  GC.item+    [C.citem|{                 $items:bef                 CUDA_SUCCEED(                   $id:fn($exp:dstmem + $exp:dstidx,@@ -153,11 +197,15 @@                 }                 |]   where-    memcpyFun DefaultSpace (Space "device")     = ("cuMemcpyDtoH", copyDevToHost)-    memcpyFun (Space "device") DefaultSpace     = ("cuMemcpyHtoD", copyHostToDev)-    memcpyFun (Space "device") (Space "device") = ("cuMemcpy",     copyDevToDev)-    memcpyFun _ _ = error $ "Cannot copy to '" ++ show dstSpace ++-                    "' from '" ++ show srcSpace ++ "'."+    memcpyFun DefaultSpace (Space "device") = ("cuMemcpyDtoH", copyDevToHost)+    memcpyFun (Space "device") DefaultSpace = ("cuMemcpyHtoD", copyHostToDev)+    memcpyFun (Space "device") (Space "device") = ("cuMemcpy", copyDevToDev)+    memcpyFun _ _ =+      error $+        "Cannot copy to '" ++ show dstSpace+          ++ "' from '"+          ++ show srcSpace+          ++ "'."  staticCUDAArray :: GC.StaticArray OpenCL () staticCUDAArray name "device" t vs = do@@ -174,7 +222,8 @@   -- Fake a memory block.   GC.contextField (C.toIdent name mempty) [C.cty|struct memblock_device|] Nothing   -- During startup, copy the data to where we need it.-  GC.atInit [C.cstm|{+  GC.atInit+    [C.cstm|{     ctx->$id:name.references = NULL;     ctx->$id:name.size = 0;     CUDA_SUCCEED(cuMemAlloc(&ctx->$id:name.mem,@@ -186,8 +235,9 @@   }|]   GC.item [C.citem|struct memblock_device $id:name = ctx->$id:name;|] staticCUDAArray _ space _ _ =-  error $ "CUDA backend cannot create static array in '" ++ space-          ++ "' memory space"+  error $+    "CUDA backend cannot create static array in '" ++ space+      ++ "' memory space"  cudaMemoryType :: GC.MemoryType OpenCL () cudaMemoryType "device" = return [C.cty|typename CUdeviceptr|]@@ -202,9 +252,9 @@   GC.stm [C.cstm|$id:v = ctx->sizes.$id:key <= $exp:x';|] callKernel (GetSizeMax v size_class) =   let field = "max_" ++ cudaSizeClass size_class-  in GC.stm [C.cstm|$id:v = ctx->cuda.$id:field;|]+   in GC.stm [C.cstm|$id:v = ctx->cuda.$id:field;|]   where-    cudaSizeClass SizeThreshold{} = "threshold"+    cudaSizeClass SizeThreshold {} = "threshold"     cudaSizeClass SizeGroup = "block_size"     cudaSizeClass SizeNumGroups = "grid_size"     cudaSizeClass SizeTile = "tile_size"@@ -219,24 +269,35 @@       shared_offsets_sc = mkOffsets shared_sizes       shared_args = zip shared_offsets shared_offsets_sc       shared_tot = last shared_offsets_sc-  forM_ shared_args $ \(arg,offset) ->+  forM_ shared_args $ \(arg, offset) ->     GC.decl [C.cdecl|unsigned int $id:arg = $exp:offset;|]    (grid_x, grid_y, grid_z) <- mkDims <$> mapM GC.compileExp num_blocks   (block_x, block_y, block_z) <- mkDims <$> mapM GC.compileExp block_size   let perm_args-        | length num_blocks == 3 = [ [C.cinit|&perm[0]|], [C.cinit|&perm[1]|], [C.cinit|&perm[2]|] ]+        | length num_blocks == 3 = [[C.cinit|&perm[0]|], [C.cinit|&perm[1]|], [C.cinit|&perm[2]|]]         | otherwise = []-      failure_args = take (numFailureParams safety)-                     [[C.cinit|&ctx->global_failure|],-                      [C.cinit|&ctx->failure_is_an_option|],-                      [C.cinit|&ctx->global_failure_args|]]-      args'' = perm_args ++ failure_args ++ [ [C.cinit|&$id:a|] | a <- args' ]-      sizes_nonzero = expsNotZero [grid_x, grid_y, grid_z,-                      block_x, block_y, block_z]+      failure_args =+        take+          (numFailureParams safety)+          [ [C.cinit|&ctx->global_failure|],+            [C.cinit|&ctx->failure_is_an_option|],+            [C.cinit|&ctx->global_failure_args|]+          ]+      args'' = perm_args ++ failure_args ++ [[C.cinit|&$id:a|] | a <- args']+      sizes_nonzero =+        expsNotZero+          [ grid_x,+            grid_y,+            grid_z,+            block_x,+            block_y,+            block_z+          ]       (bef, aft) = profilingEnclosure kernel_name -  GC.stm [C.cstm|+  GC.stm+    [C.cstm|     if ($exp:sizes_nonzero) {       int perm[3] = { 0, 1, 2 }; @@ -283,12 +344,11 @@    when (safety >= SafetyFull) $     GC.stm [C.cstm|ctx->failure_is_an_option = 1;|]-   where-    mkDims [] = ([C.cexp|0|] , [C.cexp|0|], [C.cexp|0|])+    mkDims [] = ([C.cexp|0|], [C.cexp|0|], [C.cexp|0|])     mkDims [x] = (x, [C.cexp|1|], [C.cexp|1|])-    mkDims [x,y] = (x, y, [C.cexp|1|])-    mkDims (x:y:z:_) = (x, y, z)+    mkDims [x, y] = (x, y, [C.cexp|1|])+    mkDims (x : y : z : _) = (x, y, z)     addExp x y = [C.cexp|$exp:x + $exp:y|]     alignExp e = [C.cexp|$exp:e + ((8 - ($exp:e % 8)) % 8)|]     mkOffsets = scanl (\a b -> a `addExp` alignExp b) [C.cexp|0|]
src/Futhark/CodeGen/Backends/CCUDA/Boilerplate.hs view
@@ -1,27 +1,33 @@ {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-}+ -- | Various boilerplate definitions for the CUDA backend. module Futhark.CodeGen.Backends.CCUDA.Boilerplate-  (-    generateBoilerplate-  , profilingEnclosure-  , module Futhark.CodeGen.Backends.COpenCL.Boilerplate-  ) where--import qualified Language.C.Syntax as C-import qualified Language.C.Quote.OpenCL as C+  ( generateBoilerplate,+    profilingEnclosure,+    module Futhark.CodeGen.Backends.COpenCL.Boilerplate,+  )+where +import Data.FileEmbed (embedStringFile)+import qualified Data.Map as M+import Data.Maybe+import Futhark.CodeGen.Backends.COpenCL.Boilerplate+  ( copyDevToDev,+    copyDevToHost,+    copyHostToDev,+    copyScalarFromDev,+    copyScalarToDev,+    costCentreReport,+    failureSwitch,+    kernelRuns,+    kernelRuntime,+  ) import qualified Futhark.CodeGen.Backends.GenericC as GC import Futhark.CodeGen.ImpCode.OpenCL-import Futhark.CodeGen.Backends.COpenCL.Boilerplate-  (failureSwitch, kernelRuntime, kernelRuns, costCentreReport,-   copyDevToDev, copyDevToHost, copyHostToDev,-   copyScalarToDev, copyScalarFromDev) import Futhark.Util (chunk, zEncodeString)--import qualified Data.Map as M-import Data.Maybe-import Data.FileEmbed (embedStringFile)+import qualified Language.C.Quote.OpenCL as C+import qualified Language.C.Syntax as C  errorMsgNumArgs :: ErrorMsg a -> Int errorMsgNumArgs = length . errorMsgArgTypes@@ -29,7 +35,7 @@ -- | Block items to put before and after a thing to be profiled. profilingEnclosure :: Name -> ([C.BlockItem], [C.BlockItem]) profilingEnclosure name =-  ([C.citems|+  ( [C.citems|       typename cudaEvent_t *pevents = NULL;       if (ctx->profiling && !ctx->profiling_paused) {         pevents = cuda_get_events(&ctx->cuda,@@ -38,20 +44,27 @@         CUDA_SUCCEED(cudaEventRecord(pevents[0], 0));       }       |],-   [C.citems|+    [C.citems|       if (pevents != NULL) {         CUDA_SUCCEED(cudaEventRecord(pevents[1], 0));       }-      |])+      |]+  )  -- | Called after most code has been generated to generate the bulk of -- the boilerplate.-generateBoilerplate :: String -> String -> [Name] -> M.Map KernelName KernelSafety-                    -> M.Map Name SizeClass-                    -> [FailureMsg]-                    -> GC.CompilerM OpenCL () ()+generateBoilerplate ::+  String ->+  String ->+  [Name] ->+  M.Map KernelName KernelSafety ->+  M.Map Name SizeClass ->+  [FailureMsg] ->+  GC.CompilerM OpenCL () () generateBoilerplate cuda_program cuda_prelude cost_centres kernels sizes failures = do-  mapM_ GC.earlyDecl [C.cunit|+  mapM_+    GC.earlyDecl+    [C.cunit|       $esc:("#include <cuda.h>")       $esc:("#include <nvrtc.h>")       $esc:("typedef CUdeviceptr fl_mem_t;")@@ -66,12 +79,12 @@    GC.profileReport [C.citem|CUDA_SUCCEED(cuda_tally_profiling_records(&ctx->cuda));|]   mapM_ GC.profileReport $ costCentreReport $ cost_centres ++ M.keys kernels-   where     cuda_h = $(embedStringFile "rts/c/cuda.h")     free_list_h = $(embedStringFile "rts/c/free_list.h")-    fragments = map (\s -> [C.cinit|$string:s|])-                  $ chunk 2000 (cuda_prelude ++ cuda_program)+    fragments =+      map (\s -> [C.cinit|$string:s|]) $+        chunk 2000 (cuda_prelude ++ cuda_program)  generateSizeFuns :: M.Map Name SizeClass -> GC.CompilerM OpenCL () () generateSizeFuns sizes = do@@ -85,22 +98,25 @@   GC.earlyDecl [C.cedecl|static const char *size_classes[] = { $inits:size_class_inits };|]    GC.publicDef_ "get_num_sizes" GC.InitDecl $ \s ->-    ([C.cedecl|int $id:s(void);|],-     [C.cedecl|int $id:s(void) {+    ( [C.cedecl|int $id:s(void);|],+      [C.cedecl|int $id:s(void) {                 return $int:num_sizes;-              }|])+              }|]+    )    GC.publicDef_ "get_size_name" GC.InitDecl $ \s ->-    ([C.cedecl|const char* $id:s(int);|],-     [C.cedecl|const char* $id:s(int i) {+    ( [C.cedecl|const char* $id:s(int);|],+      [C.cedecl|const char* $id:s(int i) {                 return size_names[i];-              }|])+              }|]+    )    GC.publicDef_ "get_size_class" GC.InitDecl $ \s ->-    ([C.cedecl|const char* $id:s(int);|],-     [C.cedecl|const char* $id:s(int i) {+    ( [C.cedecl|const char* $id:s(int);|],+      [C.cedecl|const char* $id:s(int i) {                 return size_classes[i];-              }|])+              }|]+    )  generateConfigFuns :: M.Map Name SizeClass -> GC.CompilerM OpenCL () String generateConfigFuns sizes = do@@ -108,20 +124,22 @@       num_sizes = M.size sizes   GC.earlyDecl [C.cedecl|struct sizes { $sdecls:size_decls };|]   cfg <- GC.publicDef "context_config" GC.InitDecl $ \s ->-    ([C.cedecl|struct $id:s;|],-     [C.cedecl|struct $id:s { struct cuda_config cu_cfg;+    ( [C.cedecl|struct $id:s;|],+      [C.cedecl|struct $id:s { struct cuda_config cu_cfg;                               int profiling;                               size_t sizes[$int:num_sizes];                               int num_nvrtc_opts;                               const char **nvrtc_opts;-                            };|])+                            };|]+    ) -  let size_value_inits = zipWith sizeInit [0..M.size sizes-1] (M.elems sizes)+  let size_value_inits = zipWith sizeInit [0 .. M.size sizes -1] (M.elems sizes)       sizeInit i size = [C.cstm|cfg->sizes[$int:i] = $int:val;|]-         where val = fromMaybe 0 $ sizeDefault size+        where+          val = fromMaybe 0 $ sizeDefault size   GC.publicDef_ "context_config_new" GC.InitDecl $ \s ->-    ([C.cedecl|struct $id:cfg* $id:s(void);|],-     [C.cedecl|struct $id:cfg* $id:s(void) {+    ( [C.cedecl|struct $id:cfg* $id:s(void);|],+      [C.cedecl|struct $id:cfg* $id:s(void) {                          struct $id:cfg *cfg = (struct $id:cfg*) malloc(sizeof(struct $id:cfg));                          if (cfg == NULL) {                            return NULL;@@ -136,102 +154,117 @@                                           size_names, size_vars,                                           cfg->sizes, size_classes);                          return cfg;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_free" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg) {                          free(cfg->nvrtc_opts);                          free(cfg);-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_add_nvrtc_option" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *opt);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *opt) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *opt);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *opt) {                          cfg->nvrtc_opts[cfg->num_nvrtc_opts] = opt;                          cfg->num_nvrtc_opts++;                          cfg->nvrtc_opts = (const char**) realloc(cfg->nvrtc_opts, (cfg->num_nvrtc_opts+1) * sizeof(const char*));                          cfg->nvrtc_opts[cfg->num_nvrtc_opts] = NULL;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_debugging" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) {                          cfg->cu_cfg.logging = cfg->cu_cfg.debugging = flag;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_profiling" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) {                          cfg->profiling = flag;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_logging" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) {                          cfg->cu_cfg.logging = flag;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_device" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *s);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *s) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *s);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *s) {                          set_preferred_device(&cfg->cu_cfg, s);-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_dump_program_to" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {                          cfg->cu_cfg.dump_program_to = path;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_load_program_from" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {                          cfg->cu_cfg.load_program_from = path;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_dump_ptx_to" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {                           cfg->cu_cfg.dump_ptx_to = path;-                      }|])+                      }|]+    )    GC.publicDef_ "context_config_load_ptx_from" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {                           cfg->cu_cfg.load_ptx_from = path;-                      }|])+                      }|]+    )    GC.publicDef_ "context_config_set_default_group_size" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, int size);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int size);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) {                          cfg->cu_cfg.default_block_size = size;                          cfg->cu_cfg.default_block_size_changed = 1;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_default_num_groups" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, int num) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, int num) {                          cfg->cu_cfg.default_grid_size = num;                          cfg->cu_cfg.default_grid_size_changed = 1;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_default_tile_size" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) {                          cfg->cu_cfg.default_tile_size = size;                          cfg->cu_cfg.default_tile_size_changed = 1;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_default_threshold" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) {                          cfg->cu_cfg.default_threshold = size;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_size" GC.InitDecl $ \s ->-    ([C.cedecl|int $id:s(struct $id:cfg* cfg, const char *size_name, size_t size_value);|],-     [C.cedecl|int $id:s(struct $id:cfg* cfg, const char *size_name, size_t size_value) {+    ( [C.cedecl|int $id:s(struct $id:cfg* cfg, const char *size_name, size_t size_value);|],+      [C.cedecl|int $id:s(struct $id:cfg* cfg, const char *size_name, size_t size_value) {                           for (int i = 0; i < $int:num_sizes; i++) {                            if (strcmp(size_name, size_names[i]) == 0) {@@ -260,38 +293,46 @@                            return 0;                          }                          return 1;-                       }|])+                       }|]+    )   return cfg -generateContextFuns :: String -> [Name] -> M.Map KernelName KernelSafety-                    -> M.Map Name SizeClass-                    -> [FailureMsg]-                    -> GC.CompilerM OpenCL () ()+generateContextFuns ::+  String ->+  [Name] ->+  M.Map KernelName KernelSafety ->+  M.Map Name SizeClass ->+  [FailureMsg] ->+  GC.CompilerM OpenCL () () generateContextFuns cfg cost_centres kernels sizes failures = do   final_inits <- GC.contextFinalInits   (fields, init_fields) <- GC.contextContents   let forCostCentre name =-        [([C.csdecl|typename int64_t $id:(kernelRuntime name);|],-          [C.cstm|ctx->$id:(kernelRuntime name) = 0;|]),-         ([C.csdecl|int $id:(kernelRuns name);|],-          [C.cstm|ctx->$id:(kernelRuns name) = 0;|])]+        [ ( [C.csdecl|typename int64_t $id:(kernelRuntime name);|],+            [C.cstm|ctx->$id:(kernelRuntime name) = 0;|]+          ),+          ( [C.csdecl|int $id:(kernelRuns name);|],+            [C.cstm|ctx->$id:(kernelRuns name) = 0;|]+          )+        ]        forKernel name =-        ([C.csdecl|typename CUfunction $id:name;|],-         [C.cstm|CUDA_SUCCEED(cuModuleGetFunction(+        ( [C.csdecl|typename CUfunction $id:name;|],+          [C.cstm|CUDA_SUCCEED(cuModuleGetFunction(                                 &ctx->$id:name,                                 ctx->cuda.module,-                                $string:(pretty (C.toIdent name mempty))));|])-        : forCostCentre name+                                $string:(pretty (C.toIdent name mempty))));|]+        ) :+        forCostCentre name        (kernel_fields, init_kernel_fields) =         unzip $-        concatMap forKernel (M.keys kernels) ++-        concatMap forCostCentre cost_centres+          concatMap forKernel (M.keys kernels)+            ++ concatMap forCostCentre cost_centres    ctx <- GC.publicDef "context" GC.InitDecl $ \s ->-    ([C.cedecl|struct $id:s;|],-     [C.cedecl|struct $id:s {+    ( [C.cedecl|struct $id:s;|],+      [C.cedecl|struct $id:s {                          int detail_memory;                          int debugging;                          int profiling;@@ -309,16 +350,20 @@                           int total_runs;                          long int total_runtime;-                       };|])+                       };|]+    ) -  let set_sizes = zipWith (\i k -> [C.cstm|ctx->sizes.$id:k = cfg->sizes[$int:i];|])-                          [(0::Int)..] $ M.keys sizes+  let set_sizes =+        zipWith+          (\i k -> [C.cstm|ctx->sizes.$id:k = cfg->sizes[$int:i];|])+          [(0 :: Int) ..]+          $ M.keys sizes       max_failure_args =         foldl max 0 $ map (errorMsgNumArgs . failureError) failures    GC.publicDef_ "context_new" GC.InitDecl $ \s ->-    ([C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg);|],-     [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg) {+    ( [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg);|],+      [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg) {                  struct $id:ctx* ctx = (struct $id:ctx*) malloc(sizeof(struct $id:ctx));                  if (ctx == NULL) {                    return NULL;@@ -347,7 +392,7 @@                  CUDA_SUCCEED(cuMemAlloc(&ctx->global_failure, sizeof(no_error)));                  CUDA_SUCCEED(cuMemcpyHtoD(ctx->global_failure, &no_error, sizeof(no_error)));                  // The +1 is to avoid zero-byte allocations.-                 CUDA_SUCCEED(cuMemAlloc(&ctx->global_failure_args, sizeof(int32_t)*($int:max_failure_args+1)));+                 CUDA_SUCCEED(cuMemAlloc(&ctx->global_failure_args, sizeof(int64_t)*($int:max_failure_args+1)));                   $stms:init_kernel_fields @@ -361,20 +406,23 @@                  futhark_context_sync(ctx);                   return ctx;-               }|])+               }|]+    )    GC.publicDef_ "context_free" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:ctx* ctx);|],-     [C.cedecl|void $id:s(struct $id:ctx* ctx) {+    ( [C.cedecl|void $id:s(struct $id:ctx* ctx);|],+      [C.cedecl|void $id:s(struct $id:ctx* ctx) {                                  free_constants(ctx);                                  cuda_cleanup(&ctx->cuda);                                  free_lock(&ctx->lock);                                  free(ctx);-                               }|])+                               }|]+    )    GC.publicDef_ "context_sync" GC.MiscDecl $ \s ->-    ([C.cedecl|int $id:s(struct $id:ctx* ctx);|],-     [C.cedecl|int $id:s(struct $id:ctx* ctx) {+    ( [C.cedecl|int $id:s(struct $id:ctx* ctx);|],+      [C.cedecl|int $id:s(struct $id:ctx* ctx) {+                 CUDA_SUCCEED(cuCtxPushCurrent(ctx->cuda.cu_ctx));                  CUDA_SUCCEED(cuCtxSynchronize());                  if (ctx->failure_is_an_option) {                    // Check for any delayed error.@@ -394,7 +442,7 @@                                     &no_failure,                                     sizeof(int32_t))); -                     typename int32_t args[$int:max_failure_args+1];+                     typename int64_t args[$int:max_failure_args+1];                      CUDA_SUCCEED(                        cuMemcpyDtoH(&args,                                     ctx->global_failure_args,@@ -405,13 +453,15 @@                      return 1;                    }                  }+                 CUDA_SUCCEED(cuCtxPopCurrent(&ctx->cuda.cu_ctx));                  return 0;-               }|])-+               }|]+    )    GC.publicDef_ "context_clear_caches" GC.MiscDecl $ \s ->-    ([C.cedecl|int $id:s(struct $id:ctx* ctx);|],-     [C.cedecl|int $id:s(struct $id:ctx* ctx) {+    ( [C.cedecl|int $id:s(struct $id:ctx* ctx);|],+      [C.cedecl|int $id:s(struct $id:ctx* ctx) {                          CUDA_SUCCEED(cuda_free_all(&ctx->cuda));                          return 0;-                       }|])+                       }|]+    )
src/Futhark/CodeGen/Backends/COpenCL.hs view
@@ -1,105 +1,162 @@-{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TupleSections #-}+ -- | Code generation for C with OpenCL. module Futhark.CodeGen.Backends.COpenCL-  ( compileProg-  , GC.CParts(..)-  , GC.asLibrary-  , GC.asExecutable-  ) where+  ( compileProg,+    GC.CParts (..),+    GC.asLibrary,+    GC.asExecutable,+  )+where  import Control.Monad hiding (mapM) import Data.List (intercalate)--import qualified Language.C.Syntax as C-import qualified Language.C.Quote.OpenCL as C--import Futhark.IR.KernelsMem-  hiding (GetSize, CmpSizeLe, GetSizeMax) import Futhark.CodeGen.Backends.COpenCL.Boilerplate import qualified Futhark.CodeGen.Backends.GenericC as GC import Futhark.CodeGen.Backends.GenericC.Options import Futhark.CodeGen.ImpCode.OpenCL import qualified Futhark.CodeGen.ImpGen.OpenCL as ImpGen+import Futhark.IR.KernelsMem hiding+  ( CmpSizeLe,+    GetSize,+    GetSizeMax,+  ) import Futhark.MonadFreshNames+import qualified Language.C.Quote.OpenCL as C+import qualified Language.C.Syntax as C  -- | Compile the program to C with calls to OpenCL. compileProg :: MonadFreshNames m => Prog KernelsMem -> m (ImpGen.Warnings, GC.CParts) compileProg prog = do-  (ws, Program opencl_code opencl_prelude kernels-       types sizes failures prog') <- ImpGen.compileProg prog+  ( ws,+    Program+      opencl_code+      opencl_prelude+      kernels+      types+      sizes+      failures+      prog'+    ) <-+    ImpGen.compileProg prog   let cost_centres =-        [copyDevToDev, copyDevToHost, copyHostToDev,-         copyScalarToDev, copyScalarFromDev]-  (ws,) <$>-    GC.compileProg "opencl" operations-    (generateBoilerplate opencl_code opencl_prelude-     cost_centres kernels types sizes failures)-    include_opencl_h [Space "device", DefaultSpace]-    cliOptions prog'-  where operations :: GC.Operations OpenCL ()-        operations = GC.defaultOperations-                     { GC.opsCompiler = callKernel-                     , GC.opsWriteScalar = writeOpenCLScalar-                     , GC.opsReadScalar = readOpenCLScalar-                     , GC.opsAllocate = allocateOpenCLBuffer-                     , GC.opsDeallocate = deallocateOpenCLBuffer-                     , GC.opsCopy = copyOpenCLMemory-                     , GC.opsStaticArray = staticOpenCLArray-                     , GC.opsMemoryType = openclMemoryType-                     , GC.opsFatMemory = True-                     }-        include_opencl_h = unlines ["#define CL_TARGET_OPENCL_VERSION 120",-                                    "#define CL_USE_DEPRECATED_OPENCL_1_2_APIS",-                                    "#ifdef __APPLE__",-                                    "#define CL_SILENCE_DEPRECATION",-                                    "#include <OpenCL/cl.h>",-                                    "#else",-                                    "#include <CL/cl.h>",-                                    "#endif"]+        [ copyDevToDev,+          copyDevToHost,+          copyHostToDev,+          copyScalarToDev,+          copyScalarFromDev+        ]+  (ws,)+    <$> GC.compileProg+      "opencl"+      operations+      ( generateBoilerplate+          opencl_code+          opencl_prelude+          cost_centres+          kernels+          types+          sizes+          failures+      )+      include_opencl_h+      [Space "device", DefaultSpace]+      cliOptions+      prog'+  where+    operations :: GC.Operations OpenCL ()+    operations =+      GC.defaultOperations+        { GC.opsCompiler = callKernel,+          GC.opsWriteScalar = writeOpenCLScalar,+          GC.opsReadScalar = readOpenCLScalar,+          GC.opsAllocate = allocateOpenCLBuffer,+          GC.opsDeallocate = deallocateOpenCLBuffer,+          GC.opsCopy = copyOpenCLMemory,+          GC.opsStaticArray = staticOpenCLArray,+          GC.opsMemoryType = openclMemoryType,+          GC.opsFatMemory = True+        }+    include_opencl_h =+      unlines+        [ "#define CL_TARGET_OPENCL_VERSION 120",+          "#define CL_USE_DEPRECATED_OPENCL_1_2_APIS",+          "#ifdef __APPLE__",+          "#define CL_SILENCE_DEPRECATION",+          "#include <OpenCL/cl.h>",+          "#else",+          "#include <CL/cl.h>",+          "#endif"+        ]  cliOptions :: [Option] cliOptions =-  commonOptions ++-  [ Option { optionLongName = "platform"-           , optionShortName = Just 'p'-           , optionArgument = RequiredArgument "NAME"-           , optionAction = [C.cstm|futhark_context_config_set_platform(cfg, optarg);|]-           }-  , Option { optionLongName = "dump-opencl"-           , optionShortName = Nothing-           , optionArgument = RequiredArgument "FILE"-           , optionAction = [C.cstm|{futhark_context_config_dump_program_to(cfg, optarg);+  commonOptions+    ++ [ Option+           { optionLongName = "platform",+             optionShortName = Just 'p',+             optionArgument = RequiredArgument "NAME",+             optionDescription = "Use the first OpenCL platform whose name contains the given string.",+             optionAction = [C.cstm|futhark_context_config_set_platform(cfg, optarg);|]+           },+         Option+           { optionLongName = "dump-opencl",+             optionShortName = Nothing,+             optionArgument = RequiredArgument "FILE",+             optionDescription = "Dump the embedded OpenCL program to the indicated file.",+             optionAction =+               [C.cstm|{futhark_context_config_dump_program_to(cfg, optarg);                                      entry_point = NULL;}|]-           }-  , Option { optionLongName = "load-opencl"-           , optionShortName = Nothing-           , optionArgument = RequiredArgument "FILE"-           , optionAction = [C.cstm|futhark_context_config_load_program_from(cfg, optarg);|]-           }-  , Option { optionLongName = "dump-opencl-binary"-           , optionShortName = Nothing-           , optionArgument = RequiredArgument "FILE"-           , optionAction = [C.cstm|{futhark_context_config_dump_binary_to(cfg, optarg);+           },+         Option+           { optionLongName = "load-opencl",+             optionShortName = Nothing,+             optionArgument = RequiredArgument "FILE",+             optionDescription = "Instead of using the embedded OpenCL program, load it from the indicated file.",+             optionAction = [C.cstm|futhark_context_config_load_program_from(cfg, optarg);|]+           },+         Option+           { optionLongName = "dump-opencl-binary",+             optionShortName = Nothing,+             optionArgument = RequiredArgument "FILE",+             optionDescription = "Dump the compiled version of the embedded OpenCL program to the indicated file.",+             optionAction =+               [C.cstm|{futhark_context_config_dump_binary_to(cfg, optarg);                                      entry_point = NULL;}|]-           }-  , Option { optionLongName = "load-opencl-binary"-           , optionShortName = Nothing-           , optionArgument = RequiredArgument "FILE"-           , optionAction = [C.cstm|futhark_context_config_load_binary_from(cfg, optarg);|]-           }-  , Option { optionLongName = "build-option"-           , optionShortName = Nothing-           , optionArgument = RequiredArgument "OPT"-           , optionAction = [C.cstm|futhark_context_config_add_build_option(cfg, optarg);|]-           }-  , Option { optionLongName = "profile"-           , optionShortName = Just 'P'-           , optionArgument = NoArgument-           , optionAction = [C.cstm|futhark_context_config_set_profiling(cfg, 1);|]+           },+         Option+           { optionLongName = "load-opencl-binary",+             optionShortName = Nothing,+             optionArgument = RequiredArgument "FILE",+             optionDescription = "Load an OpenCL binary from the indicated file.",+             optionAction = [C.cstm|futhark_context_config_load_binary_from(cfg, optarg);|]+           },+         Option+           { optionLongName = "build-option",+             optionShortName = Nothing,+             optionArgument = RequiredArgument "OPT",+             optionDescription = "Add an additional build option to the string passed to clBuildProgram().",+             optionAction = [C.cstm|futhark_context_config_add_build_option(cfg, optarg);|]+           },+         Option+           { optionLongName = "profile",+             optionShortName = Just 'P',+             optionArgument = NoArgument,+             optionDescription = "Gather profiling data while executing and print out a summary at the end.",+             optionAction = [C.cstm|futhark_context_config_set_profiling(cfg, 1);|]+           },+         Option+           { optionLongName = "list-devices",+             optionShortName = Nothing,+             optionArgument = NoArgument,+             optionDescription = "List all OpenCL devices and platforms available on the system.",+             optionAction =+               [C.cstm|{futhark_context_config_list_devices(cfg);+                        entry_point = NULL;}|]            }-  ]+       ]  -- We detect the special case of writing a constant and turn it into a -- non-blocking write.  This may be slightly faster, as it prevents@@ -112,9 +169,10 @@   val' <- newVName "write_tmp"   let (decl, blocking) =         case val of-          C.Const{} -> ([C.citem|static $ty:t $id:val' = $exp:val;|], [C.cexp|CL_FALSE|])-          _         -> ([C.citem|$ty:t $id:val' = $exp:val;|], [C.cexp|CL_TRUE|])-  GC.stm [C.cstm|{$item:decl+          C.Const {} -> ([C.citem|static $ty:t $id:val' = $exp:val;|], [C.cexp|CL_FALSE|])+          _ -> ([C.citem|$ty:t $id:val' = $exp:val;|], [C.cexp|CL_TRUE|])+  GC.stm+    [C.cstm|{$item:decl                   OPENCL_SUCCEED_OR_RETURN(                     clEnqueueWriteBuffer(ctx->opencl.queue, $exp:mem, $exp:blocking,                                          $exp:i * sizeof($ty:t), sizeof($ty:t),@@ -132,14 +190,16 @@ readOpenCLScalar mem i t "device" _ = do   val <- newVName "read_res"   GC.decl [C.cdecl|$ty:t $id:val;|]-  GC.stm [C.cstm|OPENCL_SUCCEED_OR_RETURN(+  GC.stm+    [C.cstm|OPENCL_SUCCEED_OR_RETURN(                    clEnqueueReadBuffer(ctx->opencl.queue, $exp:mem,                                        ctx->failure_is_an_option ? CL_FALSE : CL_TRUE,                                        $exp:i * sizeof($ty:t), sizeof($ty:t),                                        &$id:val,                                        0, NULL, $exp:(profilingEvent copyScalarFromDev)));               |]-  GC.stm [C.cstm|if (ctx->failure_is_an_option &&+  GC.stm+    [C.cstm|if (ctx->failure_is_an_option &&                      futhark_context_sync(ctx) != 0) { return 1; }|]   return [C.cexp|$id:val|] readOpenCLScalar _ _ _ space _ =@@ -162,7 +222,8 @@ -- out of bounds, even if asked to read zero bytes.  We protect with a -- branch to avoid this. copyOpenCLMemory destmem destidx DefaultSpace srcmem srcidx (Space "device") nbytes =-  GC.stm [C.cstm|+  GC.stm+    [C.cstm|     if ($exp:nbytes > 0) {       OPENCL_SUCCEED_OR_RETURN(         clEnqueueReadBuffer(ctx->opencl.queue, $exp:srcmem,@@ -175,7 +236,8 @@    }   |] copyOpenCLMemory destmem destidx (Space "device") srcmem srcidx DefaultSpace nbytes =-  GC.stm [C.cstm|+  GC.stm+    [C.cstm|     if ($exp:nbytes > 0) {       OPENCL_SUCCEED_OR_RETURN(         clEnqueueWriteBuffer(ctx->opencl.queue, $exp:destmem, CL_TRUE,@@ -187,7 +249,8 @@ copyOpenCLMemory destmem destidx (Space "device") srcmem srcidx (Space "device") nbytes =   -- Be aware that OpenCL swaps the usual order of operands for   -- memcpy()-like functions.  The order below is not a typo.-  GC.stm [C.cstm|{+  GC.stm+    [C.cstm|{     if ($exp:nbytes > 0) {       OPENCL_SUCCEED_OR_RETURN(         clEnqueueCopyBuffer(ctx->opencl.queue,@@ -225,7 +288,8 @@   -- Fake a memory block.   GC.contextField (C.toIdent name mempty) [C.cty|struct memblock_device|] Nothing   -- During startup, copy the data to where we need it.-  GC.atInit [C.cstm|{+  GC.atInit+    [C.cstm|{     typename cl_int success;     ctx->$id:name.references = NULL;     ctx->$id:name.size = 0;@@ -243,7 +307,6 @@     }   }|]   GC.item [C.citem|struct memblock_device $id:name = ctx->$id:name;|]- staticOpenCLArray _ space _ _ =   error $ "OpenCL backend cannot create static array in memory space '" ++ space ++ "'" @@ -253,25 +316,25 @@ callKernel (CmpSizeLe v key x) = do   x' <- GC.compileExp x   GC.stm [C.cstm|$id:v = ctx->sizes.$id:key <= $exp:x';|]-  GC.stm [C.cstm|if (ctx->logging) {+  GC.stm+    [C.cstm|if (ctx->logging) {     fprintf(stderr, "Compared %s <= %d.\n", $string:(pretty key), $exp:x');     }|] callKernel (GetSizeMax v size_class) =   let field = "max_" ++ pretty size_class-  in GC.stm [C.cstm|$id:v = ctx->opencl.$id:field;|]-+   in GC.stm [C.cstm|$id:v = ctx->opencl.$id:field;|] callKernel (LaunchKernel safety name args num_workgroups workgroup_size) = do-   -- The other failure args are set automatically when the kernel is   -- first created.   when (safety == SafetyFull) $-    GC.stm [C.cstm|+    GC.stm+      [C.cstm|       OPENCL_SUCCEED_OR_RETURN(clSetKernelArg(ctx->$id:name, 1,                                               sizeof(ctx->failure_is_an_option),                                               &ctx->failure_is_an_option));     |] -  zipWithM_ setKernelArg [numFailureParams safety..] args+  zipWithM_ setKernelArg [numFailureParams safety ..] args   num_workgroups' <- mapM GC.compileExp num_workgroups   workgroup_size' <- mapM GC.compileExp workgroup_size   local_bytes <- foldM localBytes [C.cexp|0|] args@@ -280,32 +343,38 @@    when (safety >= SafetyFull) $     GC.stm [C.cstm|ctx->failure_is_an_option = 1;|]--  where setKernelArg i (ValueKArg e bt) = do-          v <- GC.compileExpToName "kernel_arg" bt e-          GC.stm [C.cstm|+  where+    setKernelArg i (ValueKArg e bt) = do+      v <- GC.compileExpToName "kernel_arg" bt e+      GC.stm+        [C.cstm|             OPENCL_SUCCEED_OR_RETURN(clSetKernelArg(ctx->$id:name, $int:i, sizeof($id:v), &$id:v));           |]--        setKernelArg i (MemKArg v) = do-          v' <- GC.rawMem v-          GC.stm [C.cstm|+    setKernelArg i (MemKArg v) = do+      v' <- GC.rawMem v+      GC.stm+        [C.cstm|             OPENCL_SUCCEED_OR_RETURN(clSetKernelArg(ctx->$id:name, $int:i, sizeof($exp:v'), &$exp:v'));           |]--        setKernelArg i (SharedMemoryKArg num_bytes) = do-          num_bytes' <- GC.compileExp $ unCount num_bytes-          GC.stm [C.cstm|+    setKernelArg i (SharedMemoryKArg num_bytes) = do+      num_bytes' <- GC.compileExp $ unCount num_bytes+      GC.stm+        [C.cstm|             OPENCL_SUCCEED_OR_RETURN(clSetKernelArg(ctx->$id:name, $int:i, $exp:num_bytes', NULL));             |] -        localBytes cur (SharedMemoryKArg num_bytes) = do-          num_bytes' <- GC.compileExp $ unCount num_bytes-          return [C.cexp|$exp:cur + $exp:num_bytes'|]-        localBytes cur _ = return cur+    localBytes cur (SharedMemoryKArg num_bytes) = do+      num_bytes' <- GC.compileExp $ unCount num_bytes+      return [C.cexp|$exp:cur + $exp:num_bytes'|]+    localBytes cur _ = return cur -launchKernel :: C.ToExp a =>-                KernelName -> [a] -> [a] -> a -> GC.CompilerM op s ()+launchKernel ::+  C.ToExp a =>+  KernelName ->+  [a] ->+  [a] ->+  a ->+  GC.CompilerM op s () launchKernel kernel_name num_workgroups workgroup_dims local_bytes = do   global_work_size <- newVName "global_work_size"   time_start <- newVName "time_start"@@ -313,7 +382,8 @@   time_diff <- newVName "time_diff"   local_work_size <- newVName "local_work_size" -  GC.stm [C.cstm|+  GC.stm+    [C.cstm|     if ($exp:total_elements != 0) {       const size_t $id:global_work_size[$int:kernel_rank] = {$inits:kernel_dims'};       const size_t $id:local_work_size[$int:kernel_rank] = {$inits:workgroup_dims'};@@ -338,18 +408,20 @@                 $string:(pretty kernel_name), $id:time_diff);       }     }|]-  where kernel_rank = length kernel_dims-        kernel_dims = zipWith multExp num_workgroups workgroup_dims-        kernel_dims' = map toInit kernel_dims-        workgroup_dims' = map toInit workgroup_dims-        total_elements = foldl multExp [C.cexp|1|] kernel_dims+  where+    kernel_rank = length kernel_dims+    kernel_dims = zipWith multExp (map toSize num_workgroups) (map toSize workgroup_dims)+    kernel_dims' = map toInit kernel_dims+    workgroup_dims' = map toInit workgroup_dims+    total_elements = foldl multExp [C.cexp|1|] kernel_dims -        toInit e = [C.cinit|$exp:e|]-        multExp x y = [C.cexp|$exp:x * $exp:y|]+    toInit e = [C.cinit|$exp:e|]+    multExp x y = [C.cexp|$exp:x * $exp:y|]+    toSize e = [C.cexp|(size_t)$exp:e|] -        printKernelSize :: VName -> [C.Stm]-        printKernelSize work_size =-          intercalate [[C.cstm|fprintf(stderr, ", ");|]] $-          map (printKernelDim work_size) [0..kernel_rank-1]-        printKernelDim global_work_size i =-          [[C.cstm|fprintf(stderr, "%zu", $id:global_work_size[$int:i]);|]]+    printKernelSize :: VName -> [C.Stm]+    printKernelSize work_size =+      intercalate [[C.cstm|fprintf(stderr, ", ");|]] $+        map (printKernelDim work_size) [0 .. kernel_rank -1]+    printKernelDim global_work_size i =+      [[C.cstm|fprintf(stderr, "%zu", $id:global_work_size[$int:i]);|]]
src/Futhark/CodeGen/Backends/COpenCL/Boilerplate.hs view
@@ -1,49 +1,55 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE FlexibleContexts #-}-module Futhark.CodeGen.Backends.COpenCL.Boilerplate-  ( generateBoilerplate-  , profilingEvent-  , copyDevToDev, copyDevToHost, copyHostToDev, copyScalarToDev, copyScalarFromDev -  , commonOptions-  , failureSwitch-  , costCentreReport-  , kernelRuntime-  , kernelRuns-  ) where+module Futhark.CodeGen.Backends.COpenCL.Boilerplate+  ( generateBoilerplate,+    profilingEvent,+    copyDevToDev,+    copyDevToHost,+    copyHostToDev,+    copyScalarToDev,+    copyScalarFromDev,+    commonOptions,+    failureSwitch,+    costCentreReport,+    kernelRuntime,+    kernelRuns,+  )+where  import Control.Monad.State import Data.FileEmbed import qualified Data.Map as M import Data.Maybe-import qualified Language.C.Syntax as C-import qualified Language.C.Quote.OpenCL as C--import Futhark.CodeGen.ImpCode.OpenCL import qualified Futhark.CodeGen.Backends.GenericC as GC import Futhark.CodeGen.Backends.GenericC.Options+import Futhark.CodeGen.ImpCode.OpenCL import Futhark.CodeGen.OpenCL.Heuristics import Futhark.Util (chunk, zEncodeString)+import qualified Language.C.Quote.OpenCL as C+import qualified Language.C.Syntax as C  errorMsgNumArgs :: ErrorMsg a -> Int errorMsgNumArgs = length . errorMsgArgTypes  failureSwitch :: [FailureMsg] -> C.Stm failureSwitch failures =-  let printfEscape = let escapeChar '%' = "%%"-                         escapeChar c = [c]-                     in concatMap escapeChar+  let printfEscape =+        let escapeChar '%' = "%%"+            escapeChar c = [c]+         in concatMap escapeChar       onPart (ErrorString s) = printfEscape s-      onPart ErrorInt32{} = "%d"+      onPart ErrorInt32 {} = "%lld"+      onPart ErrorInt64 {} = "%lld"       onFailure i (FailureMsg emsg@(ErrorMsg parts) backtrace) =-         let msg = concatMap onPart parts ++ "\n" ++ printfEscape backtrace-             msgargs = [ [C.cexp|args[$int:j]|] | j <- [0..errorMsgNumArgs emsg-1] ]+        let msg = concatMap onPart parts ++ "\n" ++ printfEscape backtrace+            msgargs = [[C.cexp|args[$int:j]|] | j <- [0 .. errorMsgNumArgs emsg -1]]          in [C.cstm|case $int:i: {ctx->error = msgprintf($string:msg, $args:msgargs); break;}|]       failure_cases =-        zipWith onFailure [(0::Int)..] failures-  in [C.cstm|switch (failure_idx) { $stms:failure_cases }|]+        zipWith onFailure [(0 :: Int) ..] failures+   in [C.cstm|switch (failure_idx) { $stms:failure_cases }|]  copyDevToDev, copyDevToHost, copyHostToDev, copyScalarToDev, copyScalarFromDev :: Name copyDevToDev = "copy_dev_to_dev"@@ -61,12 +67,15 @@  -- | Called after most code has been generated to generate the bulk of -- the boilerplate.-generateBoilerplate :: String -> String -> [Name]-                    -> M.Map KernelName KernelSafety-                    -> [PrimType]-                    -> M.Map Name SizeClass-                    -> [FailureMsg]-                    -> GC.CompilerM OpenCL () ()+generateBoilerplate ::+  String ->+  String ->+  [Name] ->+  M.Map KernelName KernelSafety ->+  [PrimType] ->+  M.Map Name SizeClass ->+  [FailureMsg] ->+  GC.CompilerM OpenCL () () generateBoilerplate opencl_code opencl_prelude cost_centres kernels types sizes failures = do   final_inits <- GC.contextFinalInits @@ -85,39 +94,44 @@   GC.earlyDecl [C.cedecl|static const char *size_classes[] = { $inits:size_class_inits };|]    GC.publicDef_ "get_num_sizes" GC.InitDecl $ \s ->-    ([C.cedecl|int $id:s(void);|],-     [C.cedecl|int $id:s(void) {+    ( [C.cedecl|int $id:s(void);|],+      [C.cedecl|int $id:s(void) {                 return $int:num_sizes;-              }|])+              }|]+    )    GC.publicDef_ "get_size_name" GC.InitDecl $ \s ->-    ([C.cedecl|const char* $id:s(int);|],-     [C.cedecl|const char* $id:s(int i) {+    ( [C.cedecl|const char* $id:s(int);|],+      [C.cedecl|const char* $id:s(int i) {                 return size_names[i];-              }|])+              }|]+    )    GC.publicDef_ "get_size_class" GC.InitDecl $ \s ->-    ([C.cedecl|const char* $id:s(int);|],-     [C.cedecl|const char* $id:s(int i) {+    ( [C.cedecl|const char* $id:s(int);|],+      [C.cedecl|const char* $id:s(int i) {                 return size_classes[i];-              }|])+              }|]+    )    let size_decls = map (\k -> [C.csdecl|size_t $id:k;|]) $ M.keys sizes   GC.earlyDecl [C.cedecl|struct sizes { $sdecls:size_decls };|]   cfg <- GC.publicDef "context_config" GC.InitDecl $ \s ->-    ([C.cedecl|struct $id:s;|],-     [C.cedecl|struct $id:s { struct opencl_config opencl;+    ( [C.cedecl|struct $id:s;|],+      [C.cedecl|struct $id:s { struct opencl_config opencl;                               size_t sizes[$int:num_sizes];                               int num_build_opts;                               const char **build_opts;-                            };|])+                            };|]+    ) -  let size_value_inits = zipWith sizeInit [0..M.size sizes-1] (M.elems sizes)+  let size_value_inits = zipWith sizeInit [0 .. M.size sizes -1] (M.elems sizes)       sizeInit i size = [C.cstm|cfg->sizes[$int:i] = $int:val;|]-         where val = fromMaybe 0 $ sizeDefault size+        where+          val = fromMaybe 0 $ sizeDefault size   GC.publicDef_ "context_config_new" GC.InitDecl $ \s ->-    ([C.cedecl|struct $id:cfg* $id:s(void);|],-     [C.cedecl|struct $id:cfg* $id:s(void) {+    ( [C.cedecl|struct $id:cfg* $id:s(void);|],+      [C.cedecl|struct $id:cfg* $id:s(void) {                          struct $id:cfg *cfg = (struct $id:cfg*) malloc(sizeof(struct $id:cfg));                          if (cfg == NULL) {                            return NULL;@@ -131,114 +145,137 @@                                             size_names, size_vars,                                             cfg->sizes, size_classes);                          return cfg;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_free" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg) {                          free(cfg->build_opts);                          free(cfg);-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_add_build_option" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *opt);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *opt) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *opt);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *opt) {                          cfg->build_opts[cfg->num_build_opts] = opt;                          cfg->num_build_opts++;                          cfg->build_opts = (const char**) realloc(cfg->build_opts, (cfg->num_build_opts+1) * sizeof(const char*));                          cfg->build_opts[cfg->num_build_opts] = NULL;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_debugging" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) {                          cfg->opencl.profiling = cfg->opencl.logging = cfg->opencl.debugging = flag;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_profiling" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) {                          cfg->opencl.profiling = flag;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_logging" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) {                          cfg->opencl.logging = flag;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_device" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *s);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *s) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *s);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *s) {                          set_preferred_device(&cfg->opencl, s);-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_platform" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *s);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *s) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *s);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *s) {                          set_preferred_platform(&cfg->opencl, s);-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_select_device_interactively" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg) {                          select_device_interactively(&cfg->opencl);-                       }|])+                       }|]+    ) +  GC.publicDef_ "context_config_list_devices" GC.InitDecl $ \s ->+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg) {+                         list_devices(&cfg->opencl);+                       }|]+    )+   GC.publicDef_ "context_config_dump_program_to" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {                          cfg->opencl.dump_program_to = path;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_load_program_from" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {                          cfg->opencl.load_program_from = path;-                       }|])-+                       }|]+    )    GC.publicDef_ "context_config_dump_binary_to" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {                          cfg->opencl.dump_binary_to = path;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_load_binary_from" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) {                          cfg->opencl.load_binary_from = path;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_default_group_size" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, int size);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int size);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) {                          cfg->opencl.default_group_size = size;                          cfg->opencl.default_group_size_changed = 1;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_default_num_groups" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, int num) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, int num) {                          cfg->opencl.default_num_groups = num;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_default_tile_size" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) {                          cfg->opencl.default_tile_size = size;                          cfg->opencl.default_tile_size_changed = 1;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_default_threshold" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|],-     [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) {+    ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|],+      [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) {                          cfg->opencl.default_threshold = size;-                       }|])+                       }|]+    )    GC.publicDef_ "context_config_set_size" GC.InitDecl $ \s ->-    ([C.cedecl|int $id:s(struct $id:cfg* cfg, const char *size_name, size_t size_value);|],-     [C.cedecl|int $id:s(struct $id:cfg* cfg, const char *size_name, size_t size_value) {+    ( [C.cedecl|int $id:s(struct $id:cfg* cfg, const char *size_name, size_t size_value);|],+      [C.cedecl|int $id:s(struct $id:cfg* cfg, const char *size_name, size_t size_value) {                           for (int i = 0; i < $int:num_sizes; i++) {                            if (strcmp(size_name, size_names[i]) == 0) {@@ -268,12 +305,13 @@                          }                           return 1;-                       }|])+                       }|]+    )    (fields, init_fields) <- GC.contextContents   ctx <- GC.publicDef "context" GC.InitDecl $ \s ->-    ([C.cedecl|struct $id:s;|],-     [C.cedecl|struct $id:s {+    ( [C.cedecl|struct $id:s;|],+      [C.cedecl|struct $id:s {                          int detail_memory;                          int debugging;                          int profiling;@@ -289,11 +327,13 @@                          struct sizes sizes;                          // True if a potentially failing kernel has been enqueued.                          typename cl_int failure_is_an_option;-                       };|])+                       };|]+    )    mapM_ GC.earlyDecl later_top_decls -  GC.earlyDecl [C.cedecl|static void init_context_early(struct $id:cfg *cfg, struct $id:ctx* ctx) {+  GC.earlyDecl+    [C.cedecl|static void init_context_early(struct $id:cfg *cfg, struct $id:ctx* ctx) {                      ctx->opencl.cfg = cfg->opencl;                      ctx->detail_memory = cfg->opencl.debugging;                      ctx->debugging = cfg->opencl.debugging;@@ -313,12 +353,16 @@                      $stms:ctx_opencl_inits   }|] -  let set_sizes = zipWith (\i k -> [C.cstm|ctx->sizes.$id:k = cfg->sizes[$int:i];|])-                          [(0::Int)..] $ M.keys sizes+  let set_sizes =+        zipWith+          (\i k -> [C.cstm|ctx->sizes.$id:k = cfg->sizes[$int:i];|])+          [(0 :: Int) ..]+          $ M.keys sizes       max_failure_args =         foldl max 0 $ map (errorMsgNumArgs . failureError) failures -  GC.earlyDecl [C.cedecl|static int init_context_late(struct $id:cfg *cfg, struct $id:ctx* ctx, typename cl_program prog) {+  GC.earlyDecl+    [C.cedecl|static int init_context_late(struct $id:cfg *cfg, struct $id:ctx* ctx, typename cl_program prog) {                      typename cl_int error;                       typename cl_int no_error = -1;@@ -332,7 +376,7 @@                      ctx->global_failure_args =                        clCreateBuffer(ctx->opencl.ctx,                                       CL_MEM_READ_WRITE,-                                      sizeof(cl_int)*($int:max_failure_args+1), NULL, &error);+                                      sizeof(int64_t)*($int:max_failure_args+1), NULL, &error);                      OPENCL_SUCCEED_OR_RETURN(error);                       // Load all the kernels.@@ -351,12 +395,14 @@                      return futhark_context_sync(ctx);   }|] -  let set_required_types = [ [C.cstm|required_types |= OPENCL_F64; |]-                           | FloatType Float64 `elem` types ]+  let set_required_types =+        [ [C.cstm|required_types |= OPENCL_F64; |]+          | FloatType Float64 `elem` types+        ]    GC.publicDef_ "context_new" GC.InitDecl $ \s ->-    ([C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg);|],-     [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg) {+    ( [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg);|],+      [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg) {                           struct $id:ctx* ctx = (struct $id:ctx*) malloc(sizeof(struct $id:ctx));                           if (ctx == NULL) {                             return NULL;@@ -369,11 +415,12 @@                           typename cl_program prog = setup_opencl(&ctx->opencl, opencl_program, required_types, cfg->build_opts);                           init_context_late(cfg, ctx, prog);                           return ctx;-                       }|])+                       }|]+    )    GC.publicDef_ "context_new_with_command_queue" GC.InitDecl $ \s ->-    ([C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg, typename cl_command_queue queue);|],-     [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg, typename cl_command_queue queue) {+    ( [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg, typename cl_command_queue queue);|],+      [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg, typename cl_command_queue queue) {                           struct $id:ctx* ctx = (struct $id:ctx*) malloc(sizeof(struct $id:ctx));                           if (ctx == NULL) {                             return NULL;@@ -386,21 +433,23 @@                           typename cl_program prog = setup_opencl_with_command_queue(&ctx->opencl, queue, opencl_program, required_types, cfg->build_opts);                           init_context_late(cfg, ctx, prog);                           return ctx;-                       }|])+                       }|]+    )    GC.publicDef_ "context_free" GC.InitDecl $ \s ->-    ([C.cedecl|void $id:s(struct $id:ctx* ctx);|],-     [C.cedecl|void $id:s(struct $id:ctx* ctx) {+    ( [C.cedecl|void $id:s(struct $id:ctx* ctx);|],+      [C.cedecl|void $id:s(struct $id:ctx* ctx) {                                  free_constants(ctx);                                  free_lock(&ctx->lock);                                  $stms:(map releaseKernel (M.toList kernels))                                  teardown_opencl(&ctx->opencl);                                  free(ctx);-                               }|])+                               }|]+    )    GC.publicDef_ "context_sync" GC.MiscDecl $ \s ->-    ([C.cedecl|int $id:s(struct $id:ctx* ctx);|],-     [C.cedecl|int $id:s(struct $id:ctx* ctx) {+    ( [C.cedecl|int $id:s(struct $id:ctx* ctx);|],+      [C.cedecl|int $id:s(struct $id:ctx* ctx) {                  // Check for any delayed error.                  typename cl_int failure_idx = -1;                  if (ctx->failure_is_an_option) {@@ -424,7 +473,7 @@                                          0, sizeof(cl_int), &no_failure,                                          0, NULL, NULL)); -                   typename cl_int args[$int:max_failure_args+1];+                   typename int64_t args[$int:max_failure_args+1];                    OPENCL_SUCCEED_OR_RETURN(                      clEnqueueReadBuffer(ctx->opencl.queue,                                          ctx->global_failure_args,@@ -437,73 +486,90 @@                    return 1;                  }                  return 0;-               }|])+               }|]+    )    GC.publicDef_ "context_clear_caches" GC.MiscDecl $ \s ->-    ([C.cedecl|int $id:s(struct $id:ctx* ctx);|],-     [C.cedecl|int $id:s(struct $id:ctx* ctx) {+    ( [C.cedecl|int $id:s(struct $id:ctx* ctx);|],+      [C.cedecl|int $id:s(struct $id:ctx* ctx) {                          ctx->error = OPENCL_SUCCEED_NONFATAL(opencl_free_all(&ctx->opencl));                          return ctx->error != NULL;-                       }|])+                       }|]+    )    GC.publicDef_ "context_get_command_queue" GC.InitDecl $ \s ->-    ([C.cedecl|typename cl_command_queue $id:s(struct $id:ctx* ctx);|],-     [C.cedecl|typename cl_command_queue $id:s(struct $id:ctx* ctx) {+    ( [C.cedecl|typename cl_command_queue $id:s(struct $id:ctx* ctx);|],+      [C.cedecl|typename cl_command_queue $id:s(struct $id:ctx* ctx) {                  return ctx->opencl.queue;-               }|])+               }|]+    )    GC.profileReport [C.citem|OPENCL_SUCCEED_FATAL(opencl_tally_profiling_records(&ctx->opencl));|]-  mapM_ GC.profileReport $ costCentreReport $-    cost_centres ++ M.keys kernels+  mapM_ GC.profileReport $+    costCentreReport $+      cost_centres ++ M.keys kernels -openClDecls :: [Name] -> M.Map KernelName KernelSafety -> String -> String-            -> ([C.FieldGroup], [C.Stm], [C.Definition], [C.Definition])+openClDecls ::+  [Name] ->+  M.Map KernelName KernelSafety ->+  String ->+  String ->+  ([C.FieldGroup], [C.Stm], [C.Definition], [C.Definition]) openClDecls cost_centres kernels opencl_program opencl_prelude =   (ctx_fields, ctx_inits, openCL_boilerplate, openCL_load)-  where opencl_program_fragments =-          -- Some C compilers limit the size of literal strings, so-          -- chunk the entire program into small bits here, and-          -- concatenate it again at runtime.-          [ [C.cinit|$string:s|] | s <- chunk 2000 (opencl_prelude++opencl_program) ]+  where+    opencl_program_fragments =+      -- Some C compilers limit the size of literal strings, so+      -- chunk the entire program into small bits here, and+      -- concatenate it again at runtime.+      [[C.cinit|$string:s|] | s <- chunk 2000 (opencl_prelude ++ opencl_program)] -        ctx_fields =-          [ [C.csdecl|int total_runs;|],-            [C.csdecl|long int total_runtime;|] ] ++-          [ [C.csdecl|typename cl_kernel $id:name;|]-          | name <- M.keys kernels ] ++-          concat-          [ [ [C.csdecl|typename int64_t $id:(kernelRuntime name);|]-            , [C.csdecl|int $id:(kernelRuns name);|]+    ctx_fields =+      [ [C.csdecl|int total_runs;|],+        [C.csdecl|long int total_runtime;|]+      ]+        ++ [ [C.csdecl|typename cl_kernel $id:name;|]+             | name <- M.keys kernels+           ]+        ++ concat+          [ [ [C.csdecl|typename int64_t $id:(kernelRuntime name);|],+              [C.csdecl|int $id:(kernelRuns name);|]             ]-          | name <- cost_centres ++ M.keys kernels ]+            | name <- cost_centres ++ M.keys kernels+          ] -        ctx_inits =-          [ [C.cstm|ctx->total_runs = 0;|],-            [C.cstm|ctx->total_runtime = 0;|] ] ++-          concat-          [ [ [C.cstm|ctx->$id:(kernelRuntime name) = 0;|]-            , [C.cstm|ctx->$id:(kernelRuns name) = 0;|]+    ctx_inits =+      [ [C.cstm|ctx->total_runs = 0;|],+        [C.cstm|ctx->total_runtime = 0;|]+      ]+        ++ concat+          [ [ [C.cstm|ctx->$id:(kernelRuntime name) = 0;|],+              [C.cstm|ctx->$id:(kernelRuns name) = 0;|]             ]-          | name <- cost_centres ++ M.keys kernels ]+            | name <- cost_centres ++ M.keys kernels+          ] -        openCL_load = [-          [C.cedecl|+    openCL_load =+      [ [C.cedecl| void post_opencl_setup(struct opencl_context *ctx, struct opencl_device_option *option) {   $stms:(map sizeHeuristicsCode sizeHeuristicsTable)-}|]]+}|]+      ] -        free_list_h = $(embedStringFile "rts/c/free_list.h")-        openCL_h = $(embedStringFile "rts/c/opencl.h")+    free_list_h = $(embedStringFile "rts/c/free_list.h")+    openCL_h = $(embedStringFile "rts/c/opencl.h") -        program_fragments = opencl_program_fragments ++ [[C.cinit|NULL|]]-        openCL_boilerplate = [C.cunit|+    program_fragments = opencl_program_fragments ++ [[C.cinit|NULL|]]+    openCL_boilerplate =+      [C.cunit|           $esc:("typedef cl_mem fl_mem_t;")           $esc:free_list_h           $esc:openCL_h           static const char *opencl_program[] = {$inits:program_fragments};|]  loadKernel :: (KernelName, KernelSafety) -> C.Stm-loadKernel (name, safety) = [C.cstm|{+loadKernel (name, safety) =+  [C.cstm|{   ctx->$id:name = clCreateKernel(prog, $string:(pretty (C.toIdent name mempty)), &error);   OPENCL_SUCCEED_FATAL(error);   $items:set_args@@ -511,126 +577,148 @@     fprintf(stderr, "Created kernel %s.\n", $string:(pretty name));   }   }|]-  where set_global_failure =-          [C.citem|OPENCL_SUCCEED_FATAL(+  where+    set_global_failure =+      [C.citem|OPENCL_SUCCEED_FATAL(                      clSetKernelArg(ctx->$id:name, 0, sizeof(typename cl_mem),                                     &ctx->global_failure));|]-        set_global_failure_args =-          [C.citem|OPENCL_SUCCEED_FATAL(+    set_global_failure_args =+      [C.citem|OPENCL_SUCCEED_FATAL(                      clSetKernelArg(ctx->$id:name, 2, sizeof(typename cl_mem),                                     &ctx->global_failure_args));|]-        set_args = case safety of-                     SafetyNone -> []-                     SafetyCheap -> [set_global_failure]-                     SafetyFull -> [set_global_failure, set_global_failure_args]+    set_args = case safety of+      SafetyNone -> []+      SafetyCheap -> [set_global_failure]+      SafetyFull -> [set_global_failure, set_global_failure_args]  releaseKernel :: (KernelName, KernelSafety) -> C.Stm releaseKernel (name, _) = [C.cstm|OPENCL_SUCCEED_FATAL(clReleaseKernel(ctx->$id:name));|]  kernelRuntime :: KernelName -> Name-kernelRuntime = (<>"_total_runtime")+kernelRuntime = (<> "_total_runtime")  kernelRuns :: KernelName -> Name-kernelRuns = (<>"_runs")+kernelRuns = (<> "_runs")  costCentreReport :: [Name] -> [C.BlockItem] costCentreReport names = report_kernels ++ [report_total]-  where longest_name = foldl max 0 $ map (length . pretty) names-        report_kernels = concatMap reportKernel names-        format_string name =-          let padding = replicate (longest_name - length name) ' '-          in unwords [name ++ padding,-                      "ran %5d times; avg: %8ldus; total: %8ldus\n"]-        reportKernel name =-          let runs = kernelRuns name-              total_runtime = kernelRuntime name-          in [[C.citem|+  where+    longest_name = foldl max 0 $ map (length . pretty) names+    report_kernels = concatMap reportKernel names+    format_string name =+      let padding = replicate (longest_name - length name) ' '+       in unwords+            [ name ++ padding,+              "ran %5d times; avg: %8ldus; total: %8ldus\n"+            ]+    reportKernel name =+      let runs = kernelRuns name+          total_runtime = kernelRuntime name+       in [ [C.citem|                str_builder(&builder,                            $string:(format_string (pretty name)),                            ctx->$id:runs,                            (long int) ctx->$id:total_runtime / (ctx->$id:runs != 0 ? ctx->$id:runs : 1),                            (long int) ctx->$id:total_runtime);               |],-              [C.citem|ctx->total_runtime += ctx->$id:total_runtime;|],-              [C.citem|ctx->total_runs += ctx->$id:runs;|]]+            [C.citem|ctx->total_runtime += ctx->$id:total_runtime;|],+            [C.citem|ctx->total_runs += ctx->$id:runs;|]+          ] -        report_total = [C.citem|+    report_total =+      [C.citem|                           str_builder(&builder, "%d operations with cumulative runtime: %6ldus\n",                                       ctx->total_runs, ctx->total_runtime);                         |]  sizeHeuristicsCode :: SizeHeuristic -> C.Stm-sizeHeuristicsCode (SizeHeuristic platform_name device_type which what) =+sizeHeuristicsCode (SizeHeuristic platform_name device_type which (TPrimExp what)) =   [C.cstm|    if ($exp:which' == 0 &&        strstr(option->platform_name, $string:platform_name) != NULL &&        (option->device_type & $exp:(clDeviceType device_type)) == $exp:(clDeviceType device_type)) {      $items:get_size    }|]-  where clDeviceType DeviceGPU = [C.cexp|CL_DEVICE_TYPE_GPU|]-        clDeviceType DeviceCPU = [C.cexp|CL_DEVICE_TYPE_CPU|]+  where+    clDeviceType DeviceGPU = [C.cexp|CL_DEVICE_TYPE_GPU|]+    clDeviceType DeviceCPU = [C.cexp|CL_DEVICE_TYPE_CPU|] -        which' = case which of-                   LockstepWidth -> [C.cexp|ctx->lockstep_width|]-                   NumGroups -> [C.cexp|ctx->cfg.default_num_groups|]-                   GroupSize -> [C.cexp|ctx->cfg.default_group_size|]-                   TileSize -> [C.cexp|ctx->cfg.default_tile_size|]-                   Threshold -> [C.cexp|ctx->cfg.default_threshold|]+    which' = case which of+      LockstepWidth -> [C.cexp|ctx->lockstep_width|]+      NumGroups -> [C.cexp|ctx->cfg.default_num_groups|]+      GroupSize -> [C.cexp|ctx->cfg.default_group_size|]+      TileSize -> [C.cexp|ctx->cfg.default_tile_size|]+      Threshold -> [C.cexp|ctx->cfg.default_threshold|] -        get_size =-          let (e, m) = runState (GC.compilePrimExp onLeaf what) mempty-          in concat (M.elems m) ++ [[C.citem|$exp:which' = $exp:e;|]]+    get_size =+      let (e, m) = runState (GC.compilePrimExp onLeaf what) mempty+       in concat (M.elems m) ++ [[C.citem|$exp:which' = $exp:e;|]] -        onLeaf (DeviceInfo s) = do-          let s' = "CL_DEVICE_" ++ s-              v = s ++ "_val"-          m <- get-          case M.lookup s m of-            Nothing ->-              -- Cheating with the type here; works for the infos we-              -- currently use, but should be made more size-aware in-              -- the future.-              modify $ M.insert s'+    onLeaf (DeviceInfo s) = do+      let s' = "CL_DEVICE_" ++ s+          v = s ++ "_val"+      m <- get+      case M.lookup s m of+        Nothing ->+          -- Cheating with the type here; works for the infos we+          -- currently use, but should be made more size-aware in+          -- the future.+          modify $+            M.insert+              s'               [C.citems|size_t $id:v;                         clGetDeviceInfo(ctx->device, $id:s',                                         sizeof($id:v), &$id:v,                                         NULL);|]-            Just _ -> return ()+        Just _ -> return () -          return [C.cexp|$id:v|]+      return [C.cexp|$id:v|]  -- Options that are common to multiple GPU-like backends. commonOptions :: [Option] commonOptions =-   [ Option { optionLongName = "device"-            , optionShortName = Just 'd'-            , optionArgument = RequiredArgument "NAME"-            , optionAction = [C.cstm|futhark_context_config_set_device(cfg, optarg);|]-            }-   , Option { optionLongName = "default-group-size"-            , optionShortName = Nothing-            , optionArgument = RequiredArgument "INT"-            , optionAction = [C.cstm|futhark_context_config_set_default_group_size(cfg, atoi(optarg));|]-            }-   , Option { optionLongName = "default-num-groups"-            , optionShortName = Nothing-            , optionArgument = RequiredArgument "INT"-            , optionAction = [C.cstm|futhark_context_config_set_default_num_groups(cfg, atoi(optarg));|]-            }-   , Option { optionLongName = "default-tile-size"-            , optionShortName = Nothing-            , optionArgument = RequiredArgument "INT"-            , optionAction = [C.cstm|futhark_context_config_set_default_tile_size(cfg, atoi(optarg));|]-            }-   , Option { optionLongName = "default-threshold"-            , optionShortName = Nothing-            , optionArgument = RequiredArgument "INT"-            , optionAction = [C.cstm|futhark_context_config_set_default_threshold(cfg, atoi(optarg));|]-            }-   , Option { optionLongName = "print-sizes"-            , optionShortName = Nothing-            , optionArgument = NoArgument-            , optionAction = [C.cstm|{+  [ Option+      { optionLongName = "device",+        optionShortName = Just 'd',+        optionArgument = RequiredArgument "NAME",+        optionDescription = "Use the first OpenCL device whose name contains the given string.",+        optionAction = [C.cstm|futhark_context_config_set_device(cfg, optarg);|]+      },+    Option+      { optionLongName = "default-group-size",+        optionShortName = Nothing,+        optionArgument = RequiredArgument "INT",+        optionDescription = "The default size of OpenCL workgroups that are launched.",+        optionAction = [C.cstm|futhark_context_config_set_default_group_size(cfg, atoi(optarg));|]+      },+    Option+      { optionLongName = "default-num-groups",+        optionShortName = Nothing,+        optionArgument = RequiredArgument "INT",+        optionDescription = "The default number of OpenCL workgroups that are launched.",+        optionAction = [C.cstm|futhark_context_config_set_default_num_groups(cfg, atoi(optarg));|]+      },+    Option+      { optionLongName = "default-tile-size",+        optionShortName = Nothing,+        optionArgument = RequiredArgument "INT",+        optionDescription = "The default tile size used when performing two-dimensional tiling.",+        optionAction = [C.cstm|futhark_context_config_set_default_tile_size(cfg, atoi(optarg));|]+      },+    Option+      { optionLongName = "default-threshold",+        optionShortName = Nothing,+        optionArgument = RequiredArgument "INT",+        optionDescription = "The default parallelism threshold.",+        optionAction = [C.cstm|futhark_context_config_set_default_threshold(cfg, atoi(optarg));|]+      },+    Option+      { optionLongName = "print-sizes",+        optionShortName = Nothing,+        optionArgument = NoArgument,+        optionDescription = "Print all sizes that can be set with -size or --tuning.",+        optionAction =+          [C.cstm|{                 int n = futhark_get_num_sizes();                 for (int i = 0; i < n; i++) {                   printf("%s (%s)\n", futhark_get_size_name(i),@@ -638,11 +726,14 @@                 }                 exit(0);               }|]-            }-   , Option { optionLongName = "size"-            , optionShortName = Nothing-            , optionArgument = RequiredArgument "NAME=INT"-            , optionAction = [C.cstm|{+      },+    Option+      { optionLongName = "size",+        optionShortName = Nothing,+        optionArgument = RequiredArgument "ASSIGNMENT",+        optionDescription = "Set a configurable run-time parameter to the given value.",+        optionAction =+          [C.cstm|{                 char *name = optarg;                 char *equals = strstr(optarg, "=");                 char *value_str = equals != NULL ? equals+1 : optarg;@@ -655,15 +746,18 @@                 } else {                   futhark_panic(1, "Invalid argument for size option: %s\n", optarg);                 }}|]-            }-   , Option { optionLongName = "tuning"-            , optionShortName = Nothing-            , optionArgument = RequiredArgument "FILE"-            , optionAction = [C.cstm|{+      },+    Option+      { optionLongName = "tuning",+        optionShortName = Nothing,+        optionArgument = RequiredArgument "FILE",+        optionDescription = "Read size=value assignments from the given file.",+        optionAction =+          [C.cstm|{                 char *ret = load_tuning_file(optarg, cfg, (int(*)(void*, const char*, size_t))                                                           futhark_context_config_set_size);                 if (ret != NULL) {                   futhark_panic(1, "When loading tuning from '%s': %s\n", optarg, ret);                 }}|]-            }-   ]+      }+  ]
src/Futhark/CodeGen/Backends/GenericC.hs view
@@ -1,2200 +1,2414 @@-{-# LANGUAGE QuasiQuotes, GeneralizedNewtypeDeriving, FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE Trustworthy #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--- | C code generator framework.-module Futhark.CodeGen.Backends.GenericC-  ( compileProg-  , CParts(..)-  , asLibrary-  , asExecutable--  -- * Pluggable compiler-  , Operations (..)-  , defaultOperations-  , OpCompiler-  , ErrorCompiler-  , CallCompiler--  , PointerQuals-  , MemoryType-  , WriteScalar-  , writeScalarPointerWithQuals-  , ReadScalar-  , readScalarPointerWithQuals-  , Allocate-  , Deallocate-  , Copy-  , StaticArray--  -- * Monadic compiler interface-  , CompilerM-  , CompilerState (compUserState)-  , getUserState-  , modifyUserState-  , contextContents-  , contextFinalInits-  , runCompilerM-  , cachingMemory-  , blockScope-  , compileFun-  , compileCode-  , compileExp-  , compilePrimExp-  , compilePrimValue-  , compileExpToName-  , rawMem-  , item-  , items-  , stm-  , stms-  , decl-  , atInit-  , headerDecl-  , publicDef-  , publicDef_-  , profileReport-  , HeaderSection(..)-  , libDecl-  , earlyDecl-  , publicName-  , contextType-  , contextField--  -- * Building Blocks-  , primTypeToCType-  , copyMemoryDefaultSpace-  ) where--import Control.Monad.Identity-import Control.Monad.State-import Control.Monad.Reader-import Control.Monad.Writer-import Control.Monad.RWS-import Data.Bifunctor (first)-import Data.Bits (xor, shiftR)-import Data.Char (ord, isDigit, isAlphaNum)-import qualified Data.Map.Strict as M-import qualified Data.DList as DL-import Data.List (unzip4)-import Data.Loc-import Data.Maybe-import Data.FileEmbed-import Text.Printf--import qualified Language.C.Syntax as C-import qualified Language.C.Quote.OpenCL as C--import Futhark.CodeGen.ImpCode-import Futhark.MonadFreshNames-import Futhark.CodeGen.Backends.SimpleRep-import Futhark.CodeGen.Backends.GenericC.Options-import Futhark.Util (zEncodeString)-import Futhark.IR.Prop (isBuiltInFunction)---data CompilerState s = CompilerState {-    compArrayStructs :: [((C.Type, Int), (C.Type, [C.Definition]))]-  , compOpaqueStructs :: [(String, (C.Type, [C.Definition]))]-  , compEarlyDecls :: DL.DList C.Definition-  , compInit :: [C.Stm]-  , compNameSrc :: VNameSource-  , compUserState :: s-  , compHeaderDecls :: M.Map HeaderSection (DL.DList C.Definition)-  , compLibDecls :: DL.DList C.Definition-  , compCtxFields :: DL.DList (C.Id, C.Type, Maybe C.Exp)-  , compProfileItems :: DL.DList C.BlockItem-  , compDeclaredMem :: [(VName,Space)]-  }--newCompilerState :: VNameSource -> s -> CompilerState s-newCompilerState src s = CompilerState { compArrayStructs = []-                                       , compOpaqueStructs = []-                                       , compEarlyDecls = mempty-                                       , compInit = []-                                       , compNameSrc = src-                                       , compUserState = s-                                       , compHeaderDecls = mempty-                                       , compLibDecls = mempty-                                       , compCtxFields = mempty-                                       , compProfileItems = mempty-                                       , compDeclaredMem = mempty-                                       }---- | In which part of the header file we put the declaration.  This is--- to ensure that the header file remains structured and readable.-data HeaderSection = ArrayDecl String-                   | OpaqueDecl String-                   | EntryDecl-                   | MiscDecl-                   | InitDecl-                   deriving (Eq, Ord)---- | A substitute expression compiler, tried before the main--- compilation function.-type OpCompiler op s = op -> CompilerM op s ()--type ErrorCompiler op s = ErrorMsg Exp -> String -> CompilerM op s ()---- | The address space qualifiers for a pointer of the given type with--- the given annotation.-type PointerQuals op s = String -> CompilerM op s [C.TypeQual]---- | The type of a memory block in the given memory space.-type MemoryType op s = SpaceId -> CompilerM op s C.Type---- | Write a scalar to the given memory block with the given element--- index and in the given memory space.-type WriteScalar op s =-  C.Exp -> C.Exp -> C.Type -> SpaceId -> Volatility -> C.Exp -> CompilerM op s ()---- | Read a scalar from the given memory block with the given element--- index and in the given memory space.-type ReadScalar op s =-  C.Exp -> C.Exp -> C.Type -> SpaceId -> Volatility -> CompilerM op s C.Exp---- | Allocate a memory block of the given size and with the given tag--- in the given memory space, saving a reference in the given variable--- name.-type Allocate op s = C.Exp -> C.Exp -> C.Exp -> SpaceId-                     -> CompilerM op s ()---- | De-allocate the given memory block with the given tag, which is--- in the given memory space.-type Deallocate op s = C.Exp -> C.Exp -> SpaceId -> CompilerM op s ()---- | Create a static array of values - initialised at load time.-type StaticArray op s = VName -> SpaceId -> PrimType -> ArrayContents -> CompilerM op s ()---- | Copy from one memory block to another.-type Copy op s = C.Exp -> C.Exp -> Space ->-                 C.Exp -> C.Exp -> Space ->-                 C.Exp ->-                 CompilerM op s ()---- | Call a function.-type CallCompiler op s = [VName] -> Name -> [C.Exp] -> CompilerM op s ()--data Operations op s =-  Operations { opsWriteScalar :: WriteScalar op s-             , opsReadScalar :: ReadScalar op s-             , opsAllocate :: Allocate op s-             , opsDeallocate :: Deallocate op s-             , opsCopy :: Copy op s-             , opsStaticArray :: StaticArray op s--             , opsMemoryType :: MemoryType op s-             , opsCompiler :: OpCompiler op s-             , opsError :: ErrorCompiler op s-             , opsCall :: CallCompiler op s--             , opsFatMemory :: Bool-               -- ^ If true, use reference counting.  Otherwise, bare-               -- pointers.-             }--defError :: ErrorCompiler op s-defError (ErrorMsg parts) stacktrace = do-  free_all_mem <- collect $ mapM_ (uncurry unRefMem) =<< gets compDeclaredMem-  let onPart (ErrorString s) = return ("%s", [C.cexp|$string:s|])-      onPart (ErrorInt32 x) = ("%d",) <$> compileExp x-  (formatstrs, formatargs) <- unzip <$> mapM onPart parts-  let formatstr = "Error: " ++ concat formatstrs ++ "\n\nBacktrace:\n%s"-  items [C.citems|ctx->error = msgprintf($string:formatstr, $args:formatargs, $string:stacktrace);-                  $items:free_all_mem-                  return 1;|]--defCall :: CallCompiler op s-defCall dests fname args = do-  let out_args = [ [C.cexp|&$id:d|] | d <- dests ]-      args' | isBuiltInFunction fname = args-            | otherwise = [C.cexp|ctx|] : out_args ++ args-  case dests of-    [dest] | isBuiltInFunction fname ->-      stm [C.cstm|$id:dest = $id:(funName fname)($args:args');|]-    _ ->-      item [C.citem|if ($id:(funName fname)($args:args') != 0) { err = 1; goto cleanup; }|]---- | A set of operations that fail for every operation involving--- non-default memory spaces.  Uses plain pointers and @malloc@ for--- memory management.-defaultOperations :: Operations op s-defaultOperations = Operations { opsWriteScalar = defWriteScalar-                               , opsReadScalar = defReadScalar-                               , opsAllocate  = defAllocate-                               , opsDeallocate  = defDeallocate-                               , opsCopy = defCopy-                               , opsStaticArray = defStaticArray-                               , opsMemoryType = defMemoryType-                               , opsCompiler = defCompiler-                               , opsFatMemory = True-                               , opsError = defError-                               , opsCall = defCall-                               }-  where defWriteScalar _ _ _ _ _ =-          error "Cannot write to non-default memory space because I am dumb"-        defReadScalar _ _ _ _ =-          error "Cannot read from non-default memory space"-        defAllocate _ _ _ =-          error "Cannot allocate in non-default memory space"-        defDeallocate _ _ =-          error "Cannot deallocate in non-default memory space"-        defCopy destmem destoffset DefaultSpace srcmem srcoffset DefaultSpace size =-          copyMemoryDefaultSpace destmem destoffset srcmem srcoffset size-        defCopy _ _ _ _ _ _ _ =-          error "Cannot copy to or from non-default memory space"-        defStaticArray _ _ _ _ =-          error "Cannot create static array in non-default memory space"-        defMemoryType _ =-          error "Has no type for non-default memory space"-        defCompiler _ =-          error "The default compiler cannot compile extended operations"---data CompilerEnv op s = CompilerEnv-  { envOperations :: Operations op s-  , envCachedMem :: M.Map C.Exp VName-    -- ^ Mapping memory blocks to sizes.  These memory blocks are CPU-    -- memory that we know are used in particularly simple ways (no-    -- reference counting necessary).  To cut down on allocator-    -- pressure, we keep these allocations around for a long time, and-    -- record their sizes so we can reuse them if possible (and-    -- realloc() when needed).-  }--newtype CompilerAcc op s = CompilerAcc {-    accItems :: DL.DList C.BlockItem-  }--instance Semigroup (CompilerAcc op s) where-  CompilerAcc items1 <> CompilerAcc items2 =-    CompilerAcc (items1<>items2)--instance Monoid (CompilerAcc op s) where-  mempty = CompilerAcc mempty--envOpCompiler :: CompilerEnv op s -> OpCompiler op s-envOpCompiler = opsCompiler . envOperations--envMemoryType :: CompilerEnv op s -> MemoryType op s-envMemoryType = opsMemoryType . envOperations--envReadScalar :: CompilerEnv op s -> ReadScalar op s-envReadScalar = opsReadScalar . envOperations--envWriteScalar :: CompilerEnv op s -> WriteScalar op s-envWriteScalar = opsWriteScalar . envOperations--envAllocate :: CompilerEnv op s -> Allocate op s-envAllocate = opsAllocate . envOperations--envDeallocate :: CompilerEnv op s -> Deallocate op s-envDeallocate = opsDeallocate . envOperations--envCopy :: CompilerEnv op s -> Copy op s-envCopy = opsCopy . envOperations--envStaticArray :: CompilerEnv op s -> StaticArray op s-envStaticArray = opsStaticArray . envOperations--envFatMemory :: CompilerEnv op s -> Bool-envFatMemory = opsFatMemory . envOperations--arrayDefinitions, opaqueDefinitions :: CompilerState s -> [C.Definition]-arrayDefinitions = concatMap (snd . snd) . compArrayStructs-opaqueDefinitions = concatMap (snd . snd) . compOpaqueStructs--initDecls, arrayDecls, opaqueDecls, entryDecls, miscDecls :: CompilerState s -> [C.Definition]-initDecls = concatMap (DL.toList . snd) . filter ((==InitDecl) . fst) . M.toList . compHeaderDecls-arrayDecls = concatMap (DL.toList . snd) . filter (isArrayDecl . fst) . M.toList . compHeaderDecls-  where isArrayDecl ArrayDecl{} = True-        isArrayDecl _           = False-opaqueDecls = concatMap (DL.toList . snd) . filter (isOpaqueDecl . fst) . M.toList . compHeaderDecls-  where isOpaqueDecl OpaqueDecl{} = True-        isOpaqueDecl _           = False-entryDecls = concatMap (DL.toList . snd) . filter ((==EntryDecl) . fst) . M.toList . compHeaderDecls-miscDecls = concatMap (DL.toList . snd) . filter ((==MiscDecl) . fst) . M.toList . compHeaderDecls--contextContents :: CompilerM op s ([C.FieldGroup], [C.Stm])-contextContents = do-  (field_names, field_types, field_values) <- gets $ unzip3 . DL.toList . compCtxFields-  let fields = [ [C.csdecl|$ty:ty $id:name;|]-               | (name, ty) <- zip field_names field_types ]-      init_fields = [ [C.cstm|ctx->$id:name = $exp:e;|]-                    | (name, Just e) <- zip field_names field_values ]-  return (fields, init_fields)--contextFinalInits :: CompilerM op s [C.Stm]-contextFinalInits = gets compInit--newtype CompilerM op s a = CompilerM (RWS-                                      (CompilerEnv op s)-                                      (CompilerAcc op s)-                                      (CompilerState s) a)-  deriving (Functor, Applicative, Monad,-            MonadState (CompilerState s),-            MonadReader (CompilerEnv op s),-            MonadWriter (CompilerAcc op s))--instance MonadFreshNames (CompilerM op s) where-  getNameSource = gets compNameSrc-  putNameSource src = modify $ \s -> s { compNameSrc = src }--runCompilerM :: Operations op s -> VNameSource -> s-             -> CompilerM op s a-             -> (a, CompilerState s)-runCompilerM ops src userstate (CompilerM m) =-  let (x, s, _) = runRWS m (CompilerEnv ops mempty) (newCompilerState src userstate)-  in (x, s)--getUserState :: CompilerM op s s-getUserState = gets compUserState--modifyUserState :: (s -> s) -> CompilerM op s ()-modifyUserState f = modify $ \compstate ->-  compstate { compUserState = f $ compUserState compstate }--atInit :: C.Stm -> CompilerM op s ()-atInit x = modify $ \s ->-  s { compInit = compInit s ++ [x] }--collect :: CompilerM op s () -> CompilerM op s [C.BlockItem]-collect m = snd <$> collect' m--collect' :: CompilerM op s a -> CompilerM op s (a, [C.BlockItem])-collect' m = pass $ do-  (x, w) <- listen m-  return ((x, DL.toList $ accItems w),-          const w { accItems = mempty})--item :: C.BlockItem -> CompilerM op s ()-item x = tell $ mempty { accItems = DL.singleton x }--items :: [C.BlockItem] -> CompilerM op s ()-items = mapM_ item--fatMemory :: Space -> CompilerM op s Bool-fatMemory ScalarSpace{} = return False-fatMemory _ = asks envFatMemory--cacheMem :: C.ToExp a => a -> CompilerM op s (Maybe VName)-cacheMem a = asks $ M.lookup (C.toExp a noLoc) . envCachedMem--instance C.ToIdent Name where-  toIdent = C.toIdent . zEncodeString . nameToString--instance C.ToIdent VName where-  toIdent = C.toIdent . zEncodeString . pretty--instance C.ToExp VName where-  toExp v _ = [C.cexp|$id:v|]--instance C.ToExp IntValue where-  toExp (Int8Value v) = C.toExp v-  toExp (Int16Value v) = C.toExp v-  toExp (Int32Value v) = C.toExp v-  toExp (Int64Value v) = C.toExp v--instance C.ToExp FloatValue where-  toExp (Float32Value v) = C.toExp v-  toExp (Float64Value v) = C.toExp v--instance C.ToExp PrimValue where-  toExp (IntValue v) = C.toExp v-  toExp (FloatValue v) = C.toExp v-  toExp (BoolValue True) = C.toExp (1::Int8)-  toExp (BoolValue False) = C.toExp (0::Int8)-  toExp Checked = C.toExp (1::Int8)--instance C.ToExp SubExp where-  toExp (Var v) = C.toExp v-  toExp (Constant c) = C.toExp c---- | Construct a publicly visible definition using the specified name--- as the template.  The first returned definition is put in the--- header file, and the second is the implementation.  Returns the public--- name.-publicDef :: String -> HeaderSection -> (String -> (C.Definition, C.Definition))-          -> CompilerM op s String-publicDef s h f = do-  s' <- publicName s-  let (pub, priv) = f s'-  headerDecl h pub-  earlyDecl priv-  return s'---- | As 'publicDef', but ignores the public name.-publicDef_ :: String -> HeaderSection -> (String -> (C.Definition, C.Definition))-           -> CompilerM op s ()-publicDef_ s h f = void $ publicDef s h f--headerDecl :: HeaderSection -> C.Definition -> CompilerM op s ()-headerDecl sec def = modify $ \s ->-  s { compHeaderDecls = M.unionWith (<>) (compHeaderDecls s)-                              (M.singleton sec (DL.singleton def)) }--libDecl :: C.Definition -> CompilerM op s ()-libDecl def = modify $ \s ->-  s { compLibDecls = compLibDecls s <> DL.singleton def }--earlyDecl :: C.Definition -> CompilerM op s ()-earlyDecl def = modify $ \s ->-  s { compEarlyDecls = compEarlyDecls s <> DL.singleton def }--contextField :: C.Id -> C.Type -> Maybe C.Exp -> CompilerM op s ()-contextField name ty initial = modify $ \s ->-  s { compCtxFields = compCtxFields s <> DL.singleton (name,ty,initial) }--profileReport :: C.BlockItem -> CompilerM op s ()-profileReport x = modify $ \s ->-  s { compProfileItems = compProfileItems s <> DL.singleton x }--stm :: C.Stm -> CompilerM op s ()-stm s = item [C.citem|$stm:s|]--stms :: [C.Stm] -> CompilerM op s ()-stms = mapM_ stm--decl :: C.InitGroup -> CompilerM op s ()-decl x = item [C.citem|$decl:x;|]--addrOf :: C.Exp -> C.Exp-addrOf e = [C.cexp|&$exp:e|]---- | Public names must have a consitent prefix.-publicName :: String -> CompilerM op s String-publicName s = return $ "futhark_" ++ s---- | The generated code must define a struct with this name.-contextType :: CompilerM op s C.Type-contextType = do-  name <- publicName "context"-  return [C.cty|struct $id:name|]--memToCType :: VName -> Space -> CompilerM op s C.Type-memToCType v space = do-  refcount <- fatMemory space-  cached <- isJust <$> cacheMem v-  if refcount && not cached-     then return $ fatMemType space-     else rawMemCType space--rawMemCType :: Space -> CompilerM op s C.Type-rawMemCType DefaultSpace = return defaultMemBlockType-rawMemCType (Space sid) = join $ asks envMemoryType <*> pure sid-rawMemCType (ScalarSpace [] t) =-  return [C.cty|$ty:(primTypeToCType t)[1]|]-rawMemCType (ScalarSpace ds t) =-  return [C.cty|$ty:(primTypeToCType t)[$exp:(cproduct ds')]|]-  where ds' = map (`C.toExp` noLoc) ds--fatMemType :: Space -> C.Type-fatMemType space =-  [C.cty|struct $id:name|]-  where name = case space of-          Space sid    -> "memblock_" ++ sid-          _            -> "memblock"--fatMemSet :: Space -> String-fatMemSet (Space sid) = "memblock_set_" ++ sid-fatMemSet _ = "memblock_set"--fatMemAlloc :: Space -> String-fatMemAlloc (Space sid) = "memblock_alloc_" ++ sid-fatMemAlloc _ = "memblock_alloc"--fatMemUnRef :: Space -> String-fatMemUnRef (Space sid) = "memblock_unref_" ++ sid-fatMemUnRef _ = "memblock_unref"--rawMem :: VName -> CompilerM op s C.Exp-rawMem v = rawMem' <$> fat <*> pure v-  where fat = asks ((&&) . envFatMemory) <*> (isNothing <$> cacheMem v)--rawMem' :: C.ToExp a => Bool -> a -> C.Exp-rawMem' True  e = [C.cexp|$exp:e.mem|]-rawMem' False e = [C.cexp|$exp:e|]--allocRawMem :: (C.ToExp a, C.ToExp b, C.ToExp c) =>-               a -> b -> Space -> c -> CompilerM op s ()-allocRawMem dest size space desc = case space of-  Space sid ->-    join $ asks envAllocate <*> pure [C.cexp|$exp:dest|] <*>-    pure [C.cexp|$exp:size|] <*> pure [C.cexp|$exp:desc|] <*> pure sid-  _ ->-    stm [C.cstm|$exp:dest = (char*) malloc($exp:size);|]--freeRawMem :: (C.ToExp a, C.ToExp b) =>-              a -> Space -> b -> CompilerM op s ()-freeRawMem mem space desc =-  case space of-    Space sid -> do free_mem <- asks envDeallocate-                    free_mem [C.cexp|$exp:mem|] [C.cexp|$exp:desc|] sid-    _ -> item [C.citem|free($exp:mem);|]--defineMemorySpace :: Space -> CompilerM op s (C.Definition, [C.Definition], C.BlockItem)-defineMemorySpace space = do-  rm <- rawMemCType space-  let structdef =-        [C.cedecl|struct $id:sname { int *references;-                                     $ty:rm mem;-                                     typename int64_t size;-                                     const char *desc; };|]--  contextField peakname [C.cty|typename int64_t|] $ Just [C.cexp|0|]-  contextField usagename [C.cty|typename int64_t|] $ Just [C.cexp|0|]--  -- Unreferencing a memory block consists of decreasing its reference-  -- count and freeing the corresponding memory if the count reaches-  -- zero.-  free <- collect $ freeRawMem [C.cexp|block->mem|] space [C.cexp|desc|]-  ctx_ty <- contextType-  let unrefdef = [C.cedecl|static int $id:(fatMemUnRef space) ($ty:ctx_ty *ctx, $ty:mty *block, const char *desc) {-  if (block->references != NULL) {-    *(block->references) -= 1;-    if (ctx->detail_memory) {-      fprintf(stderr, "Unreferencing block %s (allocated as %s) in %s: %d references remaining.\n",-                      desc, block->desc, $string:spacedesc, *(block->references));-    }-    if (*(block->references) == 0) {-      ctx->$id:usagename -= block->size;-      $items:free-      free(block->references);-      if (ctx->detail_memory) {-        fprintf(stderr, "%lld bytes freed (now allocated: %lld bytes)\n",-                (long long) block->size, (long long) ctx->$id:usagename);-      }-    }-    block->references = NULL;-  }-  return 0;-}|]--  -- When allocating a memory block we initialise the reference count to 1.-  alloc <- collect $-           allocRawMem [C.cexp|block->mem|] [C.cexp|size|] space [C.cexp|desc|]-  let allocdef = [C.cedecl|static int $id:(fatMemAlloc space) ($ty:ctx_ty *ctx, $ty:mty *block, typename int64_t size, const char *desc) {-  if (size < 0) {-    futhark_panic(1, "Negative allocation of %lld bytes attempted for %s in %s.\n",-          (long long)size, desc, $string:spacedesc, ctx->$id:usagename);-  }-  int ret = $id:(fatMemUnRef space)(ctx, block, desc);--  ctx->$id:usagename += size;-  if (ctx->detail_memory) {-    fprintf(stderr, "Allocating %lld bytes for %s in %s (then allocated: %lld bytes)",-            (long long) size,-            desc, $string:spacedesc,-            (long long) ctx->$id:usagename);-  }-  if (ctx->$id:usagename > ctx->$id:peakname) {-    ctx->$id:peakname = ctx->$id:usagename;-    if (ctx->detail_memory) {-      fprintf(stderr, " (new peak).\n");-    }-  } else if (ctx->detail_memory) {-    fprintf(stderr, ".\n");-  }--  $items:alloc-  block->references = (int*) malloc(sizeof(int));-  *(block->references) = 1;-  block->size = size;-  block->desc = desc;-  return ret;-  }|]--  -- Memory setting - unreference the destination and increase the-  -- count of the source by one.-  let setdef = [C.cedecl|static int $id:(fatMemSet space) ($ty:ctx_ty *ctx, $ty:mty *lhs, $ty:mty *rhs, const char *lhs_desc) {-  int ret = $id:(fatMemUnRef space)(ctx, lhs, lhs_desc);-  (*(rhs->references))++;-  *lhs = *rhs;-  return ret;-}-|]--  let peakmsg = "Peak memory usage for " ++ spacedesc ++ ": %lld bytes.\n"-  return (structdef,-          [unrefdef, allocdef, setdef],-          -- Do not report memory usage for DefaultSpace (CPU memory),-          -- because it would not be accurate anyway.  This whole-          -- tracking probably needs to be rethought.-          if space == DefaultSpace-          then [C.citem|{}|]-          else [C.citem|str_builder(&builder, $string:peakmsg, (long long) ctx->$id:peakname);|])-  where mty = fatMemType space-        (peakname, usagename, sname, spacedesc) = case space of-          Space sid -> (C.toIdent ("peak_mem_usage_" ++ sid) noLoc,-                        C.toIdent ("cur_mem_usage_" ++ sid) noLoc,-                        C.toIdent ("memblock_" ++ sid) noLoc,-                        "space '" ++ sid ++ "'")-          _ -> ("peak_mem_usage_default",-                "cur_mem_usage_default",-                "memblock",-                "default space")--declMem :: VName -> Space -> CompilerM op s ()-declMem name space = do-  cached <- isJust <$> cacheMem name-  unless cached $ do-    ty <- memToCType name space-    decl [C.cdecl|$ty:ty $id:name;|]-    resetMem name space-    modify $ \s -> s { compDeclaredMem = (name, space) : compDeclaredMem s }--resetMem :: C.ToExp a => a -> Space -> CompilerM op s ()-resetMem mem space = do-  refcount <- fatMemory space-  cached <- isJust <$> cacheMem mem-  if cached-    then stm [C.cstm|$exp:mem = NULL;|]-    else when refcount $-         stm [C.cstm|$exp:mem.references = NULL;|]--setMem :: (C.ToExp a, C.ToExp b) => a -> b -> Space -> CompilerM op s ()-setMem dest src space = do-  refcount <- fatMemory space-  let src_s = pretty $ C.toExp src noLoc-  if refcount-    then stm [C.cstm|if ($id:(fatMemSet space)(ctx, &$exp:dest, &$exp:src,-                                               $string:src_s) != 0) {-                       return 1;-                     }|]-    else case space of-           ScalarSpace ds _ -> do-             i' <- newVName "i"-             let i = C.toIdent i'-                 it = primTypeToCType $ IntType Int32-                 ds' = map (`C.toExp` noLoc) ds-                 bound = cproduct ds'-             stm [C.cstm|for ($ty:it $id:i = 0; $id:i < $exp:bound; $id:i++) {-                            $exp:dest[$id:i] = $exp:src[$id:i];-                  }|]-           _ -> stm [C.cstm|$exp:dest = $exp:src;|]--unRefMem :: C.ToExp a => a -> Space -> CompilerM op s ()-unRefMem mem space = do-  refcount <- fatMemory space-  cached <- isJust <$> cacheMem mem-  let mem_s = pretty $ C.toExp mem noLoc-  when (refcount && not cached) $-    stm [C.cstm|if ($id:(fatMemUnRef space)(ctx, &$exp:mem, $string:mem_s) != 0) {-                  return 1;-                }|]--allocMem :: (C.ToExp a, C.ToExp b) =>-            a -> b -> Space -> C.Stm -> CompilerM op s ()-allocMem mem size space on_failure = do-  refcount <- fatMemory space-  let mem_s = pretty $ C.toExp mem noLoc-  if refcount-    then stm [C.cstm|if ($id:(fatMemAlloc space)(ctx, &$exp:mem, $exp:size,-                                                 $string:mem_s)) {-                       $stm:on_failure-                     }|]-    else do freeRawMem mem space mem_s-            allocRawMem mem size space [C.cexp|desc|]--primTypeInfo :: PrimType -> Signedness -> C.Exp-primTypeInfo (IntType it) t = case (it, t) of-  (Int8,  TypeUnsigned) -> [C.cexp|u8_info|]-  (Int16, TypeUnsigned) -> [C.cexp|u16_info|]-  (Int32, TypeUnsigned) -> [C.cexp|u32_info|]-  (Int64, TypeUnsigned) -> [C.cexp|u64_info|]-  (Int8,  _) -> [C.cexp|i8_info|]-  (Int16, _) -> [C.cexp|i16_info|]-  (Int32, _) -> [C.cexp|i32_info|]-  (Int64, _) -> [C.cexp|i64_info|]-primTypeInfo (FloatType Float32) _ = [C.cexp|f32_info|]-primTypeInfo (FloatType Float64) _ = [C.cexp|f64_info|]-primTypeInfo Bool _ = [C.cexp|bool_info|]-primTypeInfo Cert _ = [C.cexp|bool_info|]--copyMemoryDefaultSpace :: C.Exp -> C.Exp -> C.Exp -> C.Exp -> C.Exp ->-                          CompilerM op s ()-copyMemoryDefaultSpace destmem destidx srcmem srcidx nbytes =-  stm [C.cstm|memmove($exp:destmem + $exp:destidx,-                      $exp:srcmem + $exp:srcidx,-                      $exp:nbytes);|]----- Entry points.--arrayName :: PrimType -> Signedness -> Int -> String-arrayName pt signed rank =-  prettySigned (signed==TypeUnsigned) pt ++ "_" ++ show rank ++ "d"--opaqueName :: String -> [ValueDesc] -> String-opaqueName s _-  | valid = "opaque_" ++ s-  where valid = head s /= '_' &&-                not (isDigit $ head s) &&-                all ok s-        ok c = isAlphaNum c || c == '_'-opaqueName s vds = "opaque_" ++ hash (zipWith xor [0..] $ map ord (s ++ concatMap p vds))-  where p (ScalarValue pt signed _) =-          show (pt, signed)-        p (ArrayValue _ space pt signed dims) =-          show (space, pt, signed, length dims)--        -- FIXME: a stupid hash algorithm; may have collisions.-        hash = printf "%x" . foldl xor 0 . map (iter . (*0x45d9f3b) .-                                                iter . (*0x45d9f3b) .-                                                iter . fromIntegral)-        iter x = ((x::Word32) `shiftR` 16) `xor` x--criticalSection :: [C.BlockItem] -> [C.BlockItem]-criticalSection x = [C.citems|-                       lock_lock(&ctx->lock);-                       $items:x-                       lock_unlock(&ctx->lock);-                     |]--arrayLibraryFunctions :: Space -> PrimType -> Signedness -> [DimSize]-                      -> CompilerM op s [C.Definition]-arrayLibraryFunctions space pt signed shape = do-  let rank = length shape-      pt' = signedPrimTypeToCType signed pt-      name = arrayName pt signed rank-      arr_name = "futhark_" ++ name-      array_type = [C.cty|struct $id:arr_name|]--  new_array <- publicName $ "new_" ++ name-  new_raw_array <- publicName $ "new_raw_" ++ name-  free_array <- publicName $ "free_" ++ name-  values_array <- publicName $ "values_" ++ name-  values_raw_array <- publicName $ "values_raw_" ++ name-  shape_array <- publicName $ "shape_" ++ name--  let shape_names = [ "dim"++show i | i <- [0..rank-1] ]-      shape_params = [ [C.cparam|typename int64_t $id:k|] | k <- shape_names ]-      arr_size = cproduct [ [C.cexp|$id:k|] | k <- shape_names ]-      arr_size_array = cproduct [ [C.cexp|arr->shape[$int:i]|] | i <- [0..rank-1] ]-  copy <- asks envCopy--  memty <- rawMemCType space--  let prepare_new = do-        resetMem [C.cexp|arr->mem|] space-        allocMem [C.cexp|arr->mem|] [C.cexp|((size_t)$exp:arr_size) * sizeof($ty:pt')|] space-                 [C.cstm|return NULL;|]-        forM_ [0..rank-1] $ \i ->-          let dim_s = "dim"++show i-          in stm [C.cstm|arr->shape[$int:i] = $id:dim_s;|]--  new_body <- collect $ do-    prepare_new-    copy [C.cexp|arr->mem.mem|] [C.cexp|0|] space-         [C.cexp|data|] [C.cexp|0|] DefaultSpace-         [C.cexp|((size_t)$exp:arr_size) * sizeof($ty:pt')|]--  new_raw_body <- collect $ do-    prepare_new-    copy [C.cexp|arr->mem.mem|] [C.cexp|0|] space-         [C.cexp|data|] [C.cexp|offset|] space-         [C.cexp|((size_t)$exp:arr_size) * sizeof($ty:pt')|]--  free_body <- collect $ unRefMem [C.cexp|arr->mem|] space--  values_body <- collect $-    copy [C.cexp|data|] [C.cexp|0|] DefaultSpace-         [C.cexp|arr->mem.mem|] [C.cexp|0|] space-         [C.cexp|((size_t)$exp:arr_size_array) * sizeof($ty:pt')|]--  ctx_ty <- contextType--  headerDecl (ArrayDecl name)-    [C.cedecl|struct $id:arr_name;|]-  headerDecl (ArrayDecl name)-    [C.cedecl|$ty:array_type* $id:new_array($ty:ctx_ty *ctx, $ty:pt' *data, $params:shape_params);|]-  headerDecl (ArrayDecl name)-    [C.cedecl|$ty:array_type* $id:new_raw_array($ty:ctx_ty *ctx, $ty:memty data, int offset, $params:shape_params);|]-  headerDecl (ArrayDecl name)-    [C.cedecl|int $id:free_array($ty:ctx_ty *ctx, $ty:array_type *arr);|]-  headerDecl (ArrayDecl name)-    [C.cedecl|int $id:values_array($ty:ctx_ty *ctx, $ty:array_type *arr, $ty:pt' *data);|]-  headerDecl (ArrayDecl name)-    [C.cedecl|$ty:memty $id:values_raw_array($ty:ctx_ty *ctx, $ty:array_type *arr);|]-  headerDecl (ArrayDecl name)-    [C.cedecl|const typename int64_t* $id:shape_array($ty:ctx_ty *ctx, $ty:array_type *arr);|]--  return [C.cunit|-          $ty:array_type* $id:new_array($ty:ctx_ty *ctx, $ty:pt' *data, $params:shape_params) {-            $ty:array_type* bad = NULL;-            $ty:array_type *arr = ($ty:array_type*) malloc(sizeof($ty:array_type));-            if (arr == NULL) {-              return bad;-            }-            $items:(criticalSection new_body)-            return arr;-          }--          $ty:array_type* $id:new_raw_array($ty:ctx_ty *ctx, $ty:memty data, int offset,-                                            $params:shape_params) {-            $ty:array_type* bad = NULL;-            $ty:array_type *arr = ($ty:array_type*) malloc(sizeof($ty:array_type));-            if (arr == NULL) {-              return bad;-            }-            $items:(criticalSection new_raw_body)-            return arr;-          }--          int $id:free_array($ty:ctx_ty *ctx, $ty:array_type *arr) {-            $items:(criticalSection free_body)-            free(arr);-            return 0;-          }--          int $id:values_array($ty:ctx_ty *ctx, $ty:array_type *arr, $ty:pt' *data) {-            $items:(criticalSection values_body)-            return 0;-          }--          $ty:memty $id:values_raw_array($ty:ctx_ty *ctx, $ty:array_type *arr) {-            (void)ctx;-            return arr->mem.mem;-          }--          const typename int64_t* $id:shape_array($ty:ctx_ty *ctx, $ty:array_type *arr) {-            (void)ctx;-            return arr->shape;-          }-          |]--opaqueLibraryFunctions :: String -> [ValueDesc]-                       -> CompilerM op s [C.Definition]-opaqueLibraryFunctions desc vds = do-  name <- publicName $ opaqueName desc vds-  free_opaque <- publicName $ "free_" ++ opaqueName desc vds--  let opaque_type = [C.cty|struct $id:name|]--      freeComponent _ ScalarValue{} =-        return ()-      freeComponent i (ArrayValue _ _ pt signed shape) = do-        let rank = length shape-        free_array <- publicName $ "free_" ++ arrayName pt signed rank-        stm [C.cstm|if ((tmp = $id:free_array(ctx, obj->$id:(tupleField i))) != 0) {-                ret = tmp;-             }|]--  ctx_ty <- contextType--  free_body <- collect $ zipWithM_ freeComponent [0..] vds--  headerDecl (OpaqueDecl desc)-    [C.cedecl|int $id:free_opaque($ty:ctx_ty *ctx, $ty:opaque_type *obj);|]--  return [C.cunit|-          int $id:free_opaque($ty:ctx_ty *ctx, $ty:opaque_type *obj) {-            int ret = 0, tmp;-            $items:free_body-            free(obj);-            return ret;-          }-           |]--valueDescToCType :: ValueDesc -> CompilerM op s C.Type-valueDescToCType (ScalarValue pt signed _) =-  return $ signedPrimTypeToCType signed pt-valueDescToCType (ArrayValue mem space pt signed shape) = do-  let pt' = signedPrimTypeToCType signed pt-      rank = length shape-  exists <- gets $ lookup (pt',rank) . compArrayStructs-  case exists of-    Just (cty, _) -> return cty-    Nothing -> do-      memty <- memToCType mem space-      name <- publicName $ arrayName pt signed rank-      let struct = [C.cedecl|struct $id:name { $ty:memty mem; typename int64_t shape[$int:rank]; };|]-          stype = [C.cty|struct $id:name|]-      library <- arrayLibraryFunctions space pt signed shape-      modify $ \s -> s { compArrayStructs =-                           ((pt', rank), (stype, struct : library)) : compArrayStructs s-                       }-      return stype--opaqueToCType :: String -> [ValueDesc] -> CompilerM op s C.Type-opaqueToCType desc vds = do-  name <- publicName $ opaqueName desc vds-  exists <- gets $ lookup name . compOpaqueStructs-  case exists of-    Just (ty, _) -> return ty-    Nothing -> do-      members <- zipWithM field vds [(0::Int)..]-      let struct = [C.cedecl|struct $id:name { $sdecls:members };|]-          stype = [C.cty|struct $id:name|]-      headerDecl (OpaqueDecl desc) [C.cedecl|struct $id:name;|]-      library <- opaqueLibraryFunctions desc vds-      modify $ \s -> s { compOpaqueStructs =-                           (name, (stype, struct : library)) :-                           compOpaqueStructs s }-      return stype-  where field vd@ScalarValue{} i = do-          ct <- valueDescToCType vd-          return [C.csdecl|$ty:ct $id:(tupleField i);|]-        field vd i = do-          ct <- valueDescToCType vd-          return [C.csdecl|$ty:ct *$id:(tupleField i);|]--externalValueToCType :: ExternalValue -> CompilerM op s C.Type-externalValueToCType (TransparentValue vd) = valueDescToCType vd-externalValueToCType (OpaqueValue desc vds) = opaqueToCType desc vds--prepareEntryInputs :: [ExternalValue] -> CompilerM op s [C.Param]-prepareEntryInputs = zipWithM prepare [(0::Int)..]-  where prepare pno (TransparentValue vd) = do-          let pname = "in" ++ show pno-          ty <- prepareValue [C.cexp|$id:pname|] vd-          return [C.cparam|const $ty:ty $id:pname|]--        prepare pno (OpaqueValue desc vds) = do-          ty <- opaqueToCType desc vds-          let pname = "in" ++ show pno-              field i ScalarValue{} = [C.cexp|$id:pname->$id:(tupleField i)|]-              field i ArrayValue{} = [C.cexp|$id:pname->$id:(tupleField i)|]-          zipWithM_ prepareValue (zipWith field [0..] vds) vds-          return [C.cparam|const $ty:ty *$id:pname|]--        prepareValue src (ScalarValue pt signed name) = do-          let pt' = signedPrimTypeToCType signed pt-          stm [C.cstm|$id:name = $exp:src;|]-          return pt'--        prepareValue src vd@(ArrayValue mem _ _ _ shape) = do-          ty <- valueDescToCType vd--          stm [C.cstm|$exp:mem = $exp:src->mem;|]--          let rank = length shape-              maybeCopyDim (Var d) i =-                Just [C.cstm|$id:d = $exp:src->shape[$int:i];|]-              maybeCopyDim _ _ = Nothing--          stms $ catMaybes $ zipWith maybeCopyDim shape [0..rank-1]--          return [C.cty|$ty:ty*|]--prepareEntryOutputs :: [ExternalValue] -> CompilerM op s [C.Param]-prepareEntryOutputs = zipWithM prepare [(0::Int)..]-  where prepare pno (TransparentValue vd) = do-          let pname = "out" ++ show pno-          ty <- valueDescToCType vd--          case vd of-            ArrayValue{} -> do-              stm [C.cstm|assert((*$id:pname = ($ty:ty*) malloc(sizeof($ty:ty))) != NULL);|]-              prepareValue [C.cexp|*$id:pname|] vd-              return [C.cparam|$ty:ty **$id:pname|]-            ScalarValue{} -> do-              prepareValue [C.cexp|*$id:pname|] vd-              return [C.cparam|$ty:ty *$id:pname|]--        prepare pno (OpaqueValue desc vds) = do-          let pname = "out" ++ show pno-          ty <- opaqueToCType desc vds-          vd_ts <- mapM valueDescToCType vds--          stm [C.cstm|assert((*$id:pname = ($ty:ty*) malloc(sizeof($ty:ty))) != NULL);|]---          forM_ (zip3 [0..] vd_ts vds) $ \(i,ct,vd) -> do-            let field = [C.cexp|(*$id:pname)->$id:(tupleField i)|]-            case vd of-              ScalarValue{} -> return ()-              _ -> stm [C.cstm|assert(($exp:field = ($ty:ct*) malloc(sizeof($ty:ct))) != NULL);|]-            prepareValue field vd--          return [C.cparam|$ty:ty **$id:pname|]--        prepareValue dest (ScalarValue _ _ name) =-          stm [C.cstm|$exp:dest = $id:name;|]--        prepareValue dest (ArrayValue mem _ _ _ shape) = do-          stm [C.cstm|$exp:dest->mem = $id:mem;|]--          let rank = length shape-              maybeCopyDim (Constant x) i =-                [C.cstm|$exp:dest->shape[$int:i] = $exp:x;|]-              maybeCopyDim (Var d) i =-                [C.cstm|$exp:dest->shape[$int:i] = $id:d;|]-          stms $ zipWith maybeCopyDim shape [0..rank-1]--onEntryPoint :: Name -> Function op-             -> CompilerM op s (C.Definition, C.Definition, C.Initializer)-onEntryPoint fname function@(Function _ outputs inputs _ results args) = do-  let out_args = map (\p -> [C.cexp|&$id:(paramName p)|]) outputs-      in_args = map (\p -> [C.cexp|$id:(paramName p)|]) inputs--  inputdecls <- collect $ mapM_ stubParam inputs-  outputdecls <- collect $ mapM_ stubParam outputs--  let entry_point_name = nameToString fname-  entry_point_function_name <- publicName $ "entry_" ++ entry_point_name--  (entry_point_input_params, unpack_entry_inputs) <--    collect' $ prepareEntryInputs args-  (entry_point_output_params, pack_entry_outputs) <--    collect' $ prepareEntryOutputs results--  (cli_entry_point, cli_init) <- cliEntryPoint fname function--  ctx_ty <- contextType--  headerDecl EntryDecl [C.cedecl|int $id:entry_point_function_name-                                     ($ty:ctx_ty *ctx,-                                      $params:entry_point_output_params,-                                      $params:entry_point_input_params);|]--  return ([C.cedecl|int $id:entry_point_function_name-                         ($ty:ctx_ty *ctx,-                          $params:entry_point_output_params,-                          $params:entry_point_input_params) {-    $items:inputdecls-    $items:outputdecls--    lock_lock(&ctx->lock);--    $items:unpack_entry_inputs--    int ret = $id:(funName fname)(ctx, $args:out_args, $args:in_args);--    if (ret == 0) {-      $items:pack_entry_outputs-    }--    lock_unlock(&ctx->lock);--    return ret;-}-    |],-          cli_entry_point,-          cli_init)-  where stubParam (MemParam name space) =-          declMem name space-        stubParam (ScalarParam name ty) = do-          let ty' = primTypeToCType ty-          decl [C.cdecl|$ty:ty' $id:name;|]----- CLI interface------ Our strategy for CLI entry points is to parse everything into--- host memory ('DefaultSpace') and copy the result into host memory--- after the entry point has returned.  We have some ad-hoc frobbery--- to copy the host-level memory blocks to another memory space if--- necessary.  This will break if the Futhark entry point uses--- non-trivial index functions for its input or output.------ The idea here is to keep the nastyness in the wrapper, whilst not--- messing up anything else.--printPrimStm :: (C.ToExp a, C.ToExp b) => a -> b -> PrimType -> Signedness -> C.Stm-printPrimStm dest val bt ept =-  [C.cstm|write_scalar($exp:dest, binary_output, &$exp:(primTypeInfo bt ept), &$exp:val);|]---- | Return a statement printing the given external value.-printStm :: ExternalValue -> C.Exp -> CompilerM op s C.Stm-printStm (OpaqueValue desc _) _ =-  return [C.cstm|printf("#<opaque %s>", $string:desc);|]-printStm (TransparentValue (ScalarValue bt ept _)) e =-  return $ printPrimStm [C.cexp|stdout|] e bt ept-printStm (TransparentValue (ArrayValue _ _ bt ept shape)) e = do-  values_array <- publicName $ "values_" ++ name-  shape_array <- publicName $ "shape_" ++ name-  let num_elems = cproduct [ [C.cexp|$id:shape_array(ctx, $exp:e)[$int:i]|] | i <- [0..rank-1] ]-  return [C.cstm|{-      $ty:bt' *arr = calloc(sizeof($ty:bt'), $exp:num_elems);-      assert(arr != NULL);-      assert($id:values_array(ctx, $exp:e, arr) == 0);-      write_array(stdout, binary_output, &$exp:(primTypeInfo bt ept), arr,-                  $id:shape_array(ctx, $exp:e), $int:rank);-      free(arr);-    }|]-  where rank = length shape-        bt' = primTypeToCType bt-        name = arrayName bt ept rank--readPrimStm :: C.ToExp a => a -> Int -> PrimType -> Signedness -> C.Stm-readPrimStm place i t ept =-  [C.cstm|if (read_scalar(&$exp:(primTypeInfo t ept),&$exp:place) != 0) {-        futhark_panic(1, "Error when reading input #%d of type %s (errno: %s).\n",-              $int:i,-              $exp:(primTypeInfo t ept).type_name,-              strerror(errno));-      }|]--readInputs :: [ExternalValue] -> CompilerM op s [(C.Stm, C.Stm, C.Stm, C.Exp)]-readInputs = zipWithM readInput [0..]--readInput :: Int -> ExternalValue -> CompilerM op s (C.Stm, C.Stm, C.Stm, C.Exp)-readInput i (OpaqueValue desc _) = do-  stm [C.cstm|futhark_panic(1, "Cannot read input #%d of type %s\n", $int:i, $string:desc);|]-  return ([C.cstm|;|], [C.cstm|;|], [C.cstm|;|], [C.cexp|NULL|])-readInput i (TransparentValue (ScalarValue t ept _)) = do-  dest <- newVName "read_value"-  item [C.citem|$ty:(primTypeToCType t) $id:dest;|]-  stm $ readPrimStm dest i t ept-  return ([C.cstm|;|], [C.cstm|;|], [C.cstm|;|], [C.cexp|$id:dest|])-readInput i (TransparentValue vd@(ArrayValue _ _ t ept dims)) = do-  dest <- newVName "read_value"-  shape <- newVName "read_shape"-  arr <- newVName "read_arr"-  ty <- valueDescToCType vd-  item [C.citem|$ty:ty *$id:dest;|]--  let t' = signedPrimTypeToCType ept t-      rank = length dims-      name = arrayName t ept rank-      dims_exps = [ [C.cexp|$id:shape[$int:j]|] | j <- [0..rank-1] ]-      dims_s = concat $ replicate rank "[]"--  new_array <- publicName $ "new_" ++ name-  free_array <- publicName $ "free_" ++ name--  items [C.citems|-     typename int64_t $id:shape[$int:rank];-     $ty:t' *$id:arr = NULL;-     errno = 0;-     if (read_array(&$exp:(primTypeInfo t ept),-                    (void**) &$id:arr,-                    $id:shape,-                    $int:(length dims))-         != 0) {-       futhark_panic(1, "Cannot read input #%d of type %s%s (errno: %s).\n",-                 $int:i,-                 $string:dims_s,-                 $exp:(primTypeInfo t ept).type_name,-                 strerror(errno));-     }|]--  return ([C.cstm|assert(($exp:dest = $id:new_array(ctx, $id:arr, $args:dims_exps)) != 0);|],-          [C.cstm|assert($id:free_array(ctx, $exp:dest) == 0);|],-          [C.cstm|free($id:arr);|],-          [C.cexp|$id:dest|])--prepareOutputs :: [ExternalValue] -> CompilerM op s [(C.Exp, C.Stm)]-prepareOutputs = mapM prepareResult-  where prepareResult ev = do-          ty <- externalValueToCType ev-          result <- newVName "result"--          case ev of-            TransparentValue ScalarValue{} -> do-              item [C.citem|$ty:ty $id:result;|]-              return ([C.cexp|$id:result|], [C.cstm|;|])-            TransparentValue (ArrayValue _ _ t ept dims) -> do-              let name = arrayName t ept $ length dims-              free_array <- publicName $ "free_" ++ name-              item [C.citem|$ty:ty *$id:result;|]-              return ([C.cexp|$id:result|],-                      [C.cstm|assert($id:free_array(ctx, $exp:result) == 0);|])-            OpaqueValue desc vds -> do-              free_opaque <- publicName $ "free_" ++ opaqueName desc vds-              item [C.citem|$ty:ty *$id:result;|]-              return ([C.cexp|$id:result|],-                      [C.cstm|assert($id:free_opaque(ctx, $exp:result) == 0);|])--printResult :: [(ExternalValue,C.Exp)] -> CompilerM op s [C.Stm]-printResult vs = fmap concat $ forM vs $ \(v,e) -> do-  p <- printStm v e-  return [p, [C.cstm|printf("\n");|]]--cliEntryPoint :: Name-              -> FunctionT a-              -> CompilerM op s (C.Definition, C.Initializer)-cliEntryPoint fname (Function _ _ _ _ results args) = do-  ((pack_input, free_input, free_parsed, input_args), input_items) <--    collect' $ unzip4 <$> readInputs args--  ((output_vals, free_outputs), output_decls) <--    collect' $ unzip <$> prepareOutputs results-  printstms <- printResult $ zip results output_vals--  ctx_ty <- contextType-  sync_ctx <- publicName "context_sync"-  error_ctx <- publicName "context_get_error"--  let entry_point_name = nameToString fname-      cli_entry_point_function_name = "futrts_cli_entry_" ++ entry_point_name-  entry_point_function_name <- publicName $ "entry_" ++ entry_point_name--  pause_profiling <- publicName "context_pause_profiling"-  unpause_profiling <- publicName "context_unpause_profiling"--  let run_it = [C.citems|-                  int r;-                  // Run the program once.-                  $stms:pack_input-                  if ($id:sync_ctx(ctx) != 0) {-                    futhark_panic(1, "%s", $id:error_ctx(ctx));-                  };-                  // Only profile last run.-                  if (profile_run) {-                    $id:unpause_profiling(ctx);-                  }-                  t_start = get_wall_time();-                  r = $id:entry_point_function_name(ctx,-                                                    $args:(map addrOf output_vals),-                                                    $args:input_args);-                  if (r != 0) {-                    futhark_panic(1, "%s", $id:error_ctx(ctx));-                  }-                  if ($id:sync_ctx(ctx) != 0) {-                    futhark_panic(1, "%s", $id:error_ctx(ctx));-                  };-                  if (profile_run) {-                    $id:pause_profiling(ctx);-                  }-                  t_end = get_wall_time();-                  long int elapsed_usec = t_end - t_start;-                  if (time_runs && runtime_file != NULL) {-                    fprintf(runtime_file, "%lld\n", (long long) elapsed_usec);-                    fflush(runtime_file);-                  }-                  $stms:free_input-                |]--  return ([C.cedecl|static void $id:cli_entry_point_function_name($ty:ctx_ty *ctx) {-    typename int64_t t_start, t_end;-    int time_runs = 0, profile_run = 0;--    // We do not want to profile all the initialisation.-    $id:pause_profiling(ctx);--    // Declare and read input.-    set_binary_mode(stdin);-    $items:input_items--    if (end_of_input() != 0) {-      futhark_panic(1, "Expected EOF on stdin after reading input for %s.\n", $string:(quote (pretty fname)));-    }--    $items:output_decls--    // Warmup run-    if (perform_warmup) {-      $items:run_it-      $stms:free_outputs-    }-    time_runs = 1;-    // Proper run.-    for (int run = 0; run < num_runs; run++) {-      // Only profile last run.-      profile_run = run == num_runs -1;-      $items:run_it-      if (run < num_runs-1) {-        $stms:free_outputs-      }-    }--    // Free the parsed input.-    $stms:free_parsed--    // Print the final result.-    if (binary_output) {-      set_binary_mode(stdout);-    }-    $stms:printstms--    $stms:free_outputs-  }-                |],-          [C.cinit|{ .name = $string:entry_point_name,-                      .fun = $id:cli_entry_point_function_name }|]-    )--benchmarkOptions :: [Option]-benchmarkOptions =-   [ Option { optionLongName = "write-runtime-to"-            , optionShortName = Just 't'-            , optionArgument = RequiredArgument "FILE"-            , optionAction = set_runtime_file-            }-   , Option { optionLongName = "runs"-            , optionShortName = Just 'r'-            , optionArgument = RequiredArgument "INT"-            , optionAction = set_num_runs-            }-   , Option { optionLongName = "debugging"-            , optionShortName = Just 'D'-            , optionArgument = NoArgument-            , optionAction = [C.cstm|futhark_context_config_set_debugging(cfg, 1);|]-            }-   , Option { optionLongName = "log"-            , optionShortName = Just 'L'-            , optionArgument = NoArgument-            , optionAction = [C.cstm|futhark_context_config_set_logging(cfg, 1);|]-            }-   , Option { optionLongName = "entry-point"-            , optionShortName = Just 'e'-            , optionArgument = RequiredArgument "NAME"-            , optionAction = [C.cstm|if (entry_point != NULL) entry_point = optarg;|]-            }-   , Option { optionLongName = "binary-output"-            , optionShortName = Just 'b'-            , optionArgument = NoArgument-            , optionAction = [C.cstm|binary_output = 1;|]-            }-   ]-  where set_runtime_file = [C.cstm|{-          runtime_file = fopen(optarg, "w");-          if (runtime_file == NULL) {-            futhark_panic(1, "Cannot open %s: %s\n", optarg, strerror(errno));-          }-        }|]-        set_num_runs = [C.cstm|{-          num_runs = atoi(optarg);-          perform_warmup = 1;-          if (num_runs <= 0) {-            futhark_panic(1, "Need a positive number of runs, not %s\n", optarg);-          }-        }|]---- | The result of compilation to C is four parts, which can be put--- together in various ways.  The obvious way is to concatenate all of--- them, which yields a CLI program.  Another is to compile the--- library part by itself, and use the header file to call into it.-data CParts = CParts { cHeader :: String-                     , cUtils :: String-                       -- ^ Utility definitions that must be visible-                       -- to both CLI and library parts.-                     , cCLI :: String-                     , cLib :: String-                     }---- We may generate variables that are never used (e.g. for--- certificates) or functions that are never called (e.g. unused--- intrinsics), and generated code may have other cosmetic issues that--- compilers warn about.  We disable these warnings to not clutter the--- compilation logs.-disableWarnings :: String-disableWarnings = pretty [C.cunit|-$esc:("#ifdef __GNUC__")-$esc:("#pragma GCC diagnostic ignored \"-Wunused-function\"")-$esc:("#pragma GCC diagnostic ignored \"-Wunused-variable\"")-$esc:("#pragma GCC diagnostic ignored \"-Wparentheses\"")-$esc:("#pragma GCC diagnostic ignored \"-Wunused-label\"")-$esc:("#endif")--$esc:("#ifdef __clang__")-$esc:("#pragma clang diagnostic ignored \"-Wunused-function\"")-$esc:("#pragma clang diagnostic ignored \"-Wunused-variable\"")-$esc:("#pragma clang diagnostic ignored \"-Wparentheses\"")-$esc:("#pragma clang diagnostic ignored \"-Wunused-label\"")-$esc:("#endif")-|]---- | Produce header and implementation files.-asLibrary :: CParts -> (String, String)-asLibrary parts = ("#pragma once\n\n" <> cHeader parts,-                   disableWarnings <> cHeader parts <> cUtils parts <> cLib parts)---- | As executable with command-line interface.-asExecutable :: CParts -> String-asExecutable (CParts a b c d) = disableWarnings <> a <> b <> c <> d---- | Compile imperative program to a C program.  Always uses the--- function named "main" as entry point, so make sure it is defined.-compileProg :: MonadFreshNames m =>-               String-            -> Operations op ()-            -> CompilerM op () ()-            -> String-            -> [Space]-            -> [Option]-            -> Definitions op-            -> m CParts-compileProg backend ops extra header_extra spaces options prog = do-  src <- getNameSource-  let ((prototypes, definitions, entry_points), endstate) =-        runCompilerM ops src () compileProg'-      (entry_point_decls, cli_entry_point_decls, entry_point_inits) =-        unzip3 entry_points-      option_parser = generateOptionParser "parse_options" $ benchmarkOptions++options--  let headerdefs = [C.cunit|-$esc:("// Headers\n")-$esc:("#include <stdint.h>")-$esc:("#include <stddef.h>")-$esc:("#include <stdbool.h>")-$esc:(header_extra)--$esc:("\n// Initialisation\n")-$edecls:(initDecls endstate)--$esc:("\n// Arrays\n")-$edecls:(arrayDecls endstate)--$esc:("\n// Opaque values\n")-$edecls:(opaqueDecls endstate)--$esc:("\n// Entry points\n")-$edecls:(entryDecls endstate)--$esc:("\n// Miscellaneous\n")-$edecls:(miscDecls endstate)-$esc:("#define FUTHARK_BACKEND_"++backend)-                           |]--  let utildefs = [C.cunit|-$esc:("#include <stdio.h>")-$esc:("#include <stdlib.h>")-$esc:("#include <stdbool.h>")-$esc:("#include <math.h>")-$esc:("#include <stdint.h>")-// If NDEBUG is set, the assert() macro will do nothing. Since Futhark-// (unfortunately) makes use of assert() for error detection (and even some-// side effects), we want to avoid that.-$esc:("#undef NDEBUG")-$esc:("#include <assert.h>")-$esc:("#include <stdarg.h>")--$esc:util_h--$esc:timing_h-|]--  let clidefs = [C.cunit|-$esc:("#include <string.h>")-$esc:("#include <inttypes.h>")-$esc:("#include <errno.h>")-$esc:("#include <ctype.h>")-$esc:("#include <errno.h>")-$esc:("#include <getopt.h>")--$esc:values_h--$esc:("#define __private")--static int binary_output = 0;-static typename FILE *runtime_file;-static int perform_warmup = 0;-static int num_runs = 1;-// If the entry point is NULL, the program will terminate after doing initialisation and such.-static const char *entry_point = "main";--$esc:tuning_h--$func:option_parser--$edecls:cli_entry_point_decls--typedef void entry_point_fun(struct futhark_context*);--struct entry_point_entry {-  const char *name;-  entry_point_fun *fun;-};--int main(int argc, char** argv) {-  fut_progname = argv[0];--  struct entry_point_entry entry_points[] = {-    $inits:entry_point_inits-  };--  struct futhark_context_config *cfg = futhark_context_config_new();-  assert(cfg != NULL);--  int parsed_options = parse_options(cfg, argc, argv);-  argc -= parsed_options;-  argv += parsed_options;--  if (argc != 0) {-    futhark_panic(1, "Excess non-option: %s\n", argv[0]);-  }--  struct futhark_context *ctx = futhark_context_new(cfg);-  assert (ctx != NULL);--  char* error = futhark_context_get_error(ctx);-  if (error != NULL) {-    futhark_panic(1, "%s", error);-  }--  if (entry_point != NULL) {-    int num_entry_points = sizeof(entry_points) / sizeof(entry_points[0]);-    entry_point_fun *entry_point_fun = NULL;-    for (int i = 0; i < num_entry_points; i++) {-      if (strcmp(entry_points[i].name, entry_point) == 0) {-        entry_point_fun = entry_points[i].fun;-        break;-      }-    }--    if (entry_point_fun == NULL) {-      fprintf(stderr, "No entry point '%s'.  Select another with --entry-point.  Options are:\n",-                      entry_point);-      for (int i = 0; i < num_entry_points; i++) {-        fprintf(stderr, "%s\n", entry_points[i].name);-      }-      return 1;-    }--    entry_point_fun(ctx);--    if (runtime_file != NULL) {-      fclose(runtime_file);-    }--    char *report = futhark_context_report(ctx);-    fputs(report, stderr);-    free(report);-  }--  futhark_context_free(ctx);-  futhark_context_config_free(cfg);-  return 0;-}-                        |]--  let early_decls = DL.toList $ compEarlyDecls endstate-  let lib_decls = DL.toList $ compLibDecls endstate-  let libdefs = [C.cunit|-$esc:("#ifdef _MSC_VER\n#define inline __inline\n#endif")-$esc:("#include <string.h>")-$esc:("#include <inttypes.h>")-$esc:("#include <ctype.h>")-$esc:("#include <errno.h>")-$esc:("#include <assert.h>")--$esc:(header_extra)--$esc:lock_h--$edecls:builtin--$edecls:early_decls--$edecls:prototypes--$edecls:lib_decls--$edecls:(map funcToDef definitions)--$edecls:(arrayDefinitions endstate)--$edecls:(opaqueDefinitions endstate)--$edecls:entry_point_decls-  |]--  return $ CParts (pretty headerdefs) (pretty utildefs) (pretty clidefs) (pretty libdefs)-    where-      compileProg' = do-          let Definitions consts (Functions funs) = prog--          (memstructs, memfuns, memreport) <- unzip3 <$> mapM defineMemorySpace spaces--          get_consts <- compileConstants consts--          ctx_ty <- contextType--          (prototypes, definitions) <--            unzip <$> mapM (compileFun get_consts [[C.cparam|$ty:ctx_ty *ctx|]]) funs--          mapM_ earlyDecl memstructs-          entry_points <--            mapM (uncurry onEntryPoint) $ filter (functionEntry . snd) funs--          extra--          mapM_ earlyDecl $ concat memfuns--          commonLibFuns memreport--          return (prototypes, definitions, entry_points)--      funcToDef func = C.FuncDef func loc-        where loc = case func of-                       C.OldFunc _ _ _ _ _ _ l -> l-                       C.Func _ _ _ _ _ l      -> l--      builtin = cIntOps ++ cFloat32Ops ++ cFloat64Ops ++ cFloatConvOps ++-                cFloat32Funs ++ cFloat64Funs--      util_h  = $(embedStringFile "rts/c/util.h")-      values_h = $(embedStringFile "rts/c/values.h")-      timing_h = $(embedStringFile "rts/c/timing.h")-      lock_h   = $(embedStringFile "rts/c/lock.h")-      tuning_h = $(embedStringFile "rts/c/tuning.h")--commonLibFuns :: [C.BlockItem] -> CompilerM op s ()-commonLibFuns memreport = do-  ctx <- contextType-  profilereport <- gets $ DL.toList . compProfileItems--  publicDef_ "context_report" MiscDecl $ \s ->-    ([C.cedecl|char* $id:s($ty:ctx *ctx);|],-     [C.cedecl|char* $id:s($ty:ctx *ctx) {-                 struct str_builder builder;-                 str_builder_init(&builder);-                 if (ctx->detail_memory || ctx->profiling) {-                   $items:memreport-                 }-                 if (ctx->profiling) {-                   $items:profilereport-                 }-                 return builder.str;-               }|])--  publicDef_ "context_get_error" MiscDecl $ \s ->-    ([C.cedecl|char* $id:s($ty:ctx* ctx);|],-     [C.cedecl|char* $id:s($ty:ctx* ctx) {-                         char* error = ctx->error;-                         ctx->error = NULL;-                         return error;-                       }|])--  publicDef_ "context_pause_profiling" MiscDecl $ \s ->-    ([C.cedecl|void $id:s($ty:ctx* ctx);|],-     [C.cedecl|void $id:s($ty:ctx* ctx) {-                 ctx->profiling_paused = 1;-               }|])--  publicDef_ "context_unpause_profiling" MiscDecl $ \s ->-    ([C.cedecl|void $id:s($ty:ctx* ctx);|],-     [C.cedecl|void $id:s($ty:ctx* ctx) {-                 ctx->profiling_paused = 0;-               }|])--compileConstants :: Constants op -> CompilerM op s [C.BlockItem]-compileConstants (Constants ps init_consts) = do-  ctx_ty <- contextType-  const_fields <- mapM constParamField ps-  -- Avoid an empty struct, as that is apparently undefined behaviour.-  let const_fields' | null const_fields = [[C.csdecl|int dummy;|]]-                    | otherwise = const_fields-  contextField "constants" [C.cty|struct { $sdecls:const_fields' }|] Nothing-  earlyDecl [C.cedecl|int init_constants($ty:ctx_ty*);|]-  earlyDecl [C.cedecl|int free_constants($ty:ctx_ty*);|]--  -- We locally define macros for the constants, so that when we-  -- generate assignments to local variables, we actually assign into-  -- the constants struct.  This is not needed for functions, because-  -- they can only read constants, not write them.-  let (defs, undefs) = unzip $ map constMacro ps-  init_consts' <- blockScope $ do-                    mapM_ resetMemConst ps-                    compileCode init_consts-  libDecl [C.cedecl|int init_constants($ty:ctx_ty *ctx) {-      (void)ctx;-      int err = 0;-      $items:defs-      $items:init_consts'-      $items:undefs-      cleanup:-      return err;-    }|]--  free_consts <- collect $ mapM_ freeConst ps-  libDecl [C.cedecl|int free_constants($ty:ctx_ty *ctx) {-      (void)ctx;-      $items:free_consts-      return 0;-    }|]--  mapM getConst ps--  where constParamField (ScalarParam name bt) = do-          let ctp = primTypeToCType bt-          return [C.csdecl|$ty:ctp $id:name;|]-        constParamField (MemParam name space) = do-          ty <- memToCType name space-          return [C.csdecl|$ty:ty $id:name;|]--        constMacro p = ([C.citem|$escstm:def|], [C.citem|$escstm:undef|])-          where p' = pretty (C.toIdent (paramName p) mempty)-                def = "#define " ++ p' ++ " (" ++ "ctx->constants." ++ p' ++ ")"-                undef = "#undef " ++ p'--        resetMemConst ScalarParam{} = return ()-        resetMemConst (MemParam name space) = resetMem name space--        freeConst ScalarParam{} = return ()-        freeConst (MemParam name space) = unRefMem [C.cexp|ctx->constants.$id:name|] space--        getConst (ScalarParam name bt) = do-          let ctp = primTypeToCType bt-          return [C.citem|$ty:ctp $id:name = ctx->constants.$id:name;|]-        getConst (MemParam name space) = do-          ty <- memToCType name space-          return [C.citem|$ty:ty $id:name = ctx->constants.$id:name;|]--cachingMemory :: M.Map VName Space-              -> ([C.BlockItem] -> [C.Stm] -> CompilerM op s a)-              -> CompilerM op s a-cachingMemory lexical f = do-  -- We only consider lexical 'DefaultSpace' memory blocks to be-  -- cached.  This is not a deep technical restriction, but merely a-  -- heuristic based on GPU memory usually involving larger-  -- allocations, that do not suffer from the overhead of reference-  -- counting.-  let cached = M.keys $ M.filter (==DefaultSpace) lexical--  cached' <- forM cached $ \mem -> do-    size <- newVName $ pretty mem <> "_cached_size"-    return (mem, size)--  let lexMem env =-        env { envCachedMem =-                M.fromList (map (first (`C.toExp` noLoc)) cached')-                <> envCachedMem env-            }--      declCached (mem, size) =-        [[C.citem|size_t $id:size = 0;|],-         [C.citem|$ty:defaultMemBlockType $id:mem = NULL;|]]--      freeCached (mem, _) =-        [C.cstm|free($id:mem);|]--  local lexMem $ f (concatMap declCached cached') (map freeCached cached')--compileFun :: [C.BlockItem] -> [C.Param] -> (Name, Function op) -> CompilerM op s (C.Definition, C.Func)-compileFun get_constants extra (fname, func@(Function _ outputs inputs body _ _)) = do-  (outparams, out_ptrs) <- unzip <$> mapM compileOutput outputs-  inparams <- mapM compileInput inputs--  cachingMemory (lexicalMemoryUsage func) $ \decl_cached free_cached -> do-    body' <- blockScope $ compileFunBody out_ptrs outputs body--    return ([C.cedecl|static int $id:(funName fname)($params:extra, $params:outparams, $params:inparams);|],-            [C.cfun|static int $id:(funName fname)($params:extra, $params:outparams, $params:inparams) {-               int err = 0;-               $items:decl_cached-               $items:get_constants-               $items:body'-              cleanup:-               {}-               $stms:free_cached-               return err;-  }|])--  where compileInput (ScalarParam name bt) = do-          let ctp = primTypeToCType bt-          return [C.cparam|$ty:ctp $id:name|]-        compileInput (MemParam name space) = do-          ty <- memToCType name space-          return [C.cparam|$ty:ty $id:name|]--        compileOutput (ScalarParam name bt) = do-          let ctp = primTypeToCType bt-          p_name <- newVName $ "out_" ++ baseString name-          return ([C.cparam|$ty:ctp *$id:p_name|], [C.cexp|$id:p_name|])-        compileOutput (MemParam name space) = do-          ty <- memToCType name space-          p_name <- newVName $ baseString name ++ "_p"-          return ([C.cparam|$ty:ty *$id:p_name|], [C.cexp|$id:p_name|])--compilePrimValue :: PrimValue -> C.Exp--compilePrimValue (IntValue (Int8Value k)) = [C.cexp|$int:k|]-compilePrimValue (IntValue (Int16Value k)) = [C.cexp|$int:k|]-compilePrimValue (IntValue (Int32Value k)) = [C.cexp|$int:k|]-compilePrimValue (IntValue (Int64Value k)) = [C.cexp|$int:k|]--compilePrimValue (FloatValue (Float64Value x))-  | isInfinite x =-      if x > 0 then [C.cexp|INFINITY|] else [C.cexp|-INFINITY|]-  | isNaN x =-      [C.cexp|NAN|]-  | otherwise =-      [C.cexp|$double:x|]-compilePrimValue (FloatValue (Float32Value x))-  | isInfinite x =-      if x > 0 then [C.cexp|INFINITY|] else [C.cexp|-INFINITY|]-  | isNaN x =-      [C.cexp|NAN|]-  | otherwise =-      [C.cexp|$float:x|]--compilePrimValue (BoolValue b) =-  [C.cexp|$int:b'|]-  where b' :: Int-        b' = if b then 1 else 0--compilePrimValue Checked =-  [C.cexp|0|]--derefPointer :: C.Exp -> C.Exp -> C.Type -> C.Exp-derefPointer ptr i res_t =-  [C.cexp|(($ty:res_t)$exp:ptr)[$exp:i]|]--volQuals :: Volatility -> [C.TypeQual]-volQuals Volatile = [C.ctyquals|volatile|]-volQuals Nonvolatile = []--writeScalarPointerWithQuals :: PointerQuals op s -> WriteScalar op s-writeScalarPointerWithQuals quals_f dest i elemtype space vol v = do-  quals <- quals_f space-  let quals' = volQuals vol ++ quals-      deref = derefPointer dest i-              [C.cty|$tyquals:quals' $ty:elemtype*|]-  stm [C.cstm|$exp:deref = $exp:v;|]--readScalarPointerWithQuals :: PointerQuals op s -> ReadScalar op s-readScalarPointerWithQuals quals_f dest i elemtype space vol = do-  quals <- quals_f space-  let quals' = volQuals vol ++ quals-  return $ derefPointer dest i [C.cty|$tyquals:quals' $ty:elemtype*|]--compileExpToName :: String -> PrimType -> Exp -> CompilerM op s VName-compileExpToName _ _ (LeafExp (ScalarVar v) _) =-  return v-compileExpToName desc t e = do-  desc' <- newVName desc-  e' <- compileExp e-  decl [C.cdecl|$ty:(primTypeToCType t) $id:desc' = $e';|]-  return desc'--compileExp :: Exp -> CompilerM op s C.Exp--compileExp = compilePrimExp compileLeaf-  where compileLeaf (ScalarVar src) =-          return [C.cexp|$id:src|]--        compileLeaf (Index src (Count iexp) restype DefaultSpace vol) = do-          src' <- rawMem src-          derefPointer src'-            <$> compileExp iexp-            <*> pure [C.cty|$tyquals:(volQuals vol) $ty:(primTypeToCType restype)*|]--        compileLeaf (Index src (Count iexp) restype (Space space) vol) =-          join $ asks envReadScalar-          <*> rawMem src <*> compileExp iexp-          <*> pure (primTypeToCType restype) <*> pure space <*> pure vol--        compileLeaf (Index src (Count iexp) _ ScalarSpace{} _) = do-          iexp' <- compileExp iexp-          return [C.cexp|$id:src[$exp:iexp']|]--        compileLeaf (SizeOf t) =-          return [C.cexp|(typename int32_t)sizeof($ty:t')|]-          where t' = primTypeToCType t---- | Tell me how to compile a @v@, and I'll Compile any @PrimExp v@ for you.-compilePrimExp :: Monad m => (v -> m C.Exp) -> PrimExp v -> m C.Exp--compilePrimExp _ (ValueExp val) =-  return $ compilePrimValue val--compilePrimExp f (LeafExp v _) =-  f v--compilePrimExp f (UnOpExp Complement{} x) = do-  x' <- compilePrimExp f x-  return [C.cexp|~$exp:x'|]--compilePrimExp f (UnOpExp Not{} x) = do-  x' <- compilePrimExp f x-  return [C.cexp|!$exp:x'|]--compilePrimExp f (UnOpExp Abs{} x) = do-  x' <- compilePrimExp f x-  return [C.cexp|abs($exp:x')|]--compilePrimExp f (UnOpExp (FAbs Float32) x) = do-  x' <- compilePrimExp f x-  return [C.cexp|(float)fabs($exp:x')|]--compilePrimExp f (UnOpExp (FAbs Float64) x) = do-  x' <- compilePrimExp f x-  return [C.cexp|fabs($exp:x')|]--compilePrimExp f (UnOpExp SSignum{} x) = do-  x' <- compilePrimExp f x-  return [C.cexp|($exp:x' > 0) - ($exp:x' < 0)|]--compilePrimExp f (UnOpExp USignum{} x) = do-  x' <- compilePrimExp f x-  return [C.cexp|($exp:x' > 0) - ($exp:x' < 0) != 0|]--compilePrimExp f (CmpOpExp cmp x y) = do-  x' <- compilePrimExp f x-  y' <- compilePrimExp f y-  return $ case cmp of-    CmpEq{} -> [C.cexp|$exp:x' == $exp:y'|]--    FCmpLt{} -> [C.cexp|$exp:x' < $exp:y'|]-    FCmpLe{} -> [C.cexp|$exp:x' <= $exp:y'|]--    CmpLlt{} -> [C.cexp|$exp:x' < $exp:y'|]-    CmpLle{} -> [C.cexp|$exp:x' <= $exp:y'|]--    _ -> [C.cexp|$id:(pretty cmp)($exp:x', $exp:y')|]--compilePrimExp f (ConvOpExp conv x) = do-  x' <- compilePrimExp f x-  return [C.cexp|$id:(pretty conv)($exp:x')|]--compilePrimExp f (BinOpExp bop x y) = do-  x' <- compilePrimExp f x-  y' <- compilePrimExp f y-  -- Note that integer addition, subtraction, and multiplication with-  -- OverflowWrap are not handled by explicit operators, but rather by-  -- functions.  This is because we want to implicitly convert them to-  -- unsigned numbers, so we can do overflow without invoking-  -- undefined behaviour.-  return $ case bop of-             Add _ OverflowUndef -> [C.cexp|$exp:x' + $exp:y'|]-             Sub _ OverflowUndef -> [C.cexp|$exp:x' - $exp:y'|]-             Mul _ OverflowUndef -> [C.cexp|$exp:x' * $exp:y'|]-             FAdd{} -> [C.cexp|$exp:x' + $exp:y'|]-             FSub{} -> [C.cexp|$exp:x' - $exp:y'|]-             FMul{} -> [C.cexp|$exp:x' * $exp:y'|]-             FDiv{} -> [C.cexp|$exp:x' / $exp:y'|]-             Xor{} -> [C.cexp|$exp:x' ^ $exp:y'|]-             And{} -> [C.cexp|$exp:x' & $exp:y'|]-             Or{} -> [C.cexp|$exp:x' | $exp:y'|]-             Shl{} -> [C.cexp|$exp:x' << $exp:y'|]-             LogAnd{} -> [C.cexp|$exp:x' && $exp:y'|]-             LogOr{} -> [C.cexp|$exp:x' || $exp:y'|]-             _ -> [C.cexp|$id:(pretty bop)($exp:x', $exp:y')|]--compilePrimExp f (FunExp h args _) = do-  args' <- mapM (compilePrimExp f) args-  return [C.cexp|$id:(funName (nameFromString h))($args:args')|]--compileCode :: Code op -> CompilerM op s ()--compileCode (Op op) =-  join $ asks envOpCompiler <*> pure op--compileCode Skip = return ()--compileCode (Comment s code) = do-  xs <- blockScope $ compileCode code-  let comment = "// " ++ s-  stm [C.cstm|$comment:comment-              { $items:xs }-             |]--compileCode (DebugPrint s (Just e)) = do-  e' <- compileExp e-  stm [C.cstm|if (ctx->debugging) {-          fprintf(stderr, $string:fmtstr, $exp:s, ($ty:ety)$exp:e', '\n');-       }|]-  where (fmt, ety) = case primExpType e of-                       IntType _ -> ("llu", [C.cty|long long int|])-                       FloatType _ -> ("f", [C.cty|double|])-                       _ -> ("d", [C.cty|int|])-        fmtstr = "%s: %" ++ fmt ++ "%c"--compileCode (DebugPrint s Nothing) =-  stm [C.cstm|if (ctx->debugging) {-          fprintf(stderr, "%s\n", $exp:s);-       }|]--compileCode c-  | Just (name, vol, t, e, c') <- declareAndSet c = do-    let ct = primTypeToCType t-    e' <- compileExp e-    item [C.citem|$tyquals:(volQuals vol) $ty:ct $id:name = $exp:e';|]-    compileCode c'--compileCode (c1 :>>: c2) = compileCode c1 >> compileCode c2--compileCode (Assert e msg (loc, locs)) = do-  e' <- compileExp e-  err <- collect $ join $-         asks (opsError . envOperations) <*> pure msg <*> pure stacktrace-  stm [C.cstm|if (!$exp:e') { $items:err }|]-  where stacktrace = prettyStacktrace 0 $ map locStr $ loc:locs--compileCode (Allocate _ _ ScalarSpace{}) =-  -- Handled by the declaration of the memory block, which is-  -- translated to an actual array.-  return ()--compileCode (Allocate name (Count e) space) = do-  size <- compileExp e-  cached <- cacheMem name-  case cached of-    Just cur_size ->-      stm [C.cstm|if ($exp:cur_size < (size_t)$exp:size) {-                    $exp:name = realloc($exp:name, $exp:size);-                    $exp:cur_size = $exp:size;-                  }|]-    _ ->-      allocMem name size space [C.cstm|{err = 1; goto cleanup;}|]--compileCode (Free name space) = do-  cached <- isJust <$> cacheMem name-  unless cached $ unRefMem name space--compileCode (For i it bound body) = do-  let i' = C.toIdent i-      it' = primTypeToCType $ IntType it-  bound' <- compileExp bound-  body'  <- blockScope $ compileCode body-  stm [C.cstm|for ($ty:it' $id:i' = 0; $id:i' < $exp:bound'; $id:i'++) {-            $items:body'-          }|]--compileCode (While cond body) = do-  cond' <- compileExp cond-  body' <- blockScope $ compileCode body-  stm [C.cstm|while ($exp:cond') {-            $items:body'-          }|]--compileCode (If cond tbranch fbranch) = do-  cond' <- compileExp cond-  tbranch' <- blockScope $ compileCode tbranch-  fbranch' <- blockScope $ compileCode fbranch-  stm $ case (tbranch', fbranch') of-    (_, []) ->-      [C.cstm|if ($exp:cond') { $items:tbranch' }|]-    ([], _) ->-      [C.cstm|if (!($exp:cond')) { $items:fbranch' }|]-    _ ->-      [C.cstm|if ($exp:cond') { $items:tbranch' } else { $items:fbranch' }|]--compileCode (Copy dest (Count destoffset) DefaultSpace src (Count srcoffset) DefaultSpace (Count size)) =-  join $ copyMemoryDefaultSpace-  <$> rawMem dest <*> compileExp destoffset-  <*> rawMem src <*> compileExp srcoffset-  <*> compileExp size--compileCode (Copy dest (Count destoffset) destspace src (Count srcoffset) srcspace (Count size)) = do-  copy <- asks envCopy-  join $ copy-    <$> rawMem dest <*> compileExp destoffset <*> pure destspace-    <*> rawMem src <*> compileExp srcoffset <*> pure srcspace-    <*> compileExp size--compileCode (Write dest (Count idx) elemtype DefaultSpace vol elemexp) = do-  dest' <- rawMem dest-  deref <- derefPointer dest'-           <$> compileExp idx-           <*> pure [C.cty|$tyquals:(volQuals vol) $ty:(primTypeToCType elemtype)*|]-  elemexp' <- compileExp elemexp-  stm [C.cstm|$exp:deref = $exp:elemexp';|]--compileCode (Write dest (Count idx) _ ScalarSpace{} _ elemexp) = do-  idx' <- compileExp idx-  elemexp' <- compileExp elemexp-  stm [C.cstm|$id:dest[$exp:idx'] = $exp:elemexp';|]--compileCode (Write dest (Count idx) elemtype (Space space) vol elemexp) =-  join $ asks envWriteScalar-    <*> rawMem dest-    <*> compileExp idx-    <*> pure (primTypeToCType elemtype)-    <*> pure space-    <*> pure vol-    <*> compileExp elemexp--compileCode (DeclareMem name space) =-  declMem name space--compileCode (DeclareScalar name vol t) = do-  let ct = primTypeToCType t-  decl [C.cdecl|$tyquals:(volQuals vol) $ty:ct $id:name;|]--compileCode (DeclareArray name ScalarSpace{} _ _) =-  error $ "Cannot declare array " ++ pretty name ++ " in scalar space."--compileCode (DeclareArray name DefaultSpace t vs) = do-  name_realtype <- newVName $ baseString name ++ "_realtype"-  let ct = primTypeToCType t-  case vs of-    ArrayValues vs' -> do-      let vs'' = [[C.cinit|$exp:(compilePrimValue v)|] | v <- vs']-      earlyDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:(length vs')] = {$inits:vs''};|]-    ArrayZeros n ->-      earlyDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:n];|]-  -- Fake a memory block.-  contextField (C.toIdent name noLoc)-    [C.cty|struct memblock|] $-    Just [C.cexp|(struct memblock){NULL, (char*)$id:name_realtype, 0}|]-  item [C.citem|struct memblock $id:name = ctx->$id:name;|]--compileCode (DeclareArray name (Space space) t vs) =-  join $ asks envStaticArray <*>-  pure name <*> pure space <*> pure t <*> pure vs---- For assignments of the form 'x = x OP e', we generate C assignment--- operators to make the resulting code slightly nicer.  This has no--- effect on performance.-compileCode (SetScalar dest (BinOpExp op (LeafExp (ScalarVar x) _) y))-  | dest == x, Just f <- assignmentOperator op = do-      y' <- compileExp y-      stm [C.cstm|$exp:(f dest y');|]--compileCode (SetScalar dest src) = do-  src' <- compileExp src-  stm [C.cstm|$id:dest = $exp:src';|]--compileCode (SetMem dest src space) =-  setMem dest src space--compileCode (Call dests fname args) =-  join $ asks (opsCall . envOperations)-  <*> pure dests <*> pure fname <*> mapM compileArg args-  where compileArg (MemArg m) = return [C.cexp|$exp:m|]-        compileArg (ExpArg e) = compileExp e--blockScope :: CompilerM op s () -> CompilerM op s [C.BlockItem]-blockScope = fmap snd . blockScope'--blockScope' :: CompilerM op s a -> CompilerM op s (a, [C.BlockItem])-blockScope' m = do-  old_allocs <- gets compDeclaredMem-  (x, xs) <- pass $ do-    (x, w) <- listen m-    let xs = DL.toList $ accItems w-    return ((x, xs), const mempty)-  new_allocs <- gets $ filter (`notElem` old_allocs) . compDeclaredMem-  modify $ \s -> s { compDeclaredMem = old_allocs }-  releases <- collect $ mapM_ (uncurry unRefMem) new_allocs-  return (x, xs <> releases)--compileFunBody :: [C.Exp] -> [Param] -> Code op -> CompilerM op s ()-compileFunBody output_ptrs outputs code = do-  mapM_ declareOutput outputs-  compileCode code-  zipWithM_ setRetVal' output_ptrs outputs-  where declareOutput (MemParam name space) =-          declMem name space-        declareOutput (ScalarParam name pt) = do-          let ctp = primTypeToCType pt-          decl [C.cdecl|$ty:ctp $id:name;|]--        setRetVal' p (MemParam name space) = do-          resetMem [C.cexp|*$exp:p|] space-          setMem [C.cexp|*$exp:p|] name space-        setRetVal' p (ScalarParam name _) =-          stm [C.cstm|*$exp:p = $id:name;|]--declareAndSet :: Code op -> Maybe (VName, Volatility, PrimType, Exp, Code op)-declareAndSet code = do-  (DeclareScalar name vol t, code') <- nextCode code-  (SetScalar dest e, code'') <- nextCode code'-  guard $ name == dest-  Just (name, vol, t, e, code'')--nextCode :: Code op -> Maybe (Code op, Code op)-nextCode (x :>>: y)-  | Just (x_a, x_b) <- nextCode x =-      Just (x_a, x_b <> y)-  | otherwise =-      Just (x, y)-nextCode _ = Nothing--assignmentOperator :: BinOp -> Maybe (VName -> C.Exp -> C.Exp)-assignmentOperator Add{}  = Just $ \d e -> [C.cexp|$id:d += $exp:e|]-assignmentOperator Sub{} = Just $ \d e -> [C.cexp|$id:d -= $exp:e|]-assignmentOperator Mul{} = Just $ \d e -> [C.cexp|$id:d *= $exp:e|]-assignmentOperator _     = Nothing---- | Return an expression multiplying together the given expressions.--- If an empty list is given, the expression @1@ is returned.-cproduct :: [C.Exp] -> C.Exp-cproduct []     = [C.cexp|1|]-cproduct (e:es) = foldl mult e es-  where mult x y = [C.cexp|$exp:x * $exp:y|]+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TupleSections #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++-- | C code generator framework.+module Futhark.CodeGen.Backends.GenericC+  ( compileProg,+    CParts (..),+    asLibrary,+    asExecutable,++    -- * Pluggable compiler+    Operations (..),+    defaultOperations,+    OpCompiler,+    ErrorCompiler,+    CallCompiler,+    PointerQuals,+    MemoryType,+    WriteScalar,+    writeScalarPointerWithQuals,+    ReadScalar,+    readScalarPointerWithQuals,+    Allocate,+    Deallocate,+    Copy,+    StaticArray,++    -- * Monadic compiler interface+    CompilerM,+    CompilerState (compUserState),+    getUserState,+    modifyUserState,+    contextContents,+    contextFinalInits,+    runCompilerM,+    cachingMemory,+    blockScope,+    compileFun,+    compileCode,+    compileExp,+    compilePrimExp,+    compilePrimValue,+    compileExpToName,+    rawMem,+    item,+    items,+    stm,+    stms,+    decl,+    atInit,+    headerDecl,+    publicDef,+    publicDef_,+    profileReport,+    HeaderSection (..),+    libDecl,+    earlyDecl,+    publicName,+    contextType,+    contextField,++    -- * Building Blocks+    primTypeToCType,+    copyMemoryDefaultSpace,+  )+where++import Control.Monad.Identity+import Control.Monad.RWS+import Data.Bifunctor (first)+import Data.Bits (shiftR, xor)+import Data.Char (isAlphaNum, isDigit, ord)+import qualified Data.DList as DL+import Data.FileEmbed+import Data.List (unzip4)+import Data.Loc+import qualified Data.Map.Strict as M+import Data.Maybe+import Futhark.CodeGen.Backends.GenericC.Options+import Futhark.CodeGen.Backends.SimpleRep+import Futhark.CodeGen.ImpCode+import Futhark.IR.Prop (isBuiltInFunction)+import Futhark.MonadFreshNames+import Futhark.Util (zEncodeString)+import qualified Language.C.Quote.OpenCL as C+import qualified Language.C.Syntax as C+import Text.Printf++data CompilerState s = CompilerState+  { compArrayStructs :: [((C.Type, Int), (C.Type, [C.Definition]))],+    compOpaqueStructs :: [(String, (C.Type, [C.Definition]))],+    compEarlyDecls :: DL.DList C.Definition,+    compInit :: [C.Stm],+    compNameSrc :: VNameSource,+    compUserState :: s,+    compHeaderDecls :: M.Map HeaderSection (DL.DList C.Definition),+    compLibDecls :: DL.DList C.Definition,+    compCtxFields :: DL.DList (C.Id, C.Type, Maybe C.Exp),+    compProfileItems :: DL.DList C.BlockItem,+    compDeclaredMem :: [(VName, Space)]+  }++newCompilerState :: VNameSource -> s -> CompilerState s+newCompilerState src s =+  CompilerState+    { compArrayStructs = [],+      compOpaqueStructs = [],+      compEarlyDecls = mempty,+      compInit = [],+      compNameSrc = src,+      compUserState = s,+      compHeaderDecls = mempty,+      compLibDecls = mempty,+      compCtxFields = mempty,+      compProfileItems = mempty,+      compDeclaredMem = mempty+    }++-- | In which part of the header file we put the declaration.  This is+-- to ensure that the header file remains structured and readable.+data HeaderSection+  = ArrayDecl String+  | OpaqueDecl String+  | EntryDecl+  | MiscDecl+  | InitDecl+  deriving (Eq, Ord)++-- | A substitute expression compiler, tried before the main+-- compilation function.+type OpCompiler op s = op -> CompilerM op s ()++type ErrorCompiler op s = ErrorMsg Exp -> String -> CompilerM op s ()++-- | The address space qualifiers for a pointer of the given type with+-- the given annotation.+type PointerQuals op s = String -> CompilerM op s [C.TypeQual]++-- | The type of a memory block in the given memory space.+type MemoryType op s = SpaceId -> CompilerM op s C.Type++-- | Write a scalar to the given memory block with the given element+-- index and in the given memory space.+type WriteScalar op s =+  C.Exp -> C.Exp -> C.Type -> SpaceId -> Volatility -> C.Exp -> CompilerM op s ()++-- | Read a scalar from the given memory block with the given element+-- index and in the given memory space.+type ReadScalar op s =+  C.Exp -> C.Exp -> C.Type -> SpaceId -> Volatility -> CompilerM op s C.Exp++-- | Allocate a memory block of the given size and with the given tag+-- in the given memory space, saving a reference in the given variable+-- name.+type Allocate op s =+  C.Exp ->+  C.Exp ->+  C.Exp ->+  SpaceId ->+  CompilerM op s ()++-- | De-allocate the given memory block with the given tag, which is+-- in the given memory space.+type Deallocate op s = C.Exp -> C.Exp -> SpaceId -> CompilerM op s ()++-- | Create a static array of values - initialised at load time.+type StaticArray op s = VName -> SpaceId -> PrimType -> ArrayContents -> CompilerM op s ()++-- | Copy from one memory block to another.+type Copy op s =+  C.Exp ->+  C.Exp ->+  Space ->+  C.Exp ->+  C.Exp ->+  Space ->+  C.Exp ->+  CompilerM op s ()++-- | Call a function.+type CallCompiler op s = [VName] -> Name -> [C.Exp] -> CompilerM op s ()++data Operations op s = Operations+  { opsWriteScalar :: WriteScalar op s,+    opsReadScalar :: ReadScalar op s,+    opsAllocate :: Allocate op s,+    opsDeallocate :: Deallocate op s,+    opsCopy :: Copy op s,+    opsStaticArray :: StaticArray op s,+    opsMemoryType :: MemoryType op s,+    opsCompiler :: OpCompiler op s,+    opsError :: ErrorCompiler op s,+    opsCall :: CallCompiler op s,+    -- | If true, use reference counting.  Otherwise, bare+    -- pointers.+    opsFatMemory :: Bool,+    -- | Code to bracket critical sections.+    opsCritical :: ([C.BlockItem], [C.BlockItem])+  }++defError :: ErrorCompiler op s+defError (ErrorMsg parts) stacktrace = do+  free_all_mem <- collect $ mapM_ (uncurry unRefMem) =<< gets compDeclaredMem+  let onPart (ErrorString s) = return ("%s", [C.cexp|$string:s|])+      onPart (ErrorInt32 x) = ("%d",) <$> compileExp x+      onPart (ErrorInt64 x) = ("%lld",) <$> compileExp x+  (formatstrs, formatargs) <- unzip <$> mapM onPart parts+  let formatstr = "Error: " ++ concat formatstrs ++ "\n\nBacktrace:\n%s"+  items+    [C.citems|ctx->error = msgprintf($string:formatstr, $args:formatargs, $string:stacktrace);+                  $items:free_all_mem+                  return 1;|]++defCall :: CallCompiler op s+defCall dests fname args = do+  let out_args = [[C.cexp|&$id:d|] | d <- dests]+      args'+        | isBuiltInFunction fname = args+        | otherwise = [C.cexp|ctx|] : out_args ++ args+  case dests of+    [dest]+      | isBuiltInFunction fname ->+        stm [C.cstm|$id:dest = $id:(funName fname)($args:args');|]+    _ ->+      item [C.citem|if ($id:(funName fname)($args:args') != 0) { err = 1; goto cleanup; }|]++-- | A set of operations that fail for every operation involving+-- non-default memory spaces.  Uses plain pointers and @malloc@ for+-- memory management.+defaultOperations :: Operations op s+defaultOperations =+  Operations+    { opsWriteScalar = defWriteScalar,+      opsReadScalar = defReadScalar,+      opsAllocate = defAllocate,+      opsDeallocate = defDeallocate,+      opsCopy = defCopy,+      opsStaticArray = defStaticArray,+      opsMemoryType = defMemoryType,+      opsCompiler = defCompiler,+      opsFatMemory = True,+      opsError = defError,+      opsCall = defCall,+      opsCritical = mempty+    }+  where+    defWriteScalar _ _ _ _ _ =+      error "Cannot write to non-default memory space because I am dumb"+    defReadScalar _ _ _ _ =+      error "Cannot read from non-default memory space"+    defAllocate _ _ _ =+      error "Cannot allocate in non-default memory space"+    defDeallocate _ _ =+      error "Cannot deallocate in non-default memory space"+    defCopy destmem destoffset DefaultSpace srcmem srcoffset DefaultSpace size =+      copyMemoryDefaultSpace destmem destoffset srcmem srcoffset size+    defCopy _ _ _ _ _ _ _ =+      error "Cannot copy to or from non-default memory space"+    defStaticArray _ _ _ _ =+      error "Cannot create static array in non-default memory space"+    defMemoryType _ =+      error "Has no type for non-default memory space"+    defCompiler _ =+      error "The default compiler cannot compile extended operations"++data CompilerEnv op s = CompilerEnv+  { envOperations :: Operations op s,+    -- | Mapping memory blocks to sizes.  These memory blocks are CPU+    -- memory that we know are used in particularly simple ways (no+    -- reference counting necessary).  To cut down on allocator+    -- pressure, we keep these allocations around for a long time, and+    -- record their sizes so we can reuse them if possible (and+    -- realloc() when needed).+    envCachedMem :: M.Map C.Exp VName+  }++newtype CompilerAcc op s = CompilerAcc+  { accItems :: DL.DList C.BlockItem+  }++instance Semigroup (CompilerAcc op s) where+  CompilerAcc items1 <> CompilerAcc items2 =+    CompilerAcc (items1 <> items2)++instance Monoid (CompilerAcc op s) where+  mempty = CompilerAcc mempty++envOpCompiler :: CompilerEnv op s -> OpCompiler op s+envOpCompiler = opsCompiler . envOperations++envMemoryType :: CompilerEnv op s -> MemoryType op s+envMemoryType = opsMemoryType . envOperations++envReadScalar :: CompilerEnv op s -> ReadScalar op s+envReadScalar = opsReadScalar . envOperations++envWriteScalar :: CompilerEnv op s -> WriteScalar op s+envWriteScalar = opsWriteScalar . envOperations++envAllocate :: CompilerEnv op s -> Allocate op s+envAllocate = opsAllocate . envOperations++envDeallocate :: CompilerEnv op s -> Deallocate op s+envDeallocate = opsDeallocate . envOperations++envCopy :: CompilerEnv op s -> Copy op s+envCopy = opsCopy . envOperations++envStaticArray :: CompilerEnv op s -> StaticArray op s+envStaticArray = opsStaticArray . envOperations++envFatMemory :: CompilerEnv op s -> Bool+envFatMemory = opsFatMemory . envOperations++arrayDefinitions, opaqueDefinitions :: CompilerState s -> [C.Definition]+arrayDefinitions = concatMap (snd . snd) . compArrayStructs+opaqueDefinitions = concatMap (snd . snd) . compOpaqueStructs++initDecls, arrayDecls, opaqueDecls, entryDecls, miscDecls :: CompilerState s -> [C.Definition]+initDecls = concatMap (DL.toList . snd) . filter ((== InitDecl) . fst) . M.toList . compHeaderDecls+arrayDecls = concatMap (DL.toList . snd) . filter (isArrayDecl . fst) . M.toList . compHeaderDecls+  where+    isArrayDecl ArrayDecl {} = True+    isArrayDecl _ = False+opaqueDecls = concatMap (DL.toList . snd) . filter (isOpaqueDecl . fst) . M.toList . compHeaderDecls+  where+    isOpaqueDecl OpaqueDecl {} = True+    isOpaqueDecl _ = False+entryDecls = concatMap (DL.toList . snd) . filter ((== EntryDecl) . fst) . M.toList . compHeaderDecls+miscDecls = concatMap (DL.toList . snd) . filter ((== MiscDecl) . fst) . M.toList . compHeaderDecls++contextContents :: CompilerM op s ([C.FieldGroup], [C.Stm])+contextContents = do+  (field_names, field_types, field_values) <- gets $ unzip3 . DL.toList . compCtxFields+  let fields =+        [ [C.csdecl|$ty:ty $id:name;|]+          | (name, ty) <- zip field_names field_types+        ]+      init_fields =+        [ [C.cstm|ctx->$id:name = $exp:e;|]+          | (name, Just e) <- zip field_names field_values+        ]+  return (fields, init_fields)++contextFinalInits :: CompilerM op s [C.Stm]+contextFinalInits = gets compInit++newtype CompilerM op s a+  = CompilerM+      ( RWS+          (CompilerEnv op s)+          (CompilerAcc op s)+          (CompilerState s)+          a+      )+  deriving+    ( Functor,+      Applicative,+      Monad,+      MonadState (CompilerState s),+      MonadReader (CompilerEnv op s),+      MonadWriter (CompilerAcc op s)+    )++instance MonadFreshNames (CompilerM op s) where+  getNameSource = gets compNameSrc+  putNameSource src = modify $ \s -> s {compNameSrc = src}++runCompilerM ::+  Operations op s ->+  VNameSource ->+  s ->+  CompilerM op s a ->+  (a, CompilerState s)+runCompilerM ops src userstate (CompilerM m) =+  let (x, s, _) = runRWS m (CompilerEnv ops mempty) (newCompilerState src userstate)+   in (x, s)++getUserState :: CompilerM op s s+getUserState = gets compUserState++modifyUserState :: (s -> s) -> CompilerM op s ()+modifyUserState f = modify $ \compstate ->+  compstate {compUserState = f $ compUserState compstate}++atInit :: C.Stm -> CompilerM op s ()+atInit x = modify $ \s ->+  s {compInit = compInit s ++ [x]}++collect :: CompilerM op s () -> CompilerM op s [C.BlockItem]+collect m = snd <$> collect' m++collect' :: CompilerM op s a -> CompilerM op s (a, [C.BlockItem])+collect' m = pass $ do+  (x, w) <- listen m+  return+    ( (x, DL.toList $ accItems w),+      const w {accItems = mempty}+    )++item :: C.BlockItem -> CompilerM op s ()+item x = tell $ mempty {accItems = DL.singleton x}++items :: [C.BlockItem] -> CompilerM op s ()+items = mapM_ item++fatMemory :: Space -> CompilerM op s Bool+fatMemory ScalarSpace {} = return False+fatMemory _ = asks envFatMemory++cacheMem :: C.ToExp a => a -> CompilerM op s (Maybe VName)+cacheMem a = asks $ M.lookup (C.toExp a noLoc) . envCachedMem++instance C.ToIdent Name where+  toIdent = C.toIdent . zEncodeString . nameToString++instance C.ToIdent VName where+  toIdent = C.toIdent . zEncodeString . pretty++instance C.ToExp VName where+  toExp v _ = [C.cexp|$id:v|]++instance C.ToExp IntValue where+  toExp (Int8Value v) = C.toExp v+  toExp (Int16Value v) = C.toExp v+  toExp (Int32Value v) = C.toExp v+  toExp (Int64Value v) = C.toExp v++instance C.ToExp FloatValue where+  toExp (Float32Value v) = C.toExp v+  toExp (Float64Value v) = C.toExp v++instance C.ToExp PrimValue where+  toExp (IntValue v) = C.toExp v+  toExp (FloatValue v) = C.toExp v+  toExp (BoolValue True) = C.toExp (1 :: Int8)+  toExp (BoolValue False) = C.toExp (0 :: Int8)+  toExp Checked = C.toExp (1 :: Int8)++instance C.ToExp SubExp where+  toExp (Var v) = C.toExp v+  toExp (Constant c) = C.toExp c++-- | Construct a publicly visible definition using the specified name+-- as the template.  The first returned definition is put in the+-- header file, and the second is the implementation.  Returns the public+-- name.+publicDef ::+  String ->+  HeaderSection ->+  (String -> (C.Definition, C.Definition)) ->+  CompilerM op s String+publicDef s h f = do+  s' <- publicName s+  let (pub, priv) = f s'+  headerDecl h pub+  earlyDecl priv+  return s'++-- | As 'publicDef', but ignores the public name.+publicDef_ ::+  String ->+  HeaderSection ->+  (String -> (C.Definition, C.Definition)) ->+  CompilerM op s ()+publicDef_ s h f = void $ publicDef s h f++headerDecl :: HeaderSection -> C.Definition -> CompilerM op s ()+headerDecl sec def = modify $ \s ->+  s+    { compHeaderDecls =+        M.unionWith+          (<>)+          (compHeaderDecls s)+          (M.singleton sec (DL.singleton def))+    }++libDecl :: C.Definition -> CompilerM op s ()+libDecl def = modify $ \s ->+  s {compLibDecls = compLibDecls s <> DL.singleton def}++earlyDecl :: C.Definition -> CompilerM op s ()+earlyDecl def = modify $ \s ->+  s {compEarlyDecls = compEarlyDecls s <> DL.singleton def}++contextField :: C.Id -> C.Type -> Maybe C.Exp -> CompilerM op s ()+contextField name ty initial = modify $ \s ->+  s {compCtxFields = compCtxFields s <> DL.singleton (name, ty, initial)}++profileReport :: C.BlockItem -> CompilerM op s ()+profileReport x = modify $ \s ->+  s {compProfileItems = compProfileItems s <> DL.singleton x}++stm :: C.Stm -> CompilerM op s ()+stm s = item [C.citem|$stm:s|]++stms :: [C.Stm] -> CompilerM op s ()+stms = mapM_ stm++decl :: C.InitGroup -> CompilerM op s ()+decl x = item [C.citem|$decl:x;|]++addrOf :: C.Exp -> C.Exp+addrOf e = [C.cexp|&$exp:e|]++-- | Public names must have a consitent prefix.+publicName :: String -> CompilerM op s String+publicName s = return $ "futhark_" ++ s++-- | The generated code must define a struct with this name.+contextType :: CompilerM op s C.Type+contextType = do+  name <- publicName "context"+  return [C.cty|struct $id:name|]++memToCType :: VName -> Space -> CompilerM op s C.Type+memToCType v space = do+  refcount <- fatMemory space+  cached <- isJust <$> cacheMem v+  if refcount && not cached+    then return $ fatMemType space+    else rawMemCType space++rawMemCType :: Space -> CompilerM op s C.Type+rawMemCType DefaultSpace = return defaultMemBlockType+rawMemCType (Space sid) = join $ asks envMemoryType <*> pure sid+rawMemCType (ScalarSpace [] t) =+  return [C.cty|$ty:(primTypeToCType t)[1]|]+rawMemCType (ScalarSpace ds t) =+  return [C.cty|$ty:(primTypeToCType t)[$exp:(cproduct ds')]|]+  where+    ds' = map (`C.toExp` noLoc) ds++fatMemType :: Space -> C.Type+fatMemType space =+  [C.cty|struct $id:name|]+  where+    name = case space of+      Space sid -> "memblock_" ++ sid+      _ -> "memblock"++fatMemSet :: Space -> String+fatMemSet (Space sid) = "memblock_set_" ++ sid+fatMemSet _ = "memblock_set"++fatMemAlloc :: Space -> String+fatMemAlloc (Space sid) = "memblock_alloc_" ++ sid+fatMemAlloc _ = "memblock_alloc"++fatMemUnRef :: Space -> String+fatMemUnRef (Space sid) = "memblock_unref_" ++ sid+fatMemUnRef _ = "memblock_unref"++rawMem :: VName -> CompilerM op s C.Exp+rawMem v = rawMem' <$> fat <*> pure v+  where+    fat = asks ((&&) . envFatMemory) <*> (isNothing <$> cacheMem v)++rawMem' :: C.ToExp a => Bool -> a -> C.Exp+rawMem' True e = [C.cexp|$exp:e.mem|]+rawMem' False e = [C.cexp|$exp:e|]++allocRawMem ::+  (C.ToExp a, C.ToExp b, C.ToExp c) =>+  a ->+  b ->+  Space ->+  c ->+  CompilerM op s ()+allocRawMem dest size space desc = case space of+  Space sid ->+    join $+      asks envAllocate <*> pure [C.cexp|$exp:dest|]+        <*> pure [C.cexp|$exp:size|]+        <*> pure [C.cexp|$exp:desc|]+        <*> pure sid+  _ ->+    stm [C.cstm|$exp:dest = (char*) malloc($exp:size);|]++freeRawMem ::+  (C.ToExp a, C.ToExp b) =>+  a ->+  Space ->+  b ->+  CompilerM op s ()+freeRawMem mem space desc =+  case space of+    Space sid -> do+      free_mem <- asks envDeallocate+      free_mem [C.cexp|$exp:mem|] [C.cexp|$exp:desc|] sid+    _ -> item [C.citem|free($exp:mem);|]++defineMemorySpace :: Space -> CompilerM op s (C.Definition, [C.Definition], C.BlockItem)+defineMemorySpace space = do+  rm <- rawMemCType space+  let structdef =+        [C.cedecl|struct $id:sname { int *references;+                                     $ty:rm mem;+                                     typename int64_t size;+                                     const char *desc; };|]++  contextField peakname [C.cty|typename int64_t|] $ Just [C.cexp|0|]+  contextField usagename [C.cty|typename int64_t|] $ Just [C.cexp|0|]++  -- Unreferencing a memory block consists of decreasing its reference+  -- count and freeing the corresponding memory if the count reaches+  -- zero.+  free <- collect $ freeRawMem [C.cexp|block->mem|] space [C.cexp|desc|]+  ctx_ty <- contextType+  let unrefdef =+        [C.cedecl|static int $id:(fatMemUnRef space) ($ty:ctx_ty *ctx, $ty:mty *block, const char *desc) {+  if (block->references != NULL) {+    *(block->references) -= 1;+    if (ctx->detail_memory) {+      fprintf(stderr, "Unreferencing block %s (allocated as %s) in %s: %d references remaining.\n",+                      desc, block->desc, $string:spacedesc, *(block->references));+    }+    if (*(block->references) == 0) {+      ctx->$id:usagename -= block->size;+      $items:free+      free(block->references);+      if (ctx->detail_memory) {+        fprintf(stderr, "%lld bytes freed (now allocated: %lld bytes)\n",+                (long long) block->size, (long long) ctx->$id:usagename);+      }+    }+    block->references = NULL;+  }+  return 0;+}|]++  -- When allocating a memory block we initialise the reference count to 1.+  alloc <-+    collect $+      allocRawMem [C.cexp|block->mem|] [C.cexp|size|] space [C.cexp|desc|]+  let allocdef =+        [C.cedecl|static int $id:(fatMemAlloc space) ($ty:ctx_ty *ctx, $ty:mty *block, typename int64_t size, const char *desc) {+  if (size < 0) {+    futhark_panic(1, "Negative allocation of %lld bytes attempted for %s in %s.\n",+          (long long)size, desc, $string:spacedesc, ctx->$id:usagename);+  }+  int ret = $id:(fatMemUnRef space)(ctx, block, desc);++  ctx->$id:usagename += size;+  if (ctx->detail_memory) {+    fprintf(stderr, "Allocating %lld bytes for %s in %s (then allocated: %lld bytes)",+            (long long) size,+            desc, $string:spacedesc,+            (long long) ctx->$id:usagename);+  }+  if (ctx->$id:usagename > ctx->$id:peakname) {+    ctx->$id:peakname = ctx->$id:usagename;+    if (ctx->detail_memory) {+      fprintf(stderr, " (new peak).\n");+    }+  } else if (ctx->detail_memory) {+    fprintf(stderr, ".\n");+  }++  $items:alloc+  block->references = (int*) malloc(sizeof(int));+  *(block->references) = 1;+  block->size = size;+  block->desc = desc;+  return ret;+  }|]++  -- Memory setting - unreference the destination and increase the+  -- count of the source by one.+  let setdef =+        [C.cedecl|static int $id:(fatMemSet space) ($ty:ctx_ty *ctx, $ty:mty *lhs, $ty:mty *rhs, const char *lhs_desc) {+  int ret = $id:(fatMemUnRef space)(ctx, lhs, lhs_desc);+  (*(rhs->references))++;+  *lhs = *rhs;+  return ret;+}+|]++  let peakmsg = "Peak memory usage for " ++ spacedesc ++ ": %lld bytes.\n"+  return+    ( structdef,+      [unrefdef, allocdef, setdef],+      -- Do not report memory usage for DefaultSpace (CPU memory),+      -- because it would not be accurate anyway.  This whole+      -- tracking probably needs to be rethought.+      if space == DefaultSpace+        then [C.citem|{}|]+        else [C.citem|str_builder(&builder, $string:peakmsg, (long long) ctx->$id:peakname);|]+    )+  where+    mty = fatMemType space+    (peakname, usagename, sname, spacedesc) = case space of+      Space sid ->+        ( C.toIdent ("peak_mem_usage_" ++ sid) noLoc,+          C.toIdent ("cur_mem_usage_" ++ sid) noLoc,+          C.toIdent ("memblock_" ++ sid) noLoc,+          "space '" ++ sid ++ "'"+        )+      _ ->+        ( "peak_mem_usage_default",+          "cur_mem_usage_default",+          "memblock",+          "default space"+        )++declMem :: VName -> Space -> CompilerM op s ()+declMem name space = do+  cached <- isJust <$> cacheMem name+  unless cached $ do+    ty <- memToCType name space+    decl [C.cdecl|$ty:ty $id:name;|]+    resetMem name space+    modify $ \s -> s {compDeclaredMem = (name, space) : compDeclaredMem s}++resetMem :: C.ToExp a => a -> Space -> CompilerM op s ()+resetMem mem space = do+  refcount <- fatMemory space+  cached <- isJust <$> cacheMem mem+  if cached+    then stm [C.cstm|$exp:mem = NULL;|]+    else+      when refcount $+        stm [C.cstm|$exp:mem.references = NULL;|]++setMem :: (C.ToExp a, C.ToExp b) => a -> b -> Space -> CompilerM op s ()+setMem dest src space = do+  refcount <- fatMemory space+  let src_s = pretty $ C.toExp src noLoc+  if refcount+    then+      stm+        [C.cstm|if ($id:(fatMemSet space)(ctx, &$exp:dest, &$exp:src,+                                               $string:src_s) != 0) {+                       return 1;+                     }|]+    else case space of+      ScalarSpace ds _ -> do+        i' <- newVName "i"+        let i = C.toIdent i'+            it = primTypeToCType $ IntType Int32+            ds' = map (`C.toExp` noLoc) ds+            bound = cproduct ds'+        stm+          [C.cstm|for ($ty:it $id:i = 0; $id:i < $exp:bound; $id:i++) {+                            $exp:dest[$id:i] = $exp:src[$id:i];+                  }|]+      _ -> stm [C.cstm|$exp:dest = $exp:src;|]++unRefMem :: C.ToExp a => a -> Space -> CompilerM op s ()+unRefMem mem space = do+  refcount <- fatMemory space+  cached <- isJust <$> cacheMem mem+  let mem_s = pretty $ C.toExp mem noLoc+  when (refcount && not cached) $+    stm+      [C.cstm|if ($id:(fatMemUnRef space)(ctx, &$exp:mem, $string:mem_s) != 0) {+                  return 1;+                }|]++allocMem ::+  (C.ToExp a, C.ToExp b) =>+  a ->+  b ->+  Space ->+  C.Stm ->+  CompilerM op s ()+allocMem mem size space on_failure = do+  refcount <- fatMemory space+  let mem_s = pretty $ C.toExp mem noLoc+  if refcount+    then+      stm+        [C.cstm|if ($id:(fatMemAlloc space)(ctx, &$exp:mem, $exp:size,+                                                 $string:mem_s)) {+                       $stm:on_failure+                     }|]+    else do+      freeRawMem mem space mem_s+      allocRawMem mem size space [C.cexp|desc|]++primTypeInfo :: PrimType -> Signedness -> C.Exp+primTypeInfo (IntType it) t = case (it, t) of+  (Int8, TypeUnsigned) -> [C.cexp|u8_info|]+  (Int16, TypeUnsigned) -> [C.cexp|u16_info|]+  (Int32, TypeUnsigned) -> [C.cexp|u32_info|]+  (Int64, TypeUnsigned) -> [C.cexp|u64_info|]+  (Int8, _) -> [C.cexp|i8_info|]+  (Int16, _) -> [C.cexp|i16_info|]+  (Int32, _) -> [C.cexp|i32_info|]+  (Int64, _) -> [C.cexp|i64_info|]+primTypeInfo (FloatType Float32) _ = [C.cexp|f32_info|]+primTypeInfo (FloatType Float64) _ = [C.cexp|f64_info|]+primTypeInfo Bool _ = [C.cexp|bool_info|]+primTypeInfo Cert _ = [C.cexp|bool_info|]++copyMemoryDefaultSpace ::+  C.Exp ->+  C.Exp ->+  C.Exp ->+  C.Exp ->+  C.Exp ->+  CompilerM op s ()+copyMemoryDefaultSpace destmem destidx srcmem srcidx nbytes =+  stm+    [C.cstm|memmove($exp:destmem + $exp:destidx,+                      $exp:srcmem + $exp:srcidx,+                      $exp:nbytes);|]++--- Entry points.++arrayName :: PrimType -> Signedness -> Int -> String+arrayName pt signed rank =+  prettySigned (signed == TypeUnsigned) pt ++ "_" ++ show rank ++ "d"++opaqueName :: String -> [ValueDesc] -> String+opaqueName s _+  | valid = "opaque_" ++ s+  where+    valid =+      head s /= '_'+        && not (isDigit $ head s)+        && all ok s+    ok c = isAlphaNum c || c == '_'+opaqueName s vds = "opaque_" ++ hash (zipWith xor [0 ..] $ map ord (s ++ concatMap p vds))+  where+    p (ScalarValue pt signed _) =+      show (pt, signed)+    p (ArrayValue _ space pt signed dims) =+      show (space, pt, signed, length dims)++    -- FIXME: a stupid hash algorithm; may have collisions.+    hash =+      printf "%x" . foldl xor 0+        . map+          ( iter . (* 0x45d9f3b)+              . iter+              . (* 0x45d9f3b)+              . iter+              . fromIntegral+          )+    iter x = ((x :: Word32) `shiftR` 16) `xor` x++criticalSection :: Operations op s -> [C.BlockItem] -> [C.BlockItem]+criticalSection ops x =+  [C.citems|lock_lock(&ctx->lock);+            $items:(fst (opsCritical ops))+            $items:x+            $items:(snd (opsCritical ops))+            lock_unlock(&ctx->lock);+           |]++arrayLibraryFunctions ::+  Space ->+  PrimType ->+  Signedness ->+  [DimSize] ->+  CompilerM op s [C.Definition]+arrayLibraryFunctions space pt signed shape = do+  let rank = length shape+      pt' = signedPrimTypeToCType signed pt+      name = arrayName pt signed rank+      arr_name = "futhark_" ++ name+      array_type = [C.cty|struct $id:arr_name|]++  new_array <- publicName $ "new_" ++ name+  new_raw_array <- publicName $ "new_raw_" ++ name+  free_array <- publicName $ "free_" ++ name+  values_array <- publicName $ "values_" ++ name+  values_raw_array <- publicName $ "values_raw_" ++ name+  shape_array <- publicName $ "shape_" ++ name++  let shape_names = ["dim" ++ show i | i <- [0 .. rank -1]]+      shape_params = [[C.cparam|typename int64_t $id:k|] | k <- shape_names]+      arr_size = cproduct [[C.cexp|$id:k|] | k <- shape_names]+      arr_size_array = cproduct [[C.cexp|arr->shape[$int:i]|] | i <- [0 .. rank -1]]+  copy <- asks envCopy++  memty <- rawMemCType space++  let prepare_new = do+        resetMem [C.cexp|arr->mem|] space+        allocMem+          [C.cexp|arr->mem|]+          [C.cexp|((size_t)$exp:arr_size) * sizeof($ty:pt')|]+          space+          [C.cstm|return NULL;|]+        forM_ [0 .. rank -1] $ \i ->+          let dim_s = "dim" ++ show i+           in stm [C.cstm|arr->shape[$int:i] = $id:dim_s;|]++  new_body <- collect $ do+    prepare_new+    copy+      [C.cexp|arr->mem.mem|]+      [C.cexp|0|]+      space+      [C.cexp|data|]+      [C.cexp|0|]+      DefaultSpace+      [C.cexp|((size_t)$exp:arr_size) * sizeof($ty:pt')|]++  new_raw_body <- collect $ do+    prepare_new+    copy+      [C.cexp|arr->mem.mem|]+      [C.cexp|0|]+      space+      [C.cexp|data|]+      [C.cexp|offset|]+      space+      [C.cexp|((size_t)$exp:arr_size) * sizeof($ty:pt')|]++  free_body <- collect $ unRefMem [C.cexp|arr->mem|] space++  values_body <-+    collect $+      copy+        [C.cexp|data|]+        [C.cexp|0|]+        DefaultSpace+        [C.cexp|arr->mem.mem|]+        [C.cexp|0|]+        space+        [C.cexp|((size_t)$exp:arr_size_array) * sizeof($ty:pt')|]++  ctx_ty <- contextType+  ops <- asks envOperations++  headerDecl+    (ArrayDecl name)+    [C.cedecl|struct $id:arr_name;|]+  headerDecl+    (ArrayDecl name)+    [C.cedecl|$ty:array_type* $id:new_array($ty:ctx_ty *ctx, const $ty:pt' *data, $params:shape_params);|]+  headerDecl+    (ArrayDecl name)+    [C.cedecl|$ty:array_type* $id:new_raw_array($ty:ctx_ty *ctx, const $ty:memty data, int offset, $params:shape_params);|]+  headerDecl+    (ArrayDecl name)+    [C.cedecl|int $id:free_array($ty:ctx_ty *ctx, $ty:array_type *arr);|]+  headerDecl+    (ArrayDecl name)+    [C.cedecl|int $id:values_array($ty:ctx_ty *ctx, $ty:array_type *arr, $ty:pt' *data);|]+  headerDecl+    (ArrayDecl name)+    [C.cedecl|$ty:memty $id:values_raw_array($ty:ctx_ty *ctx, $ty:array_type *arr);|]+  headerDecl+    (ArrayDecl name)+    [C.cedecl|const typename int64_t* $id:shape_array($ty:ctx_ty *ctx, $ty:array_type *arr);|]++  return+    [C.cunit|+          $ty:array_type* $id:new_array($ty:ctx_ty *ctx, const $ty:pt' *data, $params:shape_params) {+            $ty:array_type* bad = NULL;+            $ty:array_type *arr = ($ty:array_type*) malloc(sizeof($ty:array_type));+            if (arr == NULL) {+              return bad;+            }+            $items:(criticalSection ops new_body)+            return arr;+          }++          $ty:array_type* $id:new_raw_array($ty:ctx_ty *ctx, const $ty:memty data, int offset,+                                            $params:shape_params) {+            $ty:array_type* bad = NULL;+            $ty:array_type *arr = ($ty:array_type*) malloc(sizeof($ty:array_type));+            if (arr == NULL) {+              return bad;+            }+            $items:(criticalSection ops new_raw_body)+            return arr;+          }++          int $id:free_array($ty:ctx_ty *ctx, $ty:array_type *arr) {+            $items:(criticalSection ops free_body)+            free(arr);+            return 0;+          }++          int $id:values_array($ty:ctx_ty *ctx, $ty:array_type *arr, $ty:pt' *data) {+            $items:(criticalSection ops values_body)+            return 0;+          }++          $ty:memty $id:values_raw_array($ty:ctx_ty *ctx, $ty:array_type *arr) {+            (void)ctx;+            return arr->mem.mem;+          }++          const typename int64_t* $id:shape_array($ty:ctx_ty *ctx, $ty:array_type *arr) {+            (void)ctx;+            return arr->shape;+          }+          |]++opaqueLibraryFunctions ::+  String ->+  [ValueDesc] ->+  CompilerM op s [C.Definition]+opaqueLibraryFunctions desc vds = do+  name <- publicName $ opaqueName desc vds+  free_opaque <- publicName $ "free_" ++ opaqueName desc vds++  let opaque_type = [C.cty|struct $id:name|]++      freeComponent _ ScalarValue {} =+        return ()+      freeComponent i (ArrayValue _ _ pt signed shape) = do+        let rank = length shape+        free_array <- publicName $ "free_" ++ arrayName pt signed rank+        stm+          [C.cstm|if ((tmp = $id:free_array(ctx, obj->$id:(tupleField i))) != 0) {+                ret = tmp;+             }|]++  ctx_ty <- contextType++  free_body <- collect $ zipWithM_ freeComponent [0 ..] vds++  headerDecl+    (OpaqueDecl desc)+    [C.cedecl|int $id:free_opaque($ty:ctx_ty *ctx, $ty:opaque_type *obj);|]++  return+    [C.cunit|+          int $id:free_opaque($ty:ctx_ty *ctx, $ty:opaque_type *obj) {+            int ret = 0, tmp;+            $items:free_body+            free(obj);+            return ret;+          }+           |]++valueDescToCType :: ValueDesc -> CompilerM op s C.Type+valueDescToCType (ScalarValue pt signed _) =+  return $ signedPrimTypeToCType signed pt+valueDescToCType (ArrayValue mem space pt signed shape) = do+  let pt' = signedPrimTypeToCType signed pt+      rank = length shape+  exists <- gets $ lookup (pt', rank) . compArrayStructs+  case exists of+    Just (cty, _) -> return cty+    Nothing -> do+      memty <- memToCType mem space+      name <- publicName $ arrayName pt signed rank+      let struct = [C.cedecl|struct $id:name { $ty:memty mem; typename int64_t shape[$int:rank]; };|]+          stype = [C.cty|struct $id:name|]+      library <- arrayLibraryFunctions space pt signed shape+      modify $ \s ->+        s+          { compArrayStructs =+              ((pt', rank), (stype, struct : library)) : compArrayStructs s+          }+      return stype++opaqueToCType :: String -> [ValueDesc] -> CompilerM op s C.Type+opaqueToCType desc vds = do+  name <- publicName $ opaqueName desc vds+  exists <- gets $ lookup name . compOpaqueStructs+  case exists of+    Just (ty, _) -> return ty+    Nothing -> do+      members <- zipWithM field vds [(0 :: Int) ..]+      let struct = [C.cedecl|struct $id:name { $sdecls:members };|]+          stype = [C.cty|struct $id:name|]+      headerDecl (OpaqueDecl desc) [C.cedecl|struct $id:name;|]+      library <- opaqueLibraryFunctions desc vds+      modify $ \s ->+        s+          { compOpaqueStructs =+              (name, (stype, struct : library)) :+              compOpaqueStructs s+          }+      return stype+  where+    field vd@ScalarValue {} i = do+      ct <- valueDescToCType vd+      return [C.csdecl|$ty:ct $id:(tupleField i);|]+    field vd i = do+      ct <- valueDescToCType vd+      return [C.csdecl|$ty:ct *$id:(tupleField i);|]++externalValueToCType :: ExternalValue -> CompilerM op s C.Type+externalValueToCType (TransparentValue vd) = valueDescToCType vd+externalValueToCType (OpaqueValue desc vds) = opaqueToCType desc vds++prepareEntryInputs :: [ExternalValue] -> CompilerM op s [C.Param]+prepareEntryInputs = zipWithM prepare [(0 :: Int) ..]+  where+    prepare pno (TransparentValue vd) = do+      let pname = "in" ++ show pno+      ty <- prepareValue [C.cexp|$id:pname|] vd+      return [C.cparam|const $ty:ty $id:pname|]+    prepare pno (OpaqueValue desc vds) = do+      ty <- opaqueToCType desc vds+      let pname = "in" ++ show pno+          field i ScalarValue {} = [C.cexp|$id:pname->$id:(tupleField i)|]+          field i ArrayValue {} = [C.cexp|$id:pname->$id:(tupleField i)|]+      zipWithM_ prepareValue (zipWith field [0 ..] vds) vds+      return [C.cparam|const $ty:ty *$id:pname|]++    prepareValue src (ScalarValue pt signed name) = do+      let pt' = signedPrimTypeToCType signed pt+      stm [C.cstm|$id:name = $exp:src;|]+      return pt'+    prepareValue src vd@(ArrayValue mem _ _ _ shape) = do+      ty <- valueDescToCType vd++      stm [C.cstm|$exp:mem = $exp:src->mem;|]++      let rank = length shape+          maybeCopyDim (Var d) i =+            Just [C.cstm|$id:d = $exp:src->shape[$int:i];|]+          maybeCopyDim _ _ = Nothing++      stms $ catMaybes $ zipWith maybeCopyDim shape [0 .. rank -1]++      return [C.cty|$ty:ty*|]++prepareEntryOutputs :: [ExternalValue] -> CompilerM op s [C.Param]+prepareEntryOutputs = zipWithM prepare [(0 :: Int) ..]+  where+    prepare pno (TransparentValue vd) = do+      let pname = "out" ++ show pno+      ty <- valueDescToCType vd++      case vd of+        ArrayValue {} -> do+          stm [C.cstm|assert((*$id:pname = ($ty:ty*) malloc(sizeof($ty:ty))) != NULL);|]+          prepareValue [C.cexp|*$id:pname|] vd+          return [C.cparam|$ty:ty **$id:pname|]+        ScalarValue {} -> do+          prepareValue [C.cexp|*$id:pname|] vd+          return [C.cparam|$ty:ty *$id:pname|]+    prepare pno (OpaqueValue desc vds) = do+      let pname = "out" ++ show pno+      ty <- opaqueToCType desc vds+      vd_ts <- mapM valueDescToCType vds++      stm [C.cstm|assert((*$id:pname = ($ty:ty*) malloc(sizeof($ty:ty))) != NULL);|]++      forM_ (zip3 [0 ..] vd_ts vds) $ \(i, ct, vd) -> do+        let field = [C.cexp|(*$id:pname)->$id:(tupleField i)|]+        case vd of+          ScalarValue {} -> return ()+          _ -> stm [C.cstm|assert(($exp:field = ($ty:ct*) malloc(sizeof($ty:ct))) != NULL);|]+        prepareValue field vd++      return [C.cparam|$ty:ty **$id:pname|]++    prepareValue dest (ScalarValue _ _ name) =+      stm [C.cstm|$exp:dest = $id:name;|]+    prepareValue dest (ArrayValue mem _ _ _ shape) = do+      stm [C.cstm|$exp:dest->mem = $id:mem;|]++      let rank = length shape+          maybeCopyDim (Constant x) i =+            [C.cstm|$exp:dest->shape[$int:i] = $exp:x;|]+          maybeCopyDim (Var d) i =+            [C.cstm|$exp:dest->shape[$int:i] = $id:d;|]+      stms $ zipWith maybeCopyDim shape [0 .. rank -1]++onEntryPoint ::+  Name ->+  Function op ->+  CompilerM op s (C.Definition, C.Definition, C.Initializer)+onEntryPoint fname function@(Function _ outputs inputs _ results args) = do+  let out_args = map (\p -> [C.cexp|&$id:(paramName p)|]) outputs+      in_args = map (\p -> [C.cexp|$id:(paramName p)|]) inputs++  inputdecls <- collect $ mapM_ stubParam inputs+  outputdecls <- collect $ mapM_ stubParam outputs++  let entry_point_name = nameToString fname+  entry_point_function_name <- publicName $ "entry_" ++ entry_point_name++  (entry_point_input_params, unpack_entry_inputs) <-+    collect' $ prepareEntryInputs args+  (entry_point_output_params, pack_entry_outputs) <-+    collect' $ prepareEntryOutputs results++  (cli_entry_point, cli_init) <- cliEntryPoint fname function++  ctx_ty <- contextType++  headerDecl+    EntryDecl+    [C.cedecl|int $id:entry_point_function_name+                                     ($ty:ctx_ty *ctx,+                                      $params:entry_point_output_params,+                                      $params:entry_point_input_params);|]++  let critical =+        [C.citems|+         $items:unpack_entry_inputs++         int ret = $id:(funName fname)(ctx, $args:out_args, $args:in_args);++         if (ret == 0) {+           $items:pack_entry_outputs+         }+        |]++  ops <- asks envOperations++  return+    ( [C.cedecl|+       int $id:entry_point_function_name+           ($ty:ctx_ty *ctx,+            $params:entry_point_output_params,+            $params:entry_point_input_params) {+         $items:inputdecls+         $items:outputdecls++         $items:(criticalSection ops critical)++         return ret;+       }|],+      cli_entry_point,+      cli_init+    )+  where+    stubParam (MemParam name space) =+      declMem name space+    stubParam (ScalarParam name ty) = do+      let ty' = primTypeToCType ty+      decl [C.cdecl|$ty:ty' $id:name;|]++--- CLI interface+--+-- Our strategy for CLI entry points is to parse everything into+-- host memory ('DefaultSpace') and copy the result into host memory+-- after the entry point has returned.  We have some ad-hoc frobbery+-- to copy the host-level memory blocks to another memory space if+-- necessary.  This will break if the Futhark entry point uses+-- non-trivial index functions for its input or output.+--+-- The idea here is to keep the nastyness in the wrapper, whilst not+-- messing up anything else.++printPrimStm :: (C.ToExp a, C.ToExp b) => a -> b -> PrimType -> Signedness -> C.Stm+printPrimStm dest val bt ept =+  [C.cstm|write_scalar($exp:dest, binary_output, &$exp:(primTypeInfo bt ept), &$exp:val);|]++-- | Return a statement printing the given external value.+printStm :: ExternalValue -> C.Exp -> CompilerM op s C.Stm+printStm (OpaqueValue desc _) _ =+  return [C.cstm|printf("#<opaque %s>", $string:desc);|]+printStm (TransparentValue (ScalarValue bt ept _)) e =+  return $ printPrimStm [C.cexp|stdout|] e bt ept+printStm (TransparentValue (ArrayValue _ _ bt ept shape)) e = do+  values_array <- publicName $ "values_" ++ name+  shape_array <- publicName $ "shape_" ++ name+  let num_elems = cproduct [[C.cexp|$id:shape_array(ctx, $exp:e)[$int:i]|] | i <- [0 .. rank -1]]+  return+    [C.cstm|{+      $ty:bt' *arr = calloc(sizeof($ty:bt'), $exp:num_elems);+      assert(arr != NULL);+      assert($id:values_array(ctx, $exp:e, arr) == 0);+      write_array(stdout, binary_output, &$exp:(primTypeInfo bt ept), arr,+                  $id:shape_array(ctx, $exp:e), $int:rank);+      free(arr);+    }|]+  where+    rank = length shape+    bt' = primTypeToCType bt+    name = arrayName bt ept rank++readPrimStm :: C.ToExp a => a -> Int -> PrimType -> Signedness -> C.Stm+readPrimStm place i t ept =+  [C.cstm|if (read_scalar(&$exp:(primTypeInfo t ept),&$exp:place) != 0) {+        futhark_panic(1, "Error when reading input #%d of type %s (errno: %s).\n",+              $int:i,+              $exp:(primTypeInfo t ept).type_name,+              strerror(errno));+      }|]++readInputs :: [ExternalValue] -> CompilerM op s [(C.Stm, C.Stm, C.Stm, C.Exp)]+readInputs = zipWithM readInput [0 ..]++readInput :: Int -> ExternalValue -> CompilerM op s (C.Stm, C.Stm, C.Stm, C.Exp)+readInput i (OpaqueValue desc _) = do+  stm [C.cstm|futhark_panic(1, "Cannot read input #%d of type %s\n", $int:i, $string:desc);|]+  return ([C.cstm|;|], [C.cstm|;|], [C.cstm|;|], [C.cexp|NULL|])+readInput i (TransparentValue (ScalarValue t ept _)) = do+  dest <- newVName "read_value"+  item [C.citem|$ty:(primTypeToCType t) $id:dest;|]+  stm $ readPrimStm dest i t ept+  return ([C.cstm|;|], [C.cstm|;|], [C.cstm|;|], [C.cexp|$id:dest|])+readInput i (TransparentValue vd@(ArrayValue _ _ t ept dims)) = do+  dest <- newVName "read_value"+  shape <- newVName "read_shape"+  arr <- newVName "read_arr"+  ty <- valueDescToCType vd+  item [C.citem|$ty:ty *$id:dest;|]++  let t' = signedPrimTypeToCType ept t+      rank = length dims+      name = arrayName t ept rank+      dims_exps = [[C.cexp|$id:shape[$int:j]|] | j <- [0 .. rank -1]]+      dims_s = concat $ replicate rank "[]"++  new_array <- publicName $ "new_" ++ name+  free_array <- publicName $ "free_" ++ name++  items+    [C.citems|+     typename int64_t $id:shape[$int:rank];+     $ty:t' *$id:arr = NULL;+     errno = 0;+     if (read_array(&$exp:(primTypeInfo t ept),+                    (void**) &$id:arr,+                    $id:shape,+                    $int:(length dims))+         != 0) {+       futhark_panic(1, "Cannot read input #%d of type %s%s (errno: %s).\n",+                 $int:i,+                 $string:dims_s,+                 $exp:(primTypeInfo t ept).type_name,+                 strerror(errno));+     }|]++  return+    ( [C.cstm|assert(($exp:dest = $id:new_array(ctx, $id:arr, $args:dims_exps)) != 0);|],+      [C.cstm|assert($id:free_array(ctx, $exp:dest) == 0);|],+      [C.cstm|free($id:arr);|],+      [C.cexp|$id:dest|]+    )++prepareOutputs :: [ExternalValue] -> CompilerM op s [(C.Exp, C.Stm)]+prepareOutputs = mapM prepareResult+  where+    prepareResult ev = do+      ty <- externalValueToCType ev+      result <- newVName "result"++      case ev of+        TransparentValue ScalarValue {} -> do+          item [C.citem|$ty:ty $id:result;|]+          return ([C.cexp|$id:result|], [C.cstm|;|])+        TransparentValue (ArrayValue _ _ t ept dims) -> do+          let name = arrayName t ept $ length dims+          free_array <- publicName $ "free_" ++ name+          item [C.citem|$ty:ty *$id:result;|]+          return+            ( [C.cexp|$id:result|],+              [C.cstm|assert($id:free_array(ctx, $exp:result) == 0);|]+            )+        OpaqueValue desc vds -> do+          free_opaque <- publicName $ "free_" ++ opaqueName desc vds+          item [C.citem|$ty:ty *$id:result;|]+          return+            ( [C.cexp|$id:result|],+              [C.cstm|assert($id:free_opaque(ctx, $exp:result) == 0);|]+            )++printResult :: [(ExternalValue, C.Exp)] -> CompilerM op s [C.Stm]+printResult vs = fmap concat $+  forM vs $ \(v, e) -> do+    p <- printStm v e+    return [p, [C.cstm|printf("\n");|]]++cliEntryPoint ::+  Name ->+  FunctionT a ->+  CompilerM op s (C.Definition, C.Initializer)+cliEntryPoint fname (Function _ _ _ _ results args) = do+  ((pack_input, free_input, free_parsed, input_args), input_items) <-+    collect' $ unzip4 <$> readInputs args++  ((output_vals, free_outputs), output_decls) <-+    collect' $ unzip <$> prepareOutputs results+  printstms <- printResult $ zip results output_vals++  ctx_ty <- contextType+  sync_ctx <- publicName "context_sync"+  error_ctx <- publicName "context_get_error"++  let entry_point_name = nameToString fname+      cli_entry_point_function_name = "futrts_cli_entry_" ++ entry_point_name+  entry_point_function_name <- publicName $ "entry_" ++ entry_point_name++  pause_profiling <- publicName "context_pause_profiling"+  unpause_profiling <- publicName "context_unpause_profiling"++  let run_it =+        [C.citems|+                  int r;+                  // Run the program once.+                  $stms:pack_input+                  if ($id:sync_ctx(ctx) != 0) {+                    futhark_panic(1, "%s", $id:error_ctx(ctx));+                  };+                  // Only profile last run.+                  if (profile_run) {+                    $id:unpause_profiling(ctx);+                  }+                  t_start = get_wall_time();+                  r = $id:entry_point_function_name(ctx,+                                                    $args:(map addrOf output_vals),+                                                    $args:input_args);+                  if (r != 0) {+                    futhark_panic(1, "%s", $id:error_ctx(ctx));+                  }+                  if ($id:sync_ctx(ctx) != 0) {+                    futhark_panic(1, "%s", $id:error_ctx(ctx));+                  };+                  if (profile_run) {+                    $id:pause_profiling(ctx);+                  }+                  t_end = get_wall_time();+                  long int elapsed_usec = t_end - t_start;+                  if (time_runs && runtime_file != NULL) {+                    fprintf(runtime_file, "%lld\n", (long long) elapsed_usec);+                    fflush(runtime_file);+                  }+                  $stms:free_input+                |]++  return+    ( [C.cedecl|static void $id:cli_entry_point_function_name($ty:ctx_ty *ctx) {+    typename int64_t t_start, t_end;+    int time_runs = 0, profile_run = 0;++    // We do not want to profile all the initialisation.+    $id:pause_profiling(ctx);++    // Declare and read input.+    set_binary_mode(stdin);+    $items:input_items++    if (end_of_input() != 0) {+      futhark_panic(1, "Expected EOF on stdin after reading input for %s.\n", $string:(quote (pretty fname)));+    }++    $items:output_decls++    // Warmup run+    if (perform_warmup) {+      $items:run_it+      $stms:free_outputs+    }+    time_runs = 1;+    // Proper run.+    for (int run = 0; run < num_runs; run++) {+      // Only profile last run.+      profile_run = run == num_runs -1;+      $items:run_it+      if (run < num_runs-1) {+        $stms:free_outputs+      }+    }++    // Free the parsed input.+    $stms:free_parsed++    // Print the final result.+    if (binary_output) {+      set_binary_mode(stdout);+    }+    $stms:printstms++    $stms:free_outputs+  }+                |],+      [C.cinit|{ .name = $string:entry_point_name,+                      .fun = $id:cli_entry_point_function_name }|]+    )++genericOptions :: [Option]+genericOptions =+  [ Option+      { optionLongName = "write-runtime-to",+        optionShortName = Just 't',+        optionArgument = RequiredArgument "FILE",+        optionDescription = "Print the time taken to execute the program to the indicated file, an integral number of microseconds.",+        optionAction = set_runtime_file+      },+    Option+      { optionLongName = "runs",+        optionShortName = Just 'r',+        optionArgument = RequiredArgument "INT",+        optionDescription = "Perform NUM runs of the program.",+        optionAction = set_num_runs+      },+    Option+      { optionLongName = "debugging",+        optionShortName = Just 'D',+        optionArgument = NoArgument,+        optionDescription = "Perform possibly expensive internal correctness checks and verbose logging.",+        optionAction = [C.cstm|futhark_context_config_set_debugging(cfg, 1);|]+      },+    Option+      { optionLongName = "log",+        optionShortName = Just 'L',+        optionArgument = NoArgument,+        optionDescription = "Print various low-overhead logging information to stderr while running.",+        optionAction = [C.cstm|futhark_context_config_set_logging(cfg, 1);|]+      },+    Option+      { optionLongName = "entry-point",+        optionShortName = Just 'e',+        optionArgument = RequiredArgument "NAME",+        optionDescription = "The entry point to run. Defaults to main.",+        optionAction = [C.cstm|if (entry_point != NULL) entry_point = optarg;|]+      },+    Option+      { optionLongName = "binary-output",+        optionShortName = Just 'b',+        optionArgument = NoArgument,+        optionDescription = "Print the program result in the binary output format.",+        optionAction = [C.cstm|binary_output = 1;|]+      },+    Option+      { optionLongName = "help",+        optionShortName = Just 'h',+        optionArgument = NoArgument,+        optionDescription = "Print help information and exit.",+        optionAction =+          [C.cstm|{+                   printf("Usage: %s [OPTION]...\nOptions:\n\n%s\nFor more information, consult the Futhark User's Guide or the man pages.\n",+                          fut_progname, option_descriptions);+                   exit(0);+                  }|]+      }+  ]+  where+    set_runtime_file =+      [C.cstm|{+          runtime_file = fopen(optarg, "w");+          if (runtime_file == NULL) {+            futhark_panic(1, "Cannot open %s: %s\n", optarg, strerror(errno));+          }+        }|]+    set_num_runs =+      [C.cstm|{+          num_runs = atoi(optarg);+          perform_warmup = 1;+          if (num_runs <= 0) {+            futhark_panic(1, "Need a positive number of runs, not %s\n", optarg);+          }+        }|]++-- | The result of compilation to C is four parts, which can be put+-- together in various ways.  The obvious way is to concatenate all of+-- them, which yields a CLI program.  Another is to compile the+-- library part by itself, and use the header file to call into it.+data CParts = CParts+  { cHeader :: String,+    -- | Utility definitions that must be visible+    -- to both CLI and library parts.+    cUtils :: String,+    cCLI :: String,+    cLib :: String+  }++-- We may generate variables that are never used (e.g. for+-- certificates) or functions that are never called (e.g. unused+-- intrinsics), and generated code may have other cosmetic issues that+-- compilers warn about.  We disable these warnings to not clutter the+-- compilation logs.+disableWarnings :: String+disableWarnings =+  pretty+    [C.cunit|+$esc:("#ifdef __GNUC__")+$esc:("#pragma GCC diagnostic ignored \"-Wunused-function\"")+$esc:("#pragma GCC diagnostic ignored \"-Wunused-variable\"")+$esc:("#pragma GCC diagnostic ignored \"-Wparentheses\"")+$esc:("#pragma GCC diagnostic ignored \"-Wunused-label\"")+$esc:("#endif")++$esc:("#ifdef __clang__")+$esc:("#pragma clang diagnostic ignored \"-Wunused-function\"")+$esc:("#pragma clang diagnostic ignored \"-Wunused-variable\"")+$esc:("#pragma clang diagnostic ignored \"-Wparentheses\"")+$esc:("#pragma clang diagnostic ignored \"-Wunused-label\"")+$esc:("#endif")+|]++-- | Produce header and implementation files.+asLibrary :: CParts -> (String, String)+asLibrary parts =+  ( "#pragma once\n\n" <> cHeader parts,+    disableWarnings <> cHeader parts <> cUtils parts <> cLib parts+  )++-- | As executable with command-line interface.+asExecutable :: CParts -> String+asExecutable (CParts a b c d) = disableWarnings <> a <> b <> c <> d++-- | Compile imperative program to a C program.  Always uses the+-- function named "main" as entry point, so make sure it is defined.+compileProg ::+  MonadFreshNames m =>+  String ->+  Operations op () ->+  CompilerM op () () ->+  String ->+  [Space] ->+  [Option] ->+  Definitions op ->+  m CParts+compileProg backend ops extra header_extra spaces options prog = do+  src <- getNameSource+  let ((prototypes, definitions, entry_points), endstate) =+        runCompilerM ops src () compileProg'+      (entry_point_decls, cli_entry_point_decls, entry_point_inits) =+        unzip3 entry_points+      option_parser = generateOptionParser "parse_options" $ genericOptions ++ options++  let headerdefs =+        [C.cunit|+$esc:("// Headers\n")+$esc:("#include <stdint.h>")+$esc:("#include <stddef.h>")+$esc:("#include <stdbool.h>")+$esc:(header_extra)++$esc:("\n// Initialisation\n")+$edecls:(initDecls endstate)++$esc:("\n// Arrays\n")+$edecls:(arrayDecls endstate)++$esc:("\n// Opaque values\n")+$edecls:(opaqueDecls endstate)++$esc:("\n// Entry points\n")+$edecls:(entryDecls endstate)++$esc:("\n// Miscellaneous\n")+$edecls:(miscDecls endstate)+$esc:("#define FUTHARK_BACKEND_"++backend)+                           |]++  let utildefs =+        [C.cunit|+$esc:("#include <stdio.h>")+$esc:("#include <stdlib.h>")+$esc:("#include <stdbool.h>")+$esc:("#include <math.h>")+$esc:("#include <stdint.h>")+// If NDEBUG is set, the assert() macro will do nothing. Since Futhark+// (unfortunately) makes use of assert() for error detection (and even some+// side effects), we want to avoid that.+$esc:("#undef NDEBUG")+$esc:("#include <assert.h>")+$esc:("#include <stdarg.h>")++$esc:util_h++$esc:timing_h+|]++  let clidefs =+        [C.cunit|+$esc:("#include <string.h>")+$esc:("#include <inttypes.h>")+$esc:("#include <errno.h>")+$esc:("#include <ctype.h>")+$esc:("#include <errno.h>")+$esc:("#include <getopt.h>")++$esc:values_h++$esc:("#define __private")++static int binary_output = 0;+static typename FILE *runtime_file;+static int perform_warmup = 0;+static int num_runs = 1;+// If the entry point is NULL, the program will terminate after doing initialisation and such.+static const char *entry_point = "main";++$esc:tuning_h++$func:option_parser++$edecls:cli_entry_point_decls++typedef void entry_point_fun(struct futhark_context*);++struct entry_point_entry {+  const char *name;+  entry_point_fun *fun;+};++int main(int argc, char** argv) {+  fut_progname = argv[0];++  struct entry_point_entry entry_points[] = {+    $inits:entry_point_inits+  };++  struct futhark_context_config *cfg = futhark_context_config_new();+  assert(cfg != NULL);++  int parsed_options = parse_options(cfg, argc, argv);+  argc -= parsed_options;+  argv += parsed_options;++  if (argc != 0) {+    futhark_panic(1, "Excess non-option: %s\n", argv[0]);+  }++  struct futhark_context *ctx = futhark_context_new(cfg);+  assert (ctx != NULL);++  char* error = futhark_context_get_error(ctx);+  if (error != NULL) {+    futhark_panic(1, "%s", error);+  }++  if (entry_point != NULL) {+    int num_entry_points = sizeof(entry_points) / sizeof(entry_points[0]);+    entry_point_fun *entry_point_fun = NULL;+    for (int i = 0; i < num_entry_points; i++) {+      if (strcmp(entry_points[i].name, entry_point) == 0) {+        entry_point_fun = entry_points[i].fun;+        break;+      }+    }++    if (entry_point_fun == NULL) {+      fprintf(stderr, "No entry point '%s'.  Select another with --entry-point.  Options are:\n",+                      entry_point);+      for (int i = 0; i < num_entry_points; i++) {+        fprintf(stderr, "%s\n", entry_points[i].name);+      }+      return 1;+    }++    entry_point_fun(ctx);++    if (runtime_file != NULL) {+      fclose(runtime_file);+    }++    char *report = futhark_context_report(ctx);+    fputs(report, stderr);+    free(report);+  }++  futhark_context_free(ctx);+  futhark_context_config_free(cfg);+  return 0;+}+                        |]++  let early_decls = DL.toList $ compEarlyDecls endstate+  let lib_decls = DL.toList $ compLibDecls endstate+  let libdefs =+        [C.cunit|+$esc:("#ifdef _MSC_VER\n#define inline __inline\n#endif")+$esc:("#include <string.h>")+$esc:("#include <inttypes.h>")+$esc:("#include <ctype.h>")+$esc:("#include <errno.h>")+$esc:("#include <assert.h>")++$esc:(header_extra)++$esc:lock_h++$edecls:builtin++$edecls:early_decls++$edecls:prototypes++$edecls:lib_decls++$edecls:(map funcToDef definitions)++$edecls:(arrayDefinitions endstate)++$edecls:(opaqueDefinitions endstate)++$edecls:entry_point_decls+  |]++  return $ CParts (pretty headerdefs) (pretty utildefs) (pretty clidefs) (pretty libdefs)+  where+    compileProg' = do+      let Definitions consts (Functions funs) = prog++      (memstructs, memfuns, memreport) <- unzip3 <$> mapM defineMemorySpace spaces++      get_consts <- compileConstants consts++      ctx_ty <- contextType++      (prototypes, definitions) <-+        unzip <$> mapM (compileFun get_consts [[C.cparam|$ty:ctx_ty *ctx|]]) funs++      mapM_ earlyDecl memstructs+      entry_points <-+        mapM (uncurry onEntryPoint) $ filter (functionEntry . snd) funs++      extra++      mapM_ earlyDecl $ concat memfuns++      commonLibFuns memreport++      return (prototypes, definitions, entry_points)++    funcToDef func = C.FuncDef func loc+      where+        loc = case func of+          C.OldFunc _ _ _ _ _ _ l -> l+          C.Func _ _ _ _ _ l -> l++    builtin =+      cIntOps ++ cFloat32Ops ++ cFloat64Ops ++ cFloatConvOps+        ++ cFloat32Funs+        ++ cFloat64Funs++    util_h = $(embedStringFile "rts/c/util.h")+    values_h = $(embedStringFile "rts/c/values.h")+    timing_h = $(embedStringFile "rts/c/timing.h")+    lock_h = $(embedStringFile "rts/c/lock.h")+    tuning_h = $(embedStringFile "rts/c/tuning.h")++commonLibFuns :: [C.BlockItem] -> CompilerM op s ()+commonLibFuns memreport = do+  ctx <- contextType+  profilereport <- gets $ DL.toList . compProfileItems++  publicDef_ "context_report" MiscDecl $ \s ->+    ( [C.cedecl|char* $id:s($ty:ctx *ctx);|],+      [C.cedecl|char* $id:s($ty:ctx *ctx) {+                 struct str_builder builder;+                 str_builder_init(&builder);+                 if (ctx->detail_memory || ctx->profiling) {+                   $items:memreport+                 }+                 if (ctx->profiling) {+                   $items:profilereport+                 }+                 return builder.str;+               }|]+    )++  publicDef_ "context_get_error" MiscDecl $ \s ->+    ( [C.cedecl|char* $id:s($ty:ctx* ctx);|],+      [C.cedecl|char* $id:s($ty:ctx* ctx) {+                         char* error = ctx->error;+                         ctx->error = NULL;+                         return error;+                       }|]+    )++  publicDef_ "context_pause_profiling" MiscDecl $ \s ->+    ( [C.cedecl|void $id:s($ty:ctx* ctx);|],+      [C.cedecl|void $id:s($ty:ctx* ctx) {+                 ctx->profiling_paused = 1;+               }|]+    )++  publicDef_ "context_unpause_profiling" MiscDecl $ \s ->+    ( [C.cedecl|void $id:s($ty:ctx* ctx);|],+      [C.cedecl|void $id:s($ty:ctx* ctx) {+                 ctx->profiling_paused = 0;+               }|]+    )++compileConstants :: Constants op -> CompilerM op s [C.BlockItem]+compileConstants (Constants ps init_consts) = do+  ctx_ty <- contextType+  const_fields <- mapM constParamField ps+  -- Avoid an empty struct, as that is apparently undefined behaviour.+  let const_fields'+        | null const_fields = [[C.csdecl|int dummy;|]]+        | otherwise = const_fields+  contextField "constants" [C.cty|struct { $sdecls:const_fields' }|] Nothing+  earlyDecl [C.cedecl|int init_constants($ty:ctx_ty*);|]+  earlyDecl [C.cedecl|int free_constants($ty:ctx_ty*);|]++  -- We locally define macros for the constants, so that when we+  -- generate assignments to local variables, we actually assign into+  -- the constants struct.  This is not needed for functions, because+  -- they can only read constants, not write them.+  let (defs, undefs) = unzip $ map constMacro ps+  init_consts' <- blockScope $ do+    mapM_ resetMemConst ps+    compileCode init_consts+  libDecl+    [C.cedecl|int init_constants($ty:ctx_ty *ctx) {+      (void)ctx;+      int err = 0;+      $items:defs+      $items:init_consts'+      $items:undefs+      cleanup:+      return err;+    }|]++  free_consts <- collect $ mapM_ freeConst ps+  libDecl+    [C.cedecl|int free_constants($ty:ctx_ty *ctx) {+      (void)ctx;+      $items:free_consts+      return 0;+    }|]++  mapM getConst ps+  where+    constParamField (ScalarParam name bt) = do+      let ctp = primTypeToCType bt+      return [C.csdecl|$ty:ctp $id:name;|]+    constParamField (MemParam name space) = do+      ty <- memToCType name space+      return [C.csdecl|$ty:ty $id:name;|]++    constMacro p = ([C.citem|$escstm:def|], [C.citem|$escstm:undef|])+      where+        p' = pretty (C.toIdent (paramName p) mempty)+        def = "#define " ++ p' ++ " (" ++ "ctx->constants." ++ p' ++ ")"+        undef = "#undef " ++ p'++    resetMemConst ScalarParam {} = return ()+    resetMemConst (MemParam name space) = resetMem name space++    freeConst ScalarParam {} = return ()+    freeConst (MemParam name space) = unRefMem [C.cexp|ctx->constants.$id:name|] space++    getConst (ScalarParam name bt) = do+      let ctp = primTypeToCType bt+      return [C.citem|$ty:ctp $id:name = ctx->constants.$id:name;|]+    getConst (MemParam name space) = do+      ty <- memToCType name space+      return [C.citem|$ty:ty $id:name = ctx->constants.$id:name;|]++cachingMemory ::+  M.Map VName Space ->+  ([C.BlockItem] -> [C.Stm] -> CompilerM op s a) ->+  CompilerM op s a+cachingMemory lexical f = do+  -- We only consider lexical 'DefaultSpace' memory blocks to be+  -- cached.  This is not a deep technical restriction, but merely a+  -- heuristic based on GPU memory usually involving larger+  -- allocations, that do not suffer from the overhead of reference+  -- counting.+  let cached = M.keys $ M.filter (== DefaultSpace) lexical++  cached' <- forM cached $ \mem -> do+    size <- newVName $ pretty mem <> "_cached_size"+    return (mem, size)++  let lexMem env =+        env+          { envCachedMem =+              M.fromList (map (first (`C.toExp` noLoc)) cached')+                <> envCachedMem env+          }++      declCached (mem, size) =+        [ [C.citem|size_t $id:size = 0;|],+          [C.citem|$ty:defaultMemBlockType $id:mem = NULL;|]+        ]++      freeCached (mem, _) =+        [C.cstm|free($id:mem);|]++  local lexMem $ f (concatMap declCached cached') (map freeCached cached')++compileFun :: [C.BlockItem] -> [C.Param] -> (Name, Function op) -> CompilerM op s (C.Definition, C.Func)+compileFun get_constants extra (fname, func@(Function _ outputs inputs body _ _)) = do+  (outparams, out_ptrs) <- unzip <$> mapM compileOutput outputs+  inparams <- mapM compileInput inputs++  cachingMemory (lexicalMemoryUsage func) $ \decl_cached free_cached -> do+    body' <- blockScope $ compileFunBody out_ptrs outputs body++    return+      ( [C.cedecl|static int $id:(funName fname)($params:extra, $params:outparams, $params:inparams);|],+        [C.cfun|static int $id:(funName fname)($params:extra, $params:outparams, $params:inparams) {+               $stms:ignores+               int err = 0;+               $items:decl_cached+               $items:get_constants+               $items:body'+              cleanup:+               {}+               $stms:free_cached+               return err;+  }|]+      )+  where+    -- Ignore all the boilerplate parameters, just in case we don't+    -- actually need to use them.+    ignores = [[C.cstm|(void)$id:p;|] | C.Param (Just p) _ _ _ <- extra]++    compileInput (ScalarParam name bt) = do+      let ctp = primTypeToCType bt+      return [C.cparam|$ty:ctp $id:name|]+    compileInput (MemParam name space) = do+      ty <- memToCType name space+      return [C.cparam|$ty:ty $id:name|]++    compileOutput (ScalarParam name bt) = do+      let ctp = primTypeToCType bt+      p_name <- newVName $ "out_" ++ baseString name+      return ([C.cparam|$ty:ctp *$id:p_name|], [C.cexp|$id:p_name|])+    compileOutput (MemParam name space) = do+      ty <- memToCType name space+      p_name <- newVName $ baseString name ++ "_p"+      return ([C.cparam|$ty:ty *$id:p_name|], [C.cexp|$id:p_name|])++compilePrimValue :: PrimValue -> C.Exp+compilePrimValue (IntValue (Int8Value k)) = [C.cexp|$int:k|]+compilePrimValue (IntValue (Int16Value k)) = [C.cexp|$int:k|]+compilePrimValue (IntValue (Int32Value k)) = [C.cexp|$int:k|]+compilePrimValue (IntValue (Int64Value k)) = [C.cexp|$int:k|]+compilePrimValue (FloatValue (Float64Value x))+  | isInfinite x =+    if x > 0 then [C.cexp|INFINITY|] else [C.cexp|-INFINITY|]+  | isNaN x =+    [C.cexp|NAN|]+  | otherwise =+    [C.cexp|$double:x|]+compilePrimValue (FloatValue (Float32Value x))+  | isInfinite x =+    if x > 0 then [C.cexp|INFINITY|] else [C.cexp|-INFINITY|]+  | isNaN x =+    [C.cexp|NAN|]+  | otherwise =+    [C.cexp|$float:x|]+compilePrimValue (BoolValue b) =+  [C.cexp|$int:b'|]+  where+    b' :: Int+    b' = if b then 1 else 0+compilePrimValue Checked =+  [C.cexp|0|]++derefPointer :: C.Exp -> C.Exp -> C.Type -> C.Exp+derefPointer ptr i res_t =+  [C.cexp|(($ty:res_t)$exp:ptr)[$exp:i]|]++volQuals :: Volatility -> [C.TypeQual]+volQuals Volatile = [C.ctyquals|volatile|]+volQuals Nonvolatile = []++writeScalarPointerWithQuals :: PointerQuals op s -> WriteScalar op s+writeScalarPointerWithQuals quals_f dest i elemtype space vol v = do+  quals <- quals_f space+  let quals' = volQuals vol ++ quals+      deref =+        derefPointer+          dest+          i+          [C.cty|$tyquals:quals' $ty:elemtype*|]+  stm [C.cstm|$exp:deref = $exp:v;|]++readScalarPointerWithQuals :: PointerQuals op s -> ReadScalar op s+readScalarPointerWithQuals quals_f dest i elemtype space vol = do+  quals <- quals_f space+  let quals' = volQuals vol ++ quals+  return $ derefPointer dest i [C.cty|$tyquals:quals' $ty:elemtype*|]++compileExpToName :: String -> PrimType -> Exp -> CompilerM op s VName+compileExpToName _ _ (LeafExp (ScalarVar v) _) =+  return v+compileExpToName desc t e = do+  desc' <- newVName desc+  e' <- compileExp e+  decl [C.cdecl|$ty:(primTypeToCType t) $id:desc' = $e';|]+  return desc'++compileExp :: Exp -> CompilerM op s C.Exp+compileExp = compilePrimExp compileLeaf+  where+    compileLeaf (ScalarVar src) =+      return [C.cexp|$id:src|]+    compileLeaf (Index src (Count iexp) restype DefaultSpace vol) = do+      src' <- rawMem src+      derefPointer src'+        <$> compileExp (untyped iexp)+        <*> pure [C.cty|$tyquals:(volQuals vol) $ty:(primTypeToCType restype)*|]+    compileLeaf (Index src (Count iexp) restype (Space space) vol) =+      join $+        asks envReadScalar+          <*> rawMem src+          <*> compileExp (untyped iexp)+          <*> pure (primTypeToCType restype)+          <*> pure space+          <*> pure vol+    compileLeaf (Index src (Count iexp) _ ScalarSpace {} _) = do+      iexp' <- compileExp $ untyped iexp+      return [C.cexp|$id:src[$exp:iexp']|]+    compileLeaf (SizeOf t) =+      return [C.cexp|(typename int64_t)sizeof($ty:t')|]+      where+        t' = primTypeToCType t++-- | Tell me how to compile a @v@, and I'll Compile any @PrimExp v@ for you.+compilePrimExp :: Monad m => (v -> m C.Exp) -> PrimExp v -> m C.Exp+compilePrimExp _ (ValueExp val) =+  return $ compilePrimValue val+compilePrimExp f (LeafExp v _) =+  f v+compilePrimExp f (UnOpExp Complement {} x) = do+  x' <- compilePrimExp f x+  return [C.cexp|~$exp:x'|]+compilePrimExp f (UnOpExp Not {} x) = do+  x' <- compilePrimExp f x+  return [C.cexp|!$exp:x'|]+compilePrimExp f (UnOpExp Abs {} x) = do+  x' <- compilePrimExp f x+  return [C.cexp|abs($exp:x')|]+compilePrimExp f (UnOpExp (FAbs Float32) x) = do+  x' <- compilePrimExp f x+  return [C.cexp|(float)fabs($exp:x')|]+compilePrimExp f (UnOpExp (FAbs Float64) x) = do+  x' <- compilePrimExp f x+  return [C.cexp|fabs($exp:x')|]+compilePrimExp f (UnOpExp SSignum {} x) = do+  x' <- compilePrimExp f x+  return [C.cexp|($exp:x' > 0) - ($exp:x' < 0)|]+compilePrimExp f (UnOpExp USignum {} x) = do+  x' <- compilePrimExp f x+  return [C.cexp|($exp:x' > 0) - ($exp:x' < 0) != 0|]+compilePrimExp f (CmpOpExp cmp x y) = do+  x' <- compilePrimExp f x+  y' <- compilePrimExp f y+  return $ case cmp of+    CmpEq {} -> [C.cexp|$exp:x' == $exp:y'|]+    FCmpLt {} -> [C.cexp|$exp:x' < $exp:y'|]+    FCmpLe {} -> [C.cexp|$exp:x' <= $exp:y'|]+    CmpLlt {} -> [C.cexp|$exp:x' < $exp:y'|]+    CmpLle {} -> [C.cexp|$exp:x' <= $exp:y'|]+    _ -> [C.cexp|$id:(pretty cmp)($exp:x', $exp:y')|]+compilePrimExp f (ConvOpExp conv x) = do+  x' <- compilePrimExp f x+  return [C.cexp|$id:(pretty conv)($exp:x')|]+compilePrimExp f (BinOpExp bop x y) = do+  x' <- compilePrimExp f x+  y' <- compilePrimExp f y+  -- Note that integer addition, subtraction, and multiplication with+  -- OverflowWrap are not handled by explicit operators, but rather by+  -- functions.  This is because we want to implicitly convert them to+  -- unsigned numbers, so we can do overflow without invoking+  -- undefined behaviour.+  return $ case bop of+    Add _ OverflowUndef -> [C.cexp|$exp:x' + $exp:y'|]+    Sub _ OverflowUndef -> [C.cexp|$exp:x' - $exp:y'|]+    Mul _ OverflowUndef -> [C.cexp|$exp:x' * $exp:y'|]+    FAdd {} -> [C.cexp|$exp:x' + $exp:y'|]+    FSub {} -> [C.cexp|$exp:x' - $exp:y'|]+    FMul {} -> [C.cexp|$exp:x' * $exp:y'|]+    FDiv {} -> [C.cexp|$exp:x' / $exp:y'|]+    Xor {} -> [C.cexp|$exp:x' ^ $exp:y'|]+    And {} -> [C.cexp|$exp:x' & $exp:y'|]+    Or {} -> [C.cexp|$exp:x' | $exp:y'|]+    Shl {} -> [C.cexp|$exp:x' << $exp:y'|]+    LogAnd {} -> [C.cexp|$exp:x' && $exp:y'|]+    LogOr {} -> [C.cexp|$exp:x' || $exp:y'|]+    _ -> [C.cexp|$id:(pretty bop)($exp:x', $exp:y')|]+compilePrimExp f (FunExp h args _) = do+  args' <- mapM (compilePrimExp f) args+  return [C.cexp|$id:(funName (nameFromString h))($args:args')|]++compileCode :: Code op -> CompilerM op s ()+compileCode (Op op) =+  join $ asks envOpCompiler <*> pure op+compileCode Skip = return ()+compileCode (Comment s code) = do+  xs <- blockScope $ compileCode code+  let comment = "// " ++ s+  stm+    [C.cstm|$comment:comment+              { $items:xs }+             |]+compileCode (DebugPrint s (Just e)) = do+  e' <- compileExp e+  stm+    [C.cstm|if (ctx->debugging) {+          fprintf(stderr, $string:fmtstr, $exp:s, ($ty:ety)$exp:e', '\n');+       }|]+  where+    (fmt, ety) = case primExpType e of+      IntType _ -> ("llu", [C.cty|long long int|])+      FloatType _ -> ("f", [C.cty|double|])+      _ -> ("d", [C.cty|int|])+    fmtstr = "%s: %" ++ fmt ++ "%c"+compileCode (DebugPrint s Nothing) =+  stm+    [C.cstm|if (ctx->debugging) {+          fprintf(stderr, "%s\n", $exp:s);+       }|]+compileCode c+  | Just (name, vol, t, e, c') <- declareAndSet c = do+    let ct = primTypeToCType t+    e' <- compileExp e+    item [C.citem|$tyquals:(volQuals vol) $ty:ct $id:name = $exp:e';|]+    compileCode c'+compileCode (c1 :>>: c2) = compileCode c1 >> compileCode c2+compileCode (Assert e msg (loc, locs)) = do+  e' <- compileExp e+  err <-+    collect $+      join $+        asks (opsError . envOperations) <*> pure msg <*> pure stacktrace+  stm [C.cstm|if (!$exp:e') { $items:err }|]+  where+    stacktrace = prettyStacktrace 0 $ map locStr $ loc : locs+compileCode (Allocate _ _ ScalarSpace {}) =+  -- Handled by the declaration of the memory block, which is+  -- translated to an actual array.+  return ()+compileCode (Allocate name (Count (TPrimExp e)) space) = do+  size <- compileExp e+  cached <- cacheMem name+  case cached of+    Just cur_size ->+      stm+        [C.cstm|if ($exp:cur_size < (size_t)$exp:size) {+                    $exp:name = realloc($exp:name, $exp:size);+                    $exp:cur_size = $exp:size;+                  }|]+    _ ->+      allocMem name size space [C.cstm|{err = 1; goto cleanup;}|]+compileCode (Free name space) = do+  cached <- isJust <$> cacheMem name+  unless cached $ unRefMem name space+compileCode (For i bound body) = do+  let i' = C.toIdent i+      t = primTypeToCType $ primExpType bound+  bound' <- compileExp bound+  body' <- blockScope $ compileCode body+  stm+    [C.cstm|for ($ty:t $id:i' = 0; $id:i' < $exp:bound'; $id:i'++) {+            $items:body'+          }|]+compileCode (While cond body) = do+  cond' <- compileExp $ untyped cond+  body' <- blockScope $ compileCode body+  stm+    [C.cstm|while ($exp:cond') {+            $items:body'+          }|]+compileCode (If cond tbranch fbranch) = do+  cond' <- compileExp $ untyped cond+  tbranch' <- blockScope $ compileCode tbranch+  fbranch' <- blockScope $ compileCode fbranch+  stm $ case (tbranch', fbranch') of+    (_, []) ->+      [C.cstm|if ($exp:cond') { $items:tbranch' }|]+    ([], _) ->+      [C.cstm|if (!($exp:cond')) { $items:fbranch' }|]+    _ ->+      [C.cstm|if ($exp:cond') { $items:tbranch' } else { $items:fbranch' }|]+compileCode (Copy dest (Count destoffset) DefaultSpace src (Count srcoffset) DefaultSpace (Count size)) =+  join $+    copyMemoryDefaultSpace+      <$> rawMem dest+      <*> compileExp (untyped destoffset)+      <*> rawMem src+      <*> compileExp (untyped srcoffset)+      <*> compileExp (untyped size)+compileCode (Copy dest (Count destoffset) destspace src (Count srcoffset) srcspace (Count size)) = do+  copy <- asks envCopy+  join $+    copy+      <$> rawMem dest+      <*> compileExp (untyped destoffset)+      <*> pure destspace+      <*> rawMem src+      <*> compileExp (untyped srcoffset)+      <*> pure srcspace+      <*> compileExp (untyped size)+compileCode (Write dest (Count idx) elemtype DefaultSpace vol elemexp) = do+  dest' <- rawMem dest+  deref <-+    derefPointer dest'+      <$> compileExp (untyped idx)+      <*> pure [C.cty|$tyquals:(volQuals vol) $ty:(primTypeToCType elemtype)*|]+  elemexp' <- compileExp elemexp+  stm [C.cstm|$exp:deref = $exp:elemexp';|]+compileCode (Write dest (Count idx) _ ScalarSpace {} _ elemexp) = do+  idx' <- compileExp (untyped idx)+  elemexp' <- compileExp elemexp+  stm [C.cstm|$id:dest[$exp:idx'] = $exp:elemexp';|]+compileCode (Write dest (Count idx) elemtype (Space space) vol elemexp) =+  join $+    asks envWriteScalar+      <*> rawMem dest+      <*> compileExp (untyped idx)+      <*> pure (primTypeToCType elemtype)+      <*> pure space+      <*> pure vol+      <*> compileExp elemexp+compileCode (DeclareMem name space) =+  declMem name space+compileCode (DeclareScalar name vol t) = do+  let ct = primTypeToCType t+  decl [C.cdecl|$tyquals:(volQuals vol) $ty:ct $id:name;|]+compileCode (DeclareArray name ScalarSpace {} _ _) =+  error $ "Cannot declare array " ++ pretty name ++ " in scalar space."+compileCode (DeclareArray name DefaultSpace t vs) = do+  name_realtype <- newVName $ baseString name ++ "_realtype"+  let ct = primTypeToCType t+  case vs of+    ArrayValues vs' -> do+      let vs'' = [[C.cinit|$exp:(compilePrimValue v)|] | v <- vs']+      earlyDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:(length vs')] = {$inits:vs''};|]+    ArrayZeros n ->+      earlyDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:n];|]+  -- Fake a memory block.+  contextField+    (C.toIdent name noLoc)+    [C.cty|struct memblock|]+    $ Just [C.cexp|(struct memblock){NULL, (char*)$id:name_realtype, 0}|]+  item [C.citem|struct memblock $id:name = ctx->$id:name;|]+compileCode (DeclareArray name (Space space) t vs) =+  join $+    asks envStaticArray+      <*> pure name+      <*> pure space+      <*> pure t+      <*> pure vs+-- For assignments of the form 'x = x OP e', we generate C assignment+-- operators to make the resulting code slightly nicer.  This has no+-- effect on performance.+compileCode (SetScalar dest (BinOpExp op (LeafExp (ScalarVar x) _) y))+  | dest == x,+    Just f <- assignmentOperator op = do+    y' <- compileExp y+    stm [C.cstm|$exp:(f dest y');|]+compileCode (SetScalar dest src) = do+  src' <- compileExp src+  stm [C.cstm|$id:dest = $exp:src';|]+compileCode (SetMem dest src space) =+  setMem dest src space+compileCode (Call dests fname args) =+  join $+    asks (opsCall . envOperations)+      <*> pure dests+      <*> pure fname+      <*> mapM compileArg args+  where+    compileArg (MemArg m) = return [C.cexp|$exp:m|]+    compileArg (ExpArg e) = compileExp e++blockScope :: CompilerM op s () -> CompilerM op s [C.BlockItem]+blockScope = fmap snd . blockScope'++blockScope' :: CompilerM op s a -> CompilerM op s (a, [C.BlockItem])+blockScope' m = do+  old_allocs <- gets compDeclaredMem+  (x, xs) <- pass $ do+    (x, w) <- listen m+    let xs = DL.toList $ accItems w+    return ((x, xs), const mempty)+  new_allocs <- gets $ filter (`notElem` old_allocs) . compDeclaredMem+  modify $ \s -> s {compDeclaredMem = old_allocs}+  releases <- collect $ mapM_ (uncurry unRefMem) new_allocs+  return (x, xs <> releases)++compileFunBody :: [C.Exp] -> [Param] -> Code op -> CompilerM op s ()+compileFunBody output_ptrs outputs code = do+  mapM_ declareOutput outputs+  compileCode code+  zipWithM_ setRetVal' output_ptrs outputs+  where+    declareOutput (MemParam name space) =+      declMem name space+    declareOutput (ScalarParam name pt) = do+      let ctp = primTypeToCType pt+      decl [C.cdecl|$ty:ctp $id:name;|]++    setRetVal' p (MemParam name space) = do+      resetMem [C.cexp|*$exp:p|] space+      setMem [C.cexp|*$exp:p|] name space+    setRetVal' p (ScalarParam name _) =+      stm [C.cstm|*$exp:p = $id:name;|]++declareAndSet :: Code op -> Maybe (VName, Volatility, PrimType, Exp, Code op)+declareAndSet code = do+  (DeclareScalar name vol t, code') <- nextCode code+  (SetScalar dest e, code'') <- nextCode code'+  guard $ name == dest+  Just (name, vol, t, e, code'')++nextCode :: Code op -> Maybe (Code op, Code op)+nextCode (x :>>: y)+  | Just (x_a, x_b) <- nextCode x =+    Just (x_a, x_b <> y)+  | otherwise =+    Just (x, y)+nextCode _ = Nothing++assignmentOperator :: BinOp -> Maybe (VName -> C.Exp -> C.Exp)+assignmentOperator Add {} = Just $ \d e -> [C.cexp|$id:d += $exp:e|]+assignmentOperator Sub {} = Just $ \d e -> [C.cexp|$id:d -= $exp:e|]+assignmentOperator Mul {} = Just $ \d e -> [C.cexp|$id:d *= $exp:e|]+assignmentOperator _ = Nothing++-- | Return an expression multiplying together the given expressions.+-- If an empty list is given, the expression @1@ is returned.+cproduct :: [C.Exp] -> C.Exp+cproduct [] = [C.cexp|1|]+cproduct (e : es) = foldl mult e es+  where+    mult x y = [C.cexp|$exp:x * $exp:y|]
src/Futhark/CodeGen/Backends/GenericC/Options.hs view
@@ -1,36 +1,42 @@ {-# LANGUAGE QuasiQuotes #-}+ -- | This module defines a generator for @getopt_long@ based command -- line argument parsing.  Each option is associated with arbitrary C -- code that will perform side effects, usually by setting some global -- variables. module Futhark.CodeGen.Backends.GenericC.Options-       ( Option (..)-       , OptionArgument (..)-       , generateOptionParser-       )-       where+  ( Option (..),+    OptionArgument (..),+    generateOptionParser,+  )+where +import Data.Char (isSpace)+import Data.Function ((&))+import Data.List (intercalate) import Data.Maybe--import qualified Language.C.Syntax as C import qualified Language.C.Quote.C as C+import qualified Language.C.Syntax as C  -- | Specification if a single command line option.  The option must -- have a long name, and may also have a short name. -- -- In the action, the option argument (if any) is stored as in the -- @char*@-typed variable @optarg@.-data Option = Option { optionLongName :: String-                     , optionShortName :: Maybe Char-                     , optionArgument :: OptionArgument-                     , optionAction :: C.Stm-                     }+data Option = Option+  { optionLongName :: String,+    optionShortName :: Maybe Char,+    optionArgument :: OptionArgument,+    optionDescription :: String,+    optionAction :: C.Stm+  }  -- | Whether an option accepts an argument.-data OptionArgument = NoArgument-                    | RequiredArgument String-                    -- ^ The 'String' becomes part of the help text.-                    | OptionalArgument+data OptionArgument+  = NoArgument+  | -- | The 'String' becomes part of the help text.+    RequiredArgument String+  | OptionalArgument  -- | Generate an option parser as a function of the given name, that -- accepts the given command line options.  The result is a function@@ -46,6 +52,8 @@         static struct option long_options[] = { $inits:option_fields, {0, 0, 0, 0} }; +       static char* option_descriptions = $string:option_descriptions;+        while (($id:chosen_option =                  getopt_long(argc, argv, $string:option_string, long_options, NULL)) != -1) {          $stms:option_applications@@ -60,47 +68,76 @@        return optind;      }          |]-  where chosen_option = "ch"-        option_string = ':' : optionString options-        option_applications = optionApplications chosen_option options-        option_fields = optionFields options-        option_descriptions = unwords $ map describeOption options+  where+    chosen_option = "ch"+    option_string = ':' : optionString options+    option_applications = optionApplications chosen_option options+    option_fields = optionFields options+    option_descriptions = describeOptions options -describeOption :: Option -> String-describeOption opt =-  concat [ "["-         , maybe "" (\c -> "-" ++ [c] ++ "/") $ optionShortName opt-         , "--" ++ optionLongName opt-         , case optionArgument opt of-             NoArgument -> ""-             RequiredArgument what -> " " ++ what-             OptionalArgument -> " [ARG]"-         , "]"-         ]+trim :: String -> String+trim = f . f+  where+    f = reverse . dropWhile isSpace +describeOptions :: [Option] -> String+describeOptions opts =+  let+   in unlines $ fmap extendDesc with_short_descs+  where+    with_short_descs = fmap (\opt -> (opt, shortDesc opt)) opts+    max_short_desc_len = maximum $ fmap (length . snd) with_short_descs+    extendDesc :: (Option, String) -> String+    extendDesc (opt, short) =+      take (max_short_desc_len + 1) (short ++ repeat ' ')+        ++ ( optionDescription opt+               & lines+               & fmap trim+               & intercalate ('\n' : replicate (max_short_desc_len + 1) ' ')+           )+    shortDesc :: Option -> String+    shortDesc opt =+      concat+        [ "  ",+          maybe "" (\c -> "-" ++ [c] ++ "/") $ optionShortName opt,+          "--" ++ optionLongName opt,+          case optionArgument opt of+            NoArgument -> ""+            RequiredArgument what -> " " ++ what+            OptionalArgument -> " [ARG]"+        ]+ optionFields :: [Option] -> [C.Initializer]-optionFields = zipWith field [(1::Int)..]-  where field i option =-          [C.cinit| { $string:(optionLongName option), $id:arg, NULL, $int:i } |]-          where arg = case optionArgument option of-                        NoArgument         -> "no_argument"-                        RequiredArgument _ -> "required_argument"-                        OptionalArgument   -> "optional_argument"+optionFields = zipWith field [(1 :: Int) ..]+  where+    field i option =+      [C.cinit| { $string:(optionLongName option), $id:arg, NULL, $int:i } |]+      where+        arg = case optionArgument option of+          NoArgument -> "no_argument"+          RequiredArgument _ -> "required_argument"+          OptionalArgument -> "optional_argument"  optionApplications :: String -> [Option] -> [C.Stm]-optionApplications chosen_option = zipWith check [(1::Int)..]-  where check i option =-          [C.cstm|if ($exp:cond) $stm:(optionAction option)|]-          where cond = case optionShortName option of-                         Nothing -> [C.cexp|$id:chosen_option == $int:i|]-                         Just c  -> [C.cexp|($id:chosen_option == $int:i) ||+optionApplications chosen_option = zipWith check [(1 :: Int) ..]+  where+    check i option =+      [C.cstm|if ($exp:cond) $stm:(optionAction option)|]+      where+        cond = case optionShortName option of+          Nothing -> [C.cexp|$id:chosen_option == $int:i|]+          Just c ->+            [C.cexp|($id:chosen_option == $int:i) ||                                             ($id:chosen_option == $char:c)|]+ optionString :: [Option] -> String optionString = concat . mapMaybe optionStringChunk-  where optionStringChunk option = do-          short <- optionShortName option-          return $ short :-            case optionArgument option of-              NoArgument         -> ""-              RequiredArgument _ -> ":"-              OptionalArgument   -> "::"+  where+    optionStringChunk option = do+      short <- optionShortName option+      return $+        short :+        case optionArgument option of+          NoArgument -> ""+          RequiredArgument _ -> ":"+          OptionalArgument -> "::"
src/Futhark/CodeGen/Backends/GenericPython.hs view
@@ -1,1034 +1,1213 @@-{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving, LambdaCase #-}-{-# LANGUAGE TupleSections #-}--- | A generic Python code generator which is polymorphic in the type--- of the operations.  Concretely, we use this to handle both--- sequential and PyOpenCL Python code.-module Futhark.CodeGen.Backends.GenericPython-  ( compileProg-  , Constructor (..)-  , emptyConstructor--  , compileName-  , compileVar-  , compileDim-  , compileExp-  , compilePrimExp-  , compileCode-  , compilePrimValue-  , compilePrimType-  , compilePrimTypeExt-  , compilePrimToNp-  , compilePrimToExtNp--  , Operations (..)-  , defaultOperations--  , unpackDim--  , CompilerM (..)-  , OpCompiler-  , WriteScalar-  , ReadScalar-  , Allocate-  , Copy-  , StaticArray-  , EntryOutput-  , EntryInput--  , CompilerEnv(..)-  , CompilerState(..)-  , stm-  , atInit-  , collect'-  , collect-  , simpleCall--  , copyMemoryDefaultSpace-  ) where--import Control.Monad.Identity-import Control.Monad.State-import Control.Monad.Reader-import Control.Monad.Writer-import Control.Monad.RWS-import Data.Maybe-import qualified Data.Map as M--import Futhark.IR.Primitive hiding (Bool)-import Futhark.MonadFreshNames-import Futhark.IR.Syntax (Space(..))-import qualified Futhark.CodeGen.ImpCode as Imp-import Futhark.CodeGen.Backends.GenericPython.AST-import Futhark.CodeGen.Backends.GenericPython.Options-import Futhark.CodeGen.Backends.GenericPython.Definitions-import Futhark.Util (zEncodeString)-import Futhark.IR.Prop (isBuiltInFunction)---- | A substitute expression compiler, tried before the main--- compilation function.-type OpCompiler op s = op -> CompilerM op s ()---- | Write a scalar to the given memory block with the given index and--- in the given memory space.-type WriteScalar op s = PyExp -> PyExp -> PrimType -> Imp.SpaceId -> PyExp-                        -> CompilerM op s ()---- | Read a scalar from the given memory block with the given index and--- in the given memory space.-type ReadScalar op s = PyExp -> PyExp -> PrimType -> Imp.SpaceId-                       -> CompilerM op s PyExp---- | Allocate a memory block of the given size in the given memory--- space, saving a reference in the given variable name.-type Allocate op s = PyExp -> PyExp -> Imp.SpaceId-                     -> CompilerM op s ()---- | Copy from one memory block to another.-type Copy op s = PyExp -> PyExp -> Imp.Space ->-                 PyExp -> PyExp -> Imp.Space ->-                 PyExp -> PrimType ->-                 CompilerM op s ()---- | Create a static array of values - initialised at load time.-type StaticArray op s = VName -> Imp.SpaceId -> PrimType -> Imp.ArrayContents -> CompilerM op s ()---- | Construct the Python array being returned from an entry point.-type EntryOutput op s = VName -> Imp.SpaceId ->-                        PrimType -> Imp.Signedness ->-                        [Imp.DimSize] ->-                        CompilerM op s PyExp---- | Unpack the array being passed to an entry point.-type EntryInput op s = PyExp -> Imp.SpaceId ->-                       PrimType -> Imp.Signedness ->-                       [Imp.DimSize] ->-                       PyExp ->-                       CompilerM op s ()---data Operations op s = Operations { opsWriteScalar :: WriteScalar op s-                                  , opsReadScalar :: ReadScalar op s-                                  , opsAllocate :: Allocate op s-                                  , opsCopy :: Copy op s-                                  , opsStaticArray :: StaticArray op s-                                  , opsCompiler :: OpCompiler op s-                                  , opsEntryOutput :: EntryOutput op s-                                  , opsEntryInput :: EntryInput op s-                                  }---- | A set of operations that fail for every operation involving--- non-default memory spaces.  Uses plain pointers and @malloc@ for--- memory management.-defaultOperations :: Operations op s-defaultOperations = Operations { opsWriteScalar = defWriteScalar-                               , opsReadScalar = defReadScalar-                               , opsAllocate  = defAllocate-                               , opsCopy = defCopy-                               , opsStaticArray = defStaticArray-                               , opsCompiler = defCompiler-                               , opsEntryOutput = defEntryOutput-                               , opsEntryInput = defEntryInput-                               }-  where defWriteScalar _ _ _ _ _ =-          error "Cannot write to non-default memory space because I am dumb"-        defReadScalar _ _ _ _ =-          error "Cannot read from non-default memory space"-        defAllocate _ _ _ =-          error "Cannot allocate in non-default memory space"-        defCopy _ _ _ _ _ _ _ _ =-          error "Cannot copy to or from non-default memory space"-        defStaticArray _ _ _ _ =-          error "Cannot create static array in non-default memory space"-        defCompiler _ =-          error "The default compiler cannot compile extended operations"-        defEntryOutput _ _ _ _ =-          error "Cannot return array not in default memory space"-        defEntryInput _ _ _ _ =-          error "Cannot accept array not in default memory space"--data CompilerEnv op s = CompilerEnv-  { envOperations :: Operations op s-  , envVarExp :: M.Map VName PyExp-  }--envOpCompiler :: CompilerEnv op s -> OpCompiler op s-envOpCompiler = opsCompiler . envOperations--envReadScalar :: CompilerEnv op s -> ReadScalar op s-envReadScalar = opsReadScalar . envOperations--envWriteScalar :: CompilerEnv op s -> WriteScalar op s-envWriteScalar = opsWriteScalar . envOperations--envAllocate :: CompilerEnv op s -> Allocate op s-envAllocate = opsAllocate . envOperations--envCopy :: CompilerEnv op s -> Copy op s-envCopy = opsCopy . envOperations--envStaticArray :: CompilerEnv op s -> StaticArray op s-envStaticArray = opsStaticArray . envOperations--envEntryOutput :: CompilerEnv op s -> EntryOutput op s-envEntryOutput = opsEntryOutput . envOperations--envEntryInput :: CompilerEnv op s -> EntryInput op s-envEntryInput = opsEntryInput . envOperations--newCompilerEnv :: Operations op s -> CompilerEnv op s-newCompilerEnv ops = CompilerEnv { envOperations = ops-                                 , envVarExp = mempty }--data CompilerState s = CompilerState {-    compNameSrc :: VNameSource-  , compInit :: [PyStmt]-  , compUserState :: s-}--newCompilerState :: VNameSource -> s -> CompilerState s-newCompilerState src s = CompilerState { compNameSrc = src-                                       , compInit = []-                                       , compUserState = s }--newtype CompilerM op s a = CompilerM (RWS (CompilerEnv op s) [PyStmt] (CompilerState s) a)-  deriving (Functor, Applicative, Monad,-            MonadState (CompilerState s),-            MonadReader (CompilerEnv op s),-            MonadWriter [PyStmt])--instance MonadFreshNames (CompilerM op s) where-  getNameSource = gets compNameSrc-  putNameSource src = modify $ \s -> s { compNameSrc = src }--collect :: CompilerM op s () -> CompilerM op s [PyStmt]-collect m = pass $ do-  ((), w) <- listen m-  return (w, const mempty)--collect' :: CompilerM op s a -> CompilerM op s (a, [PyStmt])-collect' m = pass $ do-  (x, w) <- listen m-  return ((x, w), const mempty)--atInit :: PyStmt -> CompilerM op s ()-atInit x = modify $ \s ->-  s { compInit = compInit s ++ [x] }--stm :: PyStmt -> CompilerM op s ()-stm x = tell [x]--futharkFun :: String -> String-futharkFun s = "futhark_" ++ zEncodeString s--compileOutput :: [Imp.Param] -> [PyExp]-compileOutput = map (Var . compileName . Imp.paramName)--runCompilerM :: Operations op s-             -> VNameSource-             -> s-             -> CompilerM op s a-             -> a-runCompilerM ops src userstate (CompilerM m) =-  fst $ evalRWS m (newCompilerEnv ops) (newCompilerState src userstate)--standardOptions :: [Option]-standardOptions = [-  Option { optionLongName = "write-runtime-to"-         , optionShortName = Just 't'-         , optionArgument = RequiredArgument "str"-         , optionAction =-           [-             If (Var "runtime_file")-             [Exp $ simpleCall "runtime_file.close" []] []-           , Assign (Var "runtime_file") $-             simpleCall "open" [Var "optarg", String "w"]-           ]-         },-  Option { optionLongName = "runs"-         , optionShortName = Just 'r'-         , optionArgument = RequiredArgument "str"-         , optionAction =-           [ Assign (Var "num_runs") $ Var "optarg"-           , Assign (Var "do_warmup_run") $ Bool True-           ]-         },-  Option { optionLongName = "entry-point"-         , optionShortName = Just 'e'-         , optionArgument = RequiredArgument "str"-         , optionAction =-           [ Assign (Var "entry_point") $ Var "optarg" ]-         },-  Option { optionLongName = "binary-output"-         , optionShortName = Just 'b'-         , optionArgument = NoArgument-         , optionAction = [Assign (Var "binary_output") $ Bool True]-         },-  Option { optionLongName = "tuning"-         , optionShortName = Nothing-         , optionArgument = RequiredArgument "open"-         , optionAction = [Exp $ simpleCall "read_tuning_file" [Var "sizes", Var "optarg"]]-         }-  ]----- | The class generated by the code generator must have a--- constructor, although it can be vacuous.-data Constructor = Constructor [String] [PyStmt]---- | A constructor that takes no arguments and does nothing.-emptyConstructor :: Constructor-emptyConstructor = Constructor ["self"] [Pass]--constructorToFunDef :: Constructor -> [PyStmt] -> PyFunDef-constructorToFunDef (Constructor params body) at_init =-  Def "__init__" params $ body <> at_init--compileProg :: MonadFreshNames m =>-               Maybe String-            -> Constructor-            -> [PyStmt]-            -> [PyStmt]-            -> Operations op s-            -> s-            -> [PyStmt]-            -> [Option]-            -> Imp.Definitions op-            -> m String-compileProg module_name constructor imports defines ops userstate sync options prog = do-  src <- getNameSource-  let prog' = runCompilerM ops src userstate compileProg'-      maybe_shebang =-        case module_name of Nothing -> "#!/usr/bin/env python\n"-                            Just _  -> ""-  return $ maybe_shebang ++-    pretty (PyProg $ imports ++-            [ Import "argparse" Nothing-            , Assign (Var "sizes") $ Dict []-            ] ++-            defines ++-            [Escape pyUtility] ++-            prog')-  where Imp.Definitions consts (Imp.Functions funs) = prog-        compileProg' = withConstantSubsts consts $ do--          compileConstants consts--          definitions <- mapM compileFunc funs-          at_inits <- gets compInit--          let constructor' = constructorToFunDef constructor at_inits--          case module_name of-            Just name -> do-              (entry_points, entry_point_types) <--                unzip <$> mapM (compileEntryFun sync) (filter (Imp.functionEntry . snd) funs)-              return [ClassDef $ Class name $-                       Assign (Var "entry_points") (Dict entry_point_types) :-                       map FunDef (constructor' : definitions ++ entry_points)]-            Nothing -> do-              let classinst = Assign (Var "self") $ simpleCall "internal" []-              (entry_point_defs, entry_point_names, entry_points) <--                unzip3 <$> mapM (callEntryFun sync)-                (filter (Imp.functionEntry . snd) funs)-              return (parse_options ++-                      ClassDef (Class "internal" $ map FunDef $-                                constructor' : definitions) :-                      classinst :-                      map FunDef entry_point_defs ++-                      selectEntryPoint entry_point_names entry_points)--        parse_options =-          Assign (Var "runtime_file") None :-          Assign (Var "do_warmup_run") (Bool False) :-          Assign (Var "num_runs") (Integer 1) :-          Assign (Var "entry_point") (String "main") :-          Assign (Var "binary_output") (Bool False) :-          generateOptionParser (standardOptions ++ options)--        selectEntryPoint entry_point_names entry_points =-          [ Assign (Var "entry_points") $-              Dict $ zip (map String entry_point_names) entry_points,-            Assign (Var "entry_point_fun") $-              simpleCall "entry_points.get" [Var "entry_point"],-            If (BinOp "==" (Var "entry_point_fun") None)-              [Exp $ simpleCall "sys.exit"-                  [Call (Field-                          (String "No entry point '{}'.  Select another with --entry point.  Options are:\n{}")-                          "format")-                    [Arg $ Var "entry_point",--                     Arg $ Call (Field (String "\n") "join")-                     [Arg $ simpleCall "entry_points.keys" []]]]]-              [Exp $ simpleCall "entry_point_fun" []]-          ]--withConstantSubsts :: Imp.Constants op -> CompilerM op s a -> CompilerM op s a-withConstantSubsts (Imp.Constants ps _) =-  local $ \env -> env { envVarExp = foldMap constExp ps }-  where constExp p =-          M.singleton (Imp.paramName p) $ Index (Var "self.constants") $-          IdxExp $ String $ pretty $ Imp.paramName p--compileConstants :: Imp.Constants op -> CompilerM op s ()-compileConstants (Imp.Constants _ init_consts) = do-  atInit $ Assign (Var "self.constants") $ Dict []-  mapM_ atInit =<< collect (compileCode init_consts)--compileFunc :: (Name, Imp.Function op) -> CompilerM op s PyFunDef-compileFunc (fname, Imp.Function _ outputs inputs body _ _) = do-  body' <- collect $ compileCode body-  let inputs' = map (compileName . Imp.paramName) inputs-  let ret = Return $ tupleOrSingle $ compileOutput outputs-  return $ Def (futharkFun . nameToString $ fname) ("self" : inputs') $-    body'++[ret]--tupleOrSingle :: [PyExp] -> PyExp-tupleOrSingle [e] = e-tupleOrSingle es = Tuple es---- | A 'Call' where the function is a variable and every argument is a--- simple 'Arg'.-simpleCall :: String -> [PyExp] -> PyExp-simpleCall fname = Call (Var fname) . map Arg--compileName :: VName -> String-compileName = zEncodeString . pretty--compileDim :: Imp.DimSize -> PyExp-compileDim (Imp.Constant v) = compilePrimValue v-compileDim (Imp.Var v) = Var $ compileName v--unpackDim :: PyExp -> Imp.DimSize -> Int32 -> CompilerM op s ()-unpackDim arr_name (Imp.Constant c) i = do-  let shape_name = Field arr_name "shape"-  let constant_c = compilePrimValue c-  let constant_i = Integer $ toInteger i-  stm $ Assert (BinOp "==" constant_c (Index shape_name $ IdxExp constant_i)) $-    String "constant dimension wrong"-unpackDim arr_name (Imp.Var var) i = do-  let shape_name = Field arr_name "shape"-      src = Index shape_name $ IdxExp $ Integer $ toInteger i-  var' <- compileVar var-  stm $ Assign var' $ simpleCall "np.int32" [src]--entryPointOutput :: Imp.ExternalValue -> CompilerM op s PyExp-entryPointOutput (Imp.OpaqueValue desc vs) =-  simpleCall "opaque" . (String (pretty desc):) <$>-  mapM (entryPointOutput . Imp.TransparentValue) vs-entryPointOutput (Imp.TransparentValue (Imp.ScalarValue bt ept name)) = do-  name' <- compileVar name-  return $ simpleCall tf [name']-  where tf = compilePrimToExtNp bt ept-entryPointOutput (Imp.TransparentValue (Imp.ArrayValue mem (Imp.Space sid) bt ept dims)) = do-  pack_output <- asks envEntryOutput-  pack_output mem sid bt ept dims-entryPointOutput (Imp.TransparentValue (Imp.ArrayValue mem _ bt ept dims)) = do-  mem' <- compileVar mem-  let cast = Cast mem' (compilePrimTypeExt bt ept)-  return $ simpleCall "createArray" [cast, Tuple $ map compileDim dims]--badInput :: Int -> PyExp -> String -> PyStmt-badInput i e t =-  Raise $ simpleCall "TypeError"-  [Call (Field (String err_msg) "format")-   [Arg (String t), Arg $ simpleCall "type" [e], Arg e]]-  where err_msg = unlines [ "Argument #" ++ show i ++ " has invalid value"-                          , "Futhark type: {}"-                          , "Argument has Python type {} and value: {}"]--badInputType :: Int -> PyExp -> String -> PyExp -> PyExp -> PyStmt-badInputType i e t de dg =-  Raise $ simpleCall "TypeError"-  [Call (Field (String err_msg) "format")-   [Arg (String t), Arg $ simpleCall "type" [e], Arg e, Arg de, Arg dg]]-  where err_msg = unlines [ "Argument #" ++ show i ++ " has invalid value"-                          , "Futhark type: {}"-                          , "Argument has Python type {} and value: {}"-                          , "Expected array with elements of dtype: {}"-                          , "The array given has elements of dtype: {}"]--badInputDim :: Int -> PyExp -> String -> Int -> PyStmt-badInputDim i e typ dimf =-  Raise $ simpleCall "TypeError"-  [Call (Field (String err_msg) "format")-   [Arg eft, Arg aft]]-  where eft = String (concat (replicate dimf "[]") ++ typ)-        aft = BinOp "+" (BinOp "*" (String "[]") (Field e "ndim")) (String typ)-        err_msg = unlines [ "Argument #" ++ show i ++ " has invalid value"-                          , "Dimensionality mismatch"-                          , "Expected Futhark type: {}"-                          , "Bad Python value passed"-                          , "Actual Futhark type: {}"]--entryPointInput :: (Int, Imp.ExternalValue, PyExp) -> CompilerM op s ()-entryPointInput (i, Imp.OpaqueValue desc vs, e) = do-  let type_is_ok = BinOp "and" (simpleCall "isinstance" [e, Var "opaque"])-                               (BinOp "==" (Field e "desc") (String desc))-  stm $ If (UnOp "not" type_is_ok) [badInput i e desc] []-  mapM_ entryPointInput $ zip3 (repeat i) (map Imp.TransparentValue vs) $-    map (Index (Field e "data") . IdxExp . Integer) [0..]--entryPointInput (i, Imp.TransparentValue (Imp.ScalarValue bt s name), e) = do-  vname' <- compileVar name-  let -- HACK: A Numpy int64 will signal an OverflowError if we pass-      -- it a number bigger than 2**63.  This does not happen if we-      -- pass e.g. int8 a number bigger than 2**7.  As a workaround,-      -- we first go through the corresponding ctypes type, which does-      -- not have this problem.-      ctobject = compilePrimType bt-      ctcall = simpleCall ctobject [e]-      npobject = compilePrimToNp bt-      npcall = simpleCall npobject [ctcall]-  stm $ Try [Assign vname' npcall]-    [Catch (Tuple [Var "TypeError", Var "AssertionError"])-     [badInput i e $ prettySigned (s==Imp.TypeUnsigned) bt]]--entryPointInput (i, Imp.TransparentValue (Imp.ArrayValue mem (Imp.Space sid) bt ept dims), e) = do-  unpack_input <- asks envEntryInput-  mem' <- compileVar mem-  unpack <- collect $ unpack_input mem' sid bt ept dims e-  stm $ Try unpack-    [Catch (Tuple [Var "TypeError", Var "AssertionError"])-     [badInput i e $ concat (replicate (length dims) "[]") ++-     prettySigned (ept==Imp.TypeUnsigned) bt]]--entryPointInput (i, Imp.TransparentValue (Imp.ArrayValue mem _ t s dims), e) = do-  let type_is_wrong = UnOp "not" $ BinOp "in" (simpleCall "type" [e]) $ List [Var "np.ndarray"]-  let dtype_is_wrong = UnOp "not" $ BinOp "==" (Field e "dtype") $ Var $ compilePrimToExtNp t s-  let dim_is_wrong = UnOp "not" $ BinOp "==" (Field e "ndim") $ Integer $ toInteger $ length dims-  stm $ If type_is_wrong-    [badInput i e $ concat (replicate (length dims) "[]") ++-     prettySigned (s==Imp.TypeUnsigned) t]-    []-  stm $ If dtype_is_wrong-    [badInputType i e-     (concat (replicate (length dims) "[]") ++ prettySigned (s==Imp.TypeUnsigned) t)-     (simpleCall "np.dtype" [Var (compilePrimToExtNp t s)])-     (Field e "dtype")]-    []-  stm $ If dim_is_wrong-    [badInputDim i e (prettySigned (s==Imp.TypeUnsigned) t ) (length dims)]-    []--  zipWithM_ (unpackDim e) dims [0..]-  dest <- compileVar mem-  let unwrap_call = simpleCall "unwrapArray" [e]--  stm $ Assign dest unwrap_call--extValueDescName :: Imp.ExternalValue -> String-extValueDescName (Imp.TransparentValue v) = extName $ valueDescName v-extValueDescName (Imp.OpaqueValue desc []) = extName $ zEncodeString desc-extValueDescName (Imp.OpaqueValue desc (v:_)) =-  extName $ zEncodeString desc ++ "_" ++ pretty (baseTag (valueDescVName v))--extName :: String -> String-extName = (++"_ext")--valueDescName :: Imp.ValueDesc -> String-valueDescName = compileName . valueDescVName--valueDescVName :: Imp.ValueDesc -> VName-valueDescVName (Imp.ScalarValue _ _ vname) = vname-valueDescVName (Imp.ArrayValue vname _ _ _ _) = vname---- Key into the FUTHARK_PRIMTYPES dict.-readTypeEnum :: PrimType -> Imp.Signedness -> String-readTypeEnum (IntType Int8)  Imp.TypeUnsigned = "u8"-readTypeEnum (IntType Int16) Imp.TypeUnsigned = "u16"-readTypeEnum (IntType Int32) Imp.TypeUnsigned = "u32"-readTypeEnum (IntType Int64) Imp.TypeUnsigned = "u64"-readTypeEnum (IntType Int8)  Imp.TypeDirect   = "i8"-readTypeEnum (IntType Int16) Imp.TypeDirect   = "i16"-readTypeEnum (IntType Int32) Imp.TypeDirect   = "i32"-readTypeEnum (IntType Int64) Imp.TypeDirect   = "i64"-readTypeEnum (FloatType Float32) _ = "f32"-readTypeEnum (FloatType Float64) _ = "f64"-readTypeEnum Imp.Bool _ = "bool"-readTypeEnum Cert _ = error "readTypeEnum: cert"--readInput :: Imp.ExternalValue -> PyStmt-readInput (Imp.OpaqueValue desc _) =-  Raise $ simpleCall "Exception"-  [String $ "Cannot read argument of type " ++ desc ++ "."]--readInput decl@(Imp.TransparentValue (Imp.ScalarValue bt ept _)) =-  let type_name = readTypeEnum bt ept-  in Assign (Var $ extValueDescName decl) $ simpleCall "read_value" [String type_name]--readInput decl@(Imp.TransparentValue (Imp.ArrayValue _ _ bt ept dims)) =-  let type_name = readTypeEnum bt ept-  in Assign (Var $ extValueDescName decl) $ simpleCall "read_value"-     [String $ concat (replicate (length dims) "[]") ++ type_name]--printValue :: [(Imp.ExternalValue, PyExp)] -> CompilerM op s [PyStmt]-printValue = fmap concat . mapM (uncurry printValue')-  -- We copy non-host arrays to the host before printing.  This is-  -- done in a hacky way - we assume the value has a .get()-method-  -- that returns an equivalent Numpy array.  This works for PyOpenCL,-  -- but we will probably need yet another plugin mechanism here in-  -- the future.-  where printValue' (Imp.OpaqueValue desc _) _ =-          return [Exp $ simpleCall "sys.stdout.write"-                  [String $ "#<opaque " ++ desc ++ ">"]]-        printValue' (Imp.TransparentValue (Imp.ArrayValue mem (Space _) bt ept shape)) e =-          printValue' (Imp.TransparentValue (Imp.ArrayValue mem DefaultSpace bt ept shape)) $-          simpleCall (pretty e ++ ".get") []-        printValue' (Imp.TransparentValue _) e =-          return [Exp $ Call (Var "write_value")-                   [Arg e,-                    ArgKeyword "binary" (Var "binary_output")],-                  Exp $ simpleCall "sys.stdout.write" [String "\n"]]--prepareEntry :: (Name, Imp.Function op) -> CompilerM op s-                (String, [String], [PyStmt], [PyStmt], [PyStmt], [PyStmt],-                 [(Imp.ExternalValue, PyExp)], [PyStmt])-prepareEntry (fname, Imp.Function _ outputs inputs _ results args) = do-  let output_paramNames = map (compileName . Imp.paramName) outputs-      funTuple = tupleOrSingle $ fmap Var output_paramNames--  (argexps_mem_copies, prepare_run) <- collect' $ forM inputs $ \case-    Imp.MemParam name space -> do-      -- A program might write to its input parameters, so create a new memory-      -- block and copy the source there.  This way the program can be run more-      -- than once.-      name' <- newVName $ baseString name <> "_copy"-      copy <- asks envCopy-      allocate <- asks envAllocate-      let size = Var (extName (compileName name) ++ ".nbytes") -- FIXME-          dest = name'-          src = name-          offset = Integer 0-      case space of-        Space sid ->-          allocate (Var (compileName name')) size sid-        _ ->-          stm $ Assign (Var (compileName name'))-                       (simpleCall "allocateMem" [size]) -- FIXME-      dest' <- compileVar dest-      src' <- compileVar src-      copy dest' offset space src' offset space size (IntType Int32) -- FIXME-      return $ Just $ compileName name'-    _ -> return Nothing--  prepareIn <- collect $ mapM_ entryPointInput $ zip3 [0..] args $-               map (Var . extValueDescName) args-  (res, prepareOut) <- collect' $ mapM entryPointOutput results--  let argexps_lib = map (compileName . Imp.paramName) inputs-      argexps_bin = zipWith fromMaybe argexps_lib argexps_mem_copies-      fname' = "self." ++ futharkFun (nameToString fname)-      call_lib = [Assign funTuple $ simpleCall fname' (fmap Var argexps_lib)]-      call_bin = [Assign funTuple $ simpleCall fname' (fmap Var argexps_bin)]--  return (nameToString fname, map extValueDescName args,-          prepareIn, call_lib, call_bin, prepareOut,-          zip results res, prepare_run)--copyMemoryDefaultSpace :: PyExp -> PyExp -> PyExp -> PyExp -> PyExp ->-                          CompilerM op s ()-copyMemoryDefaultSpace destmem destidx srcmem srcidx nbytes = do-  let offset_call1 = simpleCall "addressOffset"-                     [destmem, destidx, Var "ct.c_byte"]-  let offset_call2 = simpleCall "addressOffset"-                     [srcmem, srcidx, Var "ct.c_byte"]-  stm $ Exp $ simpleCall "ct.memmove" [offset_call1, offset_call2, nbytes]--compileEntryFun :: [PyStmt] -> (Name, Imp.Function op)-                -> CompilerM op s (PyFunDef, (PyExp, PyExp))-compileEntryFun sync entry = do-  (fname', params, prepareIn, body_lib, _, prepareOut, res, _) <- prepareEntry entry-  let ret = Return $ tupleOrSingle $ map snd res-      (pts, rts) = entryTypes $ snd entry-  return (Def fname' ("self" : params) $-           prepareIn ++ body_lib ++ prepareOut ++ sync ++ [ret],-          (String fname', Tuple [List (map String pts), List (map String rts)]))--entryTypes :: Imp.Function op -> ([String], [String])-entryTypes func = (map desc $ Imp.functionArgs func,-                   map desc $ Imp.functionResult func)-  where desc (Imp.OpaqueValue d _) = d-        desc (Imp.TransparentValue (Imp.ScalarValue pt s _)) = readTypeEnum pt s-        desc (Imp.TransparentValue (Imp.ArrayValue _ _ pt s dims)) =-          concat (replicate (length dims) "[]") ++ readTypeEnum pt s--callEntryFun :: [PyStmt] -> (Name, Imp.Function op)-             -> CompilerM op s (PyFunDef, String, PyExp)-callEntryFun pre_timing entry@(fname, Imp.Function _ _ _ _ _ decl_args) = do-  (_, _, prepare_in, _, body_bin, _, res, prepare_run) <- prepareEntry entry--  let str_input = map readInput decl_args-      end_of_input = [Exp $ simpleCall "end_of_input" [String $ pretty fname]]--      exitcall = [Exp $ simpleCall "sys.exit" [Field (String "Assertion.{} failed") "format(e)"]]-      except' = Catch (Var "AssertionError") exitcall-      do_run = body_bin ++ pre_timing-      (do_run_with_timing, close_runtime_file) = addTiming do_run--      -- We ignore overflow errors and the like for executable entry-      -- points.  These are (somewhat) well-defined in Futhark.-      ignore s = ArgKeyword s $ String "ignore"-      errstate = Call (Var "np.errstate") $ map ignore ["divide", "over", "under", "invalid"]--      do_warmup_run =-        If (Var "do_warmup_run") (prepare_run ++ do_run) []--      do_num_runs =-        For "i" (simpleCall "range" [simpleCall "int" [Var "num_runs"]])-        (prepare_run ++ do_run_with_timing)--  str_output <- printValue res--  let fname' = "entry_" ++ nameToString fname--  return (Def fname' [] $-           str_input ++ end_of_input ++ prepare_in ++-           [Try [With errstate [do_warmup_run, do_num_runs]] [except']] ++-           [close_runtime_file] ++-           str_output,--          nameToString fname,--          Var fname')--addTiming :: [PyStmt] -> ([PyStmt], PyStmt)-addTiming statements =-  ([ Assign (Var "time_start") $ simpleCall "time.time" [] ] ++-   statements ++-   [ Assign (Var "time_end") $ simpleCall "time.time" []-   , If (Var "runtime_file") print_runtime [] ],--   If (Var "runtime_file") [Exp $ simpleCall "runtime_file.close" []] [])-  where print_runtime =-          [Exp $ simpleCall "runtime_file.write"-           [simpleCall "str"-            [BinOp "-"-             (toMicroseconds (Var "time_end"))-             (toMicroseconds (Var "time_start"))]],-           Exp $ simpleCall "runtime_file.write" [String "\n"],-           Exp $ simpleCall "runtime_file.flush" []]-        toMicroseconds x =-          simpleCall "int" [BinOp "*" x $ Integer 1000000]--compileUnOp :: Imp.UnOp -> String-compileUnOp op =-  case op of-    Not -> "not"-    Complement{} -> "~"-    Abs{} -> "abs"-    FAbs{} -> "abs"-    SSignum{} -> "ssignum"-    USignum{} -> "usignum"--compileBinOpLike :: Monad m =>-                    (v -> m PyExp)-                 -> Imp.PrimExp v -> Imp.PrimExp v-                 -> m (PyExp, PyExp, String -> m PyExp)-compileBinOpLike f x y = do-  x' <- compilePrimExp f x-  y' <- compilePrimExp f y-  let simple s = return $ BinOp s x' y'-  return (x', y', simple)---- | The ctypes type corresponding to a 'PrimType'.-compilePrimType :: PrimType -> String-compilePrimType t =-  case t of-    IntType Int8 -> "ct.c_int8"-    IntType Int16 -> "ct.c_int16"-    IntType Int32 -> "ct.c_int32"-    IntType Int64 -> "ct.c_int64"-    FloatType Float32 -> "ct.c_float"-    FloatType Float64 -> "ct.c_double"-    Imp.Bool -> "ct.c_bool"-    Cert -> "ct.c_bool"---- | The ctypes type corresponding to a 'PrimType', taking sign into account.-compilePrimTypeExt :: PrimType -> Imp.Signedness -> String-compilePrimTypeExt t ept =-  case (t, ept) of-    (IntType Int8, Imp.TypeUnsigned) -> "ct.c_uint8"-    (IntType Int16, Imp.TypeUnsigned) -> "ct.c_uint16"-    (IntType Int32, Imp.TypeUnsigned) -> "ct.c_uint32"-    (IntType Int64, Imp.TypeUnsigned) -> "ct.c_uint64"-    (IntType Int8, _) -> "ct.c_int8"-    (IntType Int16, _) -> "ct.c_int16"-    (IntType Int32, _) -> "ct.c_int32"-    (IntType Int64, _) -> "ct.c_int64"-    (FloatType Float32, _) -> "ct.c_float"-    (FloatType Float64, _) -> "ct.c_double"-    (Imp.Bool, _) -> "ct.c_bool"-    (Cert, _) -> "ct.c_byte"---- | The Numpy type corresponding to a 'PrimType'.-compilePrimToNp :: Imp.PrimType -> String-compilePrimToNp bt =-  case bt of-    IntType Int8 -> "np.int8"-    IntType Int16 -> "np.int16"-    IntType Int32 -> "np.int32"-    IntType Int64 -> "np.int64"-    FloatType Float32 -> "np.float32"-    FloatType Float64 -> "np.float64"-    Imp.Bool -> "np.byte"-    Cert -> "np.byte"---- | The Numpy type corresponding to a 'PrimType', taking sign into account.-compilePrimToExtNp :: Imp.PrimType -> Imp.Signedness -> String-compilePrimToExtNp bt ept =-  case (bt,ept) of-    (IntType Int8, Imp.TypeUnsigned) -> "np.uint8"-    (IntType Int16, Imp.TypeUnsigned) -> "np.uint16"-    (IntType Int32, Imp.TypeUnsigned) -> "np.uint32"-    (IntType Int64, Imp.TypeUnsigned) -> "np.uint64"-    (IntType Int8, _) -> "np.int8"-    (IntType Int16, _) -> "np.int16"-    (IntType Int32, _) -> "np.int32"-    (IntType Int64, _) -> "np.int64"-    (FloatType Float32, _) -> "np.float32"-    (FloatType Float64, _) -> "np.float64"-    (Imp.Bool, _) -> "np.bool_"-    (Cert, _) -> "np.byte"--compilePrimValue :: Imp.PrimValue -> PyExp-compilePrimValue (IntValue (Int8Value v)) =-  simpleCall "np.int8" [Integer $ toInteger v]-compilePrimValue (IntValue (Int16Value v)) =-  simpleCall "np.int16" [Integer $ toInteger v]-compilePrimValue (IntValue (Int32Value v)) =-  simpleCall "np.int32" [Integer $ toInteger v]-compilePrimValue (IntValue (Int64Value v)) =-  simpleCall "np.int64" [Integer $ toInteger v]-compilePrimValue (FloatValue (Float32Value v))-  | isInfinite v =-      if v > 0 then Var "np.inf" else Var "-np.inf"-  | isNaN v =-      Var "np.nan"-  | otherwise = simpleCall "np.float32" [Float $ fromRational $ toRational v]-compilePrimValue (FloatValue (Float64Value v))-  | isInfinite v =-      if v > 0 then Var "np.inf" else Var "-np.inf"-  | isNaN v =-      Var "np.nan"-  | otherwise = simpleCall "np.float64" [Float $ fromRational $ toRational v]-compilePrimValue (BoolValue v) = Bool v-compilePrimValue Checked = Var "True"--compileVar :: VName -> CompilerM op s PyExp-compileVar v =-  asks $ fromMaybe (Var $ compileName v) . M.lookup v . envVarExp---- | Tell me how to compile a @v@, and I'll Compile any @PrimExp v@ for you.-compilePrimExp :: Monad m => (v -> m PyExp) -> Imp.PrimExp v -> m PyExp--compilePrimExp _ (Imp.ValueExp v) = return $ compilePrimValue v--compilePrimExp f (Imp.LeafExp v _) = f v--compilePrimExp f (Imp.BinOpExp op x y) = do-  (x', y', simple) <- compileBinOpLike f x y-  case op of-    Add{} -> simple "+"-    Sub{} -> simple "-"-    Mul{} -> simple "*"-    FAdd{} -> simple "+"-    FSub{} -> simple "-"-    FMul{} -> simple "*"-    FDiv{} -> simple "/"-    FMod{} -> simple "%"-    Xor{} -> simple "^"-    And{} -> simple "&"-    Or{} -> simple "|"-    Shl{} -> simple "<<"-    LogAnd{} -> simple "and"-    LogOr{} -> simple "or"-    _ -> return $ simpleCall (pretty op) [x', y']--compilePrimExp f (Imp.ConvOpExp conv x) = do-  x' <- compilePrimExp f x-  return $ simpleCall (pretty conv) [x']--compilePrimExp f (Imp.CmpOpExp cmp x y) = do-  (x', y', simple) <- compileBinOpLike f x y-  case cmp of-    CmpEq{} -> simple "=="-    FCmpLt{} -> simple "<"-    FCmpLe{} -> simple "<="-    CmpLlt -> simple "<"-    CmpLle -> simple "<="-    _ -> return $ simpleCall (pretty cmp) [x', y']--compilePrimExp f (Imp.UnOpExp op exp1) =-  UnOp (compileUnOp op) <$> compilePrimExp f exp1--compilePrimExp f (Imp.FunExp h args _) =-  simpleCall (futharkFun (pretty h)) <$> mapM (compilePrimExp f) args--compileExp :: Imp.Exp -> CompilerM op s PyExp-compileExp = compilePrimExp compileLeaf-  where compileLeaf (Imp.ScalarVar vname) =-          compileVar vname--        compileLeaf (Imp.SizeOf t) =-          return $ simpleCall (compilePrimToNp $ IntType Int32) [Integer $ primByteSize t]--        compileLeaf (Imp.Index src (Imp.Count iexp) restype (Imp.Space space) _) =-          join $ asks envReadScalar-          <*> compileVar src <*> compileExp iexp-          <*> pure restype <*> pure space--        compileLeaf (Imp.Index src (Imp.Count iexp) bt _ _) = do-          iexp' <- compileExp iexp-          let bt' = compilePrimType bt-              nptype = compilePrimToNp bt-          src' <- compileVar src-          return $ simpleCall "indexArray" [src', iexp', Var bt', Var nptype]--compileCode :: Imp.Code op -> CompilerM op s ()--compileCode Imp.DebugPrint{} =-  return ()--compileCode (Imp.Op op) =-  join $ asks envOpCompiler <*> pure op--compileCode (Imp.If cond tb fb) = do-  cond' <- compileExp cond-  tb' <- collect $ compileCode tb-  fb' <- collect $ compileCode fb-  stm $ If cond' tb' fb'--compileCode (c1 Imp.:>>: c2) = do-  compileCode c1-  compileCode c2--compileCode (Imp.While cond body) = do-  cond' <- compileExp cond-  body' <- collect $ compileCode body-  stm $ While cond' body'--compileCode (Imp.For i it bound body) = do-  bound' <- compileExp bound-  let i' = compileName i-  body' <- collect $ compileCode body-  counter <- pretty <$> newVName "counter"-  one <- pretty <$> newVName "one"-  stm $ Assign (Var i') $ simpleCall (compilePrimToNp (IntType it)) [Integer 0]-  stm $ Assign (Var one) $ simpleCall (compilePrimToNp (IntType it)) [Integer 1]-  stm $ For counter (simpleCall "range" [bound']) $-    body' ++ [AssignOp "+" (Var i') (Var one)]--compileCode (Imp.SetScalar name exp1) =-  stm =<< Assign <$> compileVar name <*> compileExp exp1--compileCode Imp.DeclareMem{} = return ()-compileCode (Imp.DeclareScalar v _ Cert) = do-  v' <- compileVar v-  stm $ Assign v' $ Var "True"-compileCode Imp.DeclareScalar{} = return ()--compileCode (Imp.DeclareArray name (Space space) t vs) =-  join $ asks envStaticArray <*>-  pure name <*> pure space <*> pure t <*> pure vs--compileCode (Imp.DeclareArray name _ t vs) = do-  let arr_name = compileName name <> "_arr"-  -- It is important to store the Numpy array in a temporary variable-  -- to prevent it from going "out-of-scope" before calling-  -- unwrapArray (which internally uses the .ctype method); see-  -- https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.ctypes.html-  atInit $ Assign (Field (Var "self") arr_name) $ case vs of-    Imp.ArrayValues vs' ->-      Call (Var "np.array")-      [Arg $ List $ map compilePrimValue vs',-       ArgKeyword "dtype" $ Var $ compilePrimToNp t]-    Imp.ArrayZeros n ->-      Call (Var "np.zeros")-      [Arg $ Integer $ fromIntegral n,-       ArgKeyword "dtype" $ Var $ compilePrimToNp t]-  atInit $-    Assign (Field (Var "self") (compileName name)) $-    simpleCall "unwrapArray" [Field (Var "self") arr_name]-  name' <- compileVar name-  stm $ Assign name' $ Field (Var "self") (compileName name)--compileCode (Imp.Comment s code) = do-  code' <- collect $ compileCode code-  stm $ Comment s code'--compileCode (Imp.Assert e (Imp.ErrorMsg parts) (loc,locs)) = do-  e' <- compileExp e-  let onPart (Imp.ErrorString s) = return ("%s", String s)-      onPart (Imp.ErrorInt32 x) = ("%d",) <$> compileExp x-  (formatstrs, formatargs) <- unzip <$> mapM onPart parts-  stm $ Assert e' (BinOp "%"-                   (String $ "Error: " ++ concat formatstrs ++ "\n\nBacktrace:\n" ++ stacktrace)-                   (Tuple formatargs))-  where stacktrace = prettyStacktrace 0 $ map locStr $ loc:locs--compileCode (Imp.Call dests fname args) = do-  args' <- mapM compileArg args-  dests' <- tupleOrSingle <$> mapM compileVar dests-  let fname'-        | isBuiltInFunction fname = futharkFun (pretty  fname)-        | otherwise               = "self." ++ futharkFun (pretty  fname)-      call' = simpleCall fname' args'-  -- If the function returns nothing (is called only for side-  -- effects), take care not to assign to an empty tuple.-  stm $ if null dests-        then Exp call'-        else Assign dests' call'-  where compileArg (Imp.MemArg m) = compileVar m-        compileArg (Imp.ExpArg e) = compileExp e--compileCode (Imp.SetMem dest src _) =-  stm =<< Assign <$> compileVar dest <*> compileVar src--compileCode (Imp.Allocate name (Imp.Count e) (Imp.Space space)) =-  join $ asks envAllocate-    <*> compileVar name-    <*> compileExp e-    <*> pure space--compileCode (Imp.Allocate name (Imp.Count e) _) = do-  e' <- compileExp e-  let allocate' = simpleCall "allocateMem" [e']-  stm =<< Assign <$> compileVar name <*> pure allocate'--compileCode (Imp.Free name _) =-  stm =<< Assign <$> compileVar name <*> pure None--compileCode (Imp.Copy dest (Imp.Count destoffset) DefaultSpace src (Imp.Count srcoffset) DefaultSpace (Imp.Count size)) = do-  destoffset' <- compileExp destoffset-  srcoffset' <- compileExp srcoffset-  dest' <- compileVar dest-  src' <- compileVar src-  size' <- compileExp size-  let offset_call1 = simpleCall "addressOffset" [dest', destoffset', Var "ct.c_byte"]-  let offset_call2 = simpleCall "addressOffset" [src', srcoffset', Var "ct.c_byte"]-  stm $ Exp $ simpleCall "ct.memmove" [offset_call1, offset_call2, size']--compileCode (Imp.Copy dest (Imp.Count destoffset) destspace src (Imp.Count srcoffset) srcspace (Imp.Count size)) = do-  copy <- asks envCopy-  join $ copy-    <$> compileVar dest <*> compileExp destoffset <*> pure destspace-    <*> compileVar src <*> compileExp srcoffset <*> pure srcspace-    <*> compileExp size <*> pure (IntType Int32) -- FIXME--compileCode (Imp.Write dest (Imp.Count idx) elemtype (Imp.Space space) _ elemexp) =-  join $ asks envWriteScalar-    <*> compileVar dest-    <*> compileExp idx-    <*> pure elemtype-    <*> pure space-    <*> compileExp elemexp--compileCode (Imp.Write dest (Imp.Count idx) elemtype _ _ elemexp) = do-  idx' <- compileExp idx-  elemexp' <- compileExp elemexp-  dest' <- compileVar dest-  let elemtype' = compilePrimType elemtype-      ctype = simpleCall elemtype' [elemexp']-  stm $ Exp $ simpleCall "writeScalarArray" [dest', idx', ctype]-+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}++-- | A generic Python code generator which is polymorphic in the type+-- of the operations.  Concretely, we use this to handle both+-- sequential and PyOpenCL Python code.+module Futhark.CodeGen.Backends.GenericPython+  ( compileProg,+    Constructor (..),+    emptyConstructor,+    compileName,+    compileVar,+    compileDim,+    compileExp,+    compilePrimExp,+    compileCode,+    compilePrimValue,+    compilePrimType,+    compilePrimTypeExt,+    compilePrimToNp,+    compilePrimToExtNp,+    Operations (..),+    defaultOperations,+    unpackDim,+    CompilerM (..),+    OpCompiler,+    WriteScalar,+    ReadScalar,+    Allocate,+    Copy,+    StaticArray,+    EntryOutput,+    EntryInput,+    CompilerEnv (..),+    CompilerState (..),+    stm,+    atInit,+    collect',+    collect,+    simpleCall,+    copyMemoryDefaultSpace,+  )+where++import Control.Monad.Identity+import Control.Monad.RWS+import qualified Data.Map as M+import Data.Maybe+import Futhark.CodeGen.Backends.GenericPython.AST+import Futhark.CodeGen.Backends.GenericPython.Definitions+import Futhark.CodeGen.Backends.GenericPython.Options+import qualified Futhark.CodeGen.ImpCode as Imp+import Futhark.IR.Primitive hiding (Bool)+import Futhark.IR.Prop (isBuiltInFunction)+import Futhark.IR.Syntax (Space (..))+import Futhark.MonadFreshNames+import Futhark.Util (zEncodeString)++-- | A substitute expression compiler, tried before the main+-- compilation function.+type OpCompiler op s = op -> CompilerM op s ()++-- | Write a scalar to the given memory block with the given index and+-- in the given memory space.+type WriteScalar op s =+  PyExp ->+  PyExp ->+  PrimType ->+  Imp.SpaceId ->+  PyExp ->+  CompilerM op s ()++-- | Read a scalar from the given memory block with the given index and+-- in the given memory space.+type ReadScalar op s =+  PyExp ->+  PyExp ->+  PrimType ->+  Imp.SpaceId ->+  CompilerM op s PyExp++-- | Allocate a memory block of the given size in the given memory+-- space, saving a reference in the given variable name.+type Allocate op s =+  PyExp ->+  PyExp ->+  Imp.SpaceId ->+  CompilerM op s ()++-- | Copy from one memory block to another.+type Copy op s =+  PyExp ->+  PyExp ->+  Imp.Space ->+  PyExp ->+  PyExp ->+  Imp.Space ->+  PyExp ->+  PrimType ->+  CompilerM op s ()++-- | Create a static array of values - initialised at load time.+type StaticArray op s = VName -> Imp.SpaceId -> PrimType -> Imp.ArrayContents -> CompilerM op s ()++-- | Construct the Python array being returned from an entry point.+type EntryOutput op s =+  VName ->+  Imp.SpaceId ->+  PrimType ->+  Imp.Signedness ->+  [Imp.DimSize] ->+  CompilerM op s PyExp++-- | Unpack the array being passed to an entry point.+type EntryInput op s =+  PyExp ->+  Imp.SpaceId ->+  PrimType ->+  Imp.Signedness ->+  [Imp.DimSize] ->+  PyExp ->+  CompilerM op s ()++data Operations op s = Operations+  { opsWriteScalar :: WriteScalar op s,+    opsReadScalar :: ReadScalar op s,+    opsAllocate :: Allocate op s,+    opsCopy :: Copy op s,+    opsStaticArray :: StaticArray op s,+    opsCompiler :: OpCompiler op s,+    opsEntryOutput :: EntryOutput op s,+    opsEntryInput :: EntryInput op s+  }++-- | A set of operations that fail for every operation involving+-- non-default memory spaces.  Uses plain pointers and @malloc@ for+-- memory management.+defaultOperations :: Operations op s+defaultOperations =+  Operations+    { opsWriteScalar = defWriteScalar,+      opsReadScalar = defReadScalar,+      opsAllocate = defAllocate,+      opsCopy = defCopy,+      opsStaticArray = defStaticArray,+      opsCompiler = defCompiler,+      opsEntryOutput = defEntryOutput,+      opsEntryInput = defEntryInput+    }+  where+    defWriteScalar _ _ _ _ _ =+      error "Cannot write to non-default memory space because I am dumb"+    defReadScalar _ _ _ _ =+      error "Cannot read from non-default memory space"+    defAllocate _ _ _ =+      error "Cannot allocate in non-default memory space"+    defCopy _ _ _ _ _ _ _ _ =+      error "Cannot copy to or from non-default memory space"+    defStaticArray _ _ _ _ =+      error "Cannot create static array in non-default memory space"+    defCompiler _ =+      error "The default compiler cannot compile extended operations"+    defEntryOutput _ _ _ _ =+      error "Cannot return array not in default memory space"+    defEntryInput _ _ _ _ =+      error "Cannot accept array not in default memory space"++data CompilerEnv op s = CompilerEnv+  { envOperations :: Operations op s,+    envVarExp :: M.Map VName PyExp+  }++envOpCompiler :: CompilerEnv op s -> OpCompiler op s+envOpCompiler = opsCompiler . envOperations++envReadScalar :: CompilerEnv op s -> ReadScalar op s+envReadScalar = opsReadScalar . envOperations++envWriteScalar :: CompilerEnv op s -> WriteScalar op s+envWriteScalar = opsWriteScalar . envOperations++envAllocate :: CompilerEnv op s -> Allocate op s+envAllocate = opsAllocate . envOperations++envCopy :: CompilerEnv op s -> Copy op s+envCopy = opsCopy . envOperations++envStaticArray :: CompilerEnv op s -> StaticArray op s+envStaticArray = opsStaticArray . envOperations++envEntryOutput :: CompilerEnv op s -> EntryOutput op s+envEntryOutput = opsEntryOutput . envOperations++envEntryInput :: CompilerEnv op s -> EntryInput op s+envEntryInput = opsEntryInput . envOperations++newCompilerEnv :: Operations op s -> CompilerEnv op s+newCompilerEnv ops =+  CompilerEnv+    { envOperations = ops,+      envVarExp = mempty+    }++data CompilerState s = CompilerState+  { compNameSrc :: VNameSource,+    compInit :: [PyStmt],+    compUserState :: s+  }++newCompilerState :: VNameSource -> s -> CompilerState s+newCompilerState src s =+  CompilerState+    { compNameSrc = src,+      compInit = [],+      compUserState = s+    }++newtype CompilerM op s a = CompilerM (RWS (CompilerEnv op s) [PyStmt] (CompilerState s) a)+  deriving+    ( Functor,+      Applicative,+      Monad,+      MonadState (CompilerState s),+      MonadReader (CompilerEnv op s),+      MonadWriter [PyStmt]+    )++instance MonadFreshNames (CompilerM op s) where+  getNameSource = gets compNameSrc+  putNameSource src = modify $ \s -> s {compNameSrc = src}++collect :: CompilerM op s () -> CompilerM op s [PyStmt]+collect m = pass $ do+  ((), w) <- listen m+  return (w, const mempty)++collect' :: CompilerM op s a -> CompilerM op s (a, [PyStmt])+collect' m = pass $ do+  (x, w) <- listen m+  return ((x, w), const mempty)++atInit :: PyStmt -> CompilerM op s ()+atInit x = modify $ \s ->+  s {compInit = compInit s ++ [x]}++stm :: PyStmt -> CompilerM op s ()+stm x = tell [x]++futharkFun :: String -> String+futharkFun s = "futhark_" ++ zEncodeString s++compileOutput :: [Imp.Param] -> [PyExp]+compileOutput = map (Var . compileName . Imp.paramName)++runCompilerM ::+  Operations op s ->+  VNameSource ->+  s ->+  CompilerM op s a ->+  a+runCompilerM ops src userstate (CompilerM m) =+  fst $ evalRWS m (newCompilerEnv ops) (newCompilerState src userstate)++standardOptions :: [Option]+standardOptions =+  [ Option+      { optionLongName = "write-runtime-to",+        optionShortName = Just 't',+        optionArgument = RequiredArgument "str",+        optionAction =+          [ If+              (Var "runtime_file")+              [Exp $ simpleCall "runtime_file.close" []]+              [],+            Assign (Var "runtime_file") $+              simpleCall "open" [Var "optarg", String "w"]+          ]+      },+    Option+      { optionLongName = "runs",+        optionShortName = Just 'r',+        optionArgument = RequiredArgument "str",+        optionAction =+          [ Assign (Var "num_runs") $ Var "optarg",+            Assign (Var "do_warmup_run") $ Bool True+          ]+      },+    Option+      { optionLongName = "entry-point",+        optionShortName = Just 'e',+        optionArgument = RequiredArgument "str",+        optionAction =+          [Assign (Var "entry_point") $ Var "optarg"]+      },+    Option+      { optionLongName = "binary-output",+        optionShortName = Just 'b',+        optionArgument = NoArgument,+        optionAction = [Assign (Var "binary_output") $ Bool True]+      },+    Option+      { optionLongName = "tuning",+        optionShortName = Nothing,+        optionArgument = RequiredArgument "open",+        optionAction = [Exp $ simpleCall "read_tuning_file" [Var "sizes", Var "optarg"]]+      }+  ]++-- | The class generated by the code generator must have a+-- constructor, although it can be vacuous.+data Constructor = Constructor [String] [PyStmt]++-- | A constructor that takes no arguments and does nothing.+emptyConstructor :: Constructor+emptyConstructor = Constructor ["self"] [Pass]++constructorToFunDef :: Constructor -> [PyStmt] -> PyFunDef+constructorToFunDef (Constructor params body) at_init =+  Def "__init__" params $ body <> at_init++compileProg ::+  MonadFreshNames m =>+  Maybe String ->+  Constructor ->+  [PyStmt] ->+  [PyStmt] ->+  Operations op s ->+  s ->+  [PyStmt] ->+  [Option] ->+  Imp.Definitions op ->+  m String+compileProg module_name constructor imports defines ops userstate sync options prog = do+  src <- getNameSource+  let prog' = runCompilerM ops src userstate compileProg'+      maybe_shebang =+        case module_name of+          Nothing -> "#!/usr/bin/env python\n"+          Just _ -> ""+  return $+    maybe_shebang+      ++ pretty+        ( PyProg $+            imports+              ++ [ Import "argparse" Nothing,+                   Assign (Var "sizes") $ Dict []+                 ]+              ++ defines+              ++ [Escape pyUtility]+              ++ prog'+        )+  where+    Imp.Definitions consts (Imp.Functions funs) = prog+    compileProg' = withConstantSubsts consts $ do+      compileConstants consts++      definitions <- mapM compileFunc funs+      at_inits <- gets compInit++      let constructor' = constructorToFunDef constructor at_inits++      case module_name of+        Just name -> do+          (entry_points, entry_point_types) <-+            unzip <$> mapM (compileEntryFun sync) (filter (Imp.functionEntry . snd) funs)+          return+            [ ClassDef $+                Class name $+                  Assign (Var "entry_points") (Dict entry_point_types) :+                  map FunDef (constructor' : definitions ++ entry_points)+            ]+        Nothing -> do+          let classinst = Assign (Var "self") $ simpleCall "internal" []+          (entry_point_defs, entry_point_names, entry_points) <-+            unzip3+              <$> mapM+                (callEntryFun sync)+                (filter (Imp.functionEntry . snd) funs)+          return+            ( parse_options+                ++ ClassDef+                  ( Class "internal" $+                      map FunDef $+                        constructor' : definitions+                  ) :+              classinst :+              map FunDef entry_point_defs+                ++ selectEntryPoint entry_point_names entry_points+            )++    parse_options =+      Assign (Var "runtime_file") None :+      Assign (Var "do_warmup_run") (Bool False) :+      Assign (Var "num_runs") (Integer 1) :+      Assign (Var "entry_point") (String "main") :+      Assign (Var "binary_output") (Bool False) :+      generateOptionParser (standardOptions ++ options)++    selectEntryPoint entry_point_names entry_points =+      [ Assign (Var "entry_points") $+          Dict $ zip (map String entry_point_names) entry_points,+        Assign (Var "entry_point_fun") $+          simpleCall "entry_points.get" [Var "entry_point"],+        If+          (BinOp "==" (Var "entry_point_fun") None)+          [ Exp $+              simpleCall+                "sys.exit"+                [ Call+                    ( Field+                        (String "No entry point '{}'.  Select another with --entry point.  Options are:\n{}")+                        "format"+                    )+                    [ Arg $ Var "entry_point",+                      Arg $+                        Call+                          (Field (String "\n") "join")+                          [Arg $ simpleCall "entry_points.keys" []]+                    ]+                ]+          ]+          [Exp $ simpleCall "entry_point_fun" []]+      ]++withConstantSubsts :: Imp.Constants op -> CompilerM op s a -> CompilerM op s a+withConstantSubsts (Imp.Constants ps _) =+  local $ \env -> env {envVarExp = foldMap constExp ps}+  where+    constExp p =+      M.singleton (Imp.paramName p) $+        Index (Var "self.constants") $+          IdxExp $ String $ pretty $ Imp.paramName p++compileConstants :: Imp.Constants op -> CompilerM op s ()+compileConstants (Imp.Constants _ init_consts) = do+  atInit $ Assign (Var "self.constants") $ Dict []+  mapM_ atInit =<< collect (compileCode init_consts)++compileFunc :: (Name, Imp.Function op) -> CompilerM op s PyFunDef+compileFunc (fname, Imp.Function _ outputs inputs body _ _) = do+  body' <- collect $ compileCode body+  let inputs' = map (compileName . Imp.paramName) inputs+  let ret = Return $ tupleOrSingle $ compileOutput outputs+  return $+    Def (futharkFun . nameToString $ fname) ("self" : inputs') $+      body' ++ [ret]++tupleOrSingle :: [PyExp] -> PyExp+tupleOrSingle [e] = e+tupleOrSingle es = Tuple es++-- | A 'Call' where the function is a variable and every argument is a+-- simple 'Arg'.+simpleCall :: String -> [PyExp] -> PyExp+simpleCall fname = Call (Var fname) . map Arg++compileName :: VName -> String+compileName = zEncodeString . pretty++compileDim :: Imp.DimSize -> PyExp+compileDim (Imp.Constant v) = compilePrimValue v+compileDim (Imp.Var v) = Var $ compileName v++unpackDim :: PyExp -> Imp.DimSize -> Int32 -> CompilerM op s ()+unpackDim arr_name (Imp.Constant c) i = do+  let shape_name = Field arr_name "shape"+  let constant_c = compilePrimValue c+  let constant_i = Integer $ toInteger i+  stm $+    Assert (BinOp "==" constant_c (Index shape_name $ IdxExp constant_i)) $+      String "constant dimension wrong"+unpackDim arr_name (Imp.Var var) i = do+  let shape_name = Field arr_name "shape"+      src = Index shape_name $ IdxExp $ Integer $ toInteger i+  var' <- compileVar var+  stm $ Assign var' $ simpleCall "np.int32" [src]++entryPointOutput :: Imp.ExternalValue -> CompilerM op s PyExp+entryPointOutput (Imp.OpaqueValue desc vs) =+  simpleCall "opaque" . (String (pretty desc) :)+    <$> mapM (entryPointOutput . Imp.TransparentValue) vs+entryPointOutput (Imp.TransparentValue (Imp.ScalarValue bt ept name)) = do+  name' <- compileVar name+  return $ simpleCall tf [name']+  where+    tf = compilePrimToExtNp bt ept+entryPointOutput (Imp.TransparentValue (Imp.ArrayValue mem (Imp.Space sid) bt ept dims)) = do+  pack_output <- asks envEntryOutput+  pack_output mem sid bt ept dims+entryPointOutput (Imp.TransparentValue (Imp.ArrayValue mem _ bt ept dims)) = do+  mem' <- compileVar mem+  let cast = Cast mem' (compilePrimTypeExt bt ept)+  return $ simpleCall "createArray" [cast, Tuple $ map compileDim dims]++badInput :: Int -> PyExp -> String -> PyStmt+badInput i e t =+  Raise $+    simpleCall+      "TypeError"+      [ Call+          (Field (String err_msg) "format")+          [Arg (String t), Arg $ simpleCall "type" [e], Arg e]+      ]+  where+    err_msg =+      unlines+        [ "Argument #" ++ show i ++ " has invalid value",+          "Futhark type: {}",+          "Argument has Python type {} and value: {}"+        ]++badInputType :: Int -> PyExp -> String -> PyExp -> PyExp -> PyStmt+badInputType i e t de dg =+  Raise $+    simpleCall+      "TypeError"+      [ Call+          (Field (String err_msg) "format")+          [Arg (String t), Arg $ simpleCall "type" [e], Arg e, Arg de, Arg dg]+      ]+  where+    err_msg =+      unlines+        [ "Argument #" ++ show i ++ " has invalid value",+          "Futhark type: {}",+          "Argument has Python type {} and value: {}",+          "Expected array with elements of dtype: {}",+          "The array given has elements of dtype: {}"+        ]++badInputDim :: Int -> PyExp -> String -> Int -> PyStmt+badInputDim i e typ dimf =+  Raise $+    simpleCall+      "TypeError"+      [ Call+          (Field (String err_msg) "format")+          [Arg eft, Arg aft]+      ]+  where+    eft = String (concat (replicate dimf "[]") ++ typ)+    aft = BinOp "+" (BinOp "*" (String "[]") (Field e "ndim")) (String typ)+    err_msg =+      unlines+        [ "Argument #" ++ show i ++ " has invalid value",+          "Dimensionality mismatch",+          "Expected Futhark type: {}",+          "Bad Python value passed",+          "Actual Futhark type: {}"+        ]++entryPointInput :: (Int, Imp.ExternalValue, PyExp) -> CompilerM op s ()+entryPointInput (i, Imp.OpaqueValue desc vs, e) = do+  let type_is_ok =+        BinOp+          "and"+          (simpleCall "isinstance" [e, Var "opaque"])+          (BinOp "==" (Field e "desc") (String desc))+  stm $ If (UnOp "not" type_is_ok) [badInput i e desc] []+  mapM_ entryPointInput $+    zip3 (repeat i) (map Imp.TransparentValue vs) $+      map (Index (Field e "data") . IdxExp . Integer) [0 ..]+entryPointInput (i, Imp.TransparentValue (Imp.ScalarValue bt s name), e) = do+  vname' <- compileVar name+  let -- HACK: A Numpy int64 will signal an OverflowError if we pass+      -- it a number bigger than 2**63.  This does not happen if we+      -- pass e.g. int8 a number bigger than 2**7.  As a workaround,+      -- we first go through the corresponding ctypes type, which does+      -- not have this problem.+      ctobject = compilePrimType bt+      ctcall = simpleCall ctobject [e]+      npobject = compilePrimToNp bt+      npcall = simpleCall npobject [ctcall]+  stm $+    Try+      [Assign vname' npcall]+      [ Catch+          (Tuple [Var "TypeError", Var "AssertionError"])+          [badInput i e $ prettySigned (s == Imp.TypeUnsigned) bt]+      ]+entryPointInput (i, Imp.TransparentValue (Imp.ArrayValue mem (Imp.Space sid) bt ept dims), e) = do+  unpack_input <- asks envEntryInput+  mem' <- compileVar mem+  unpack <- collect $ unpack_input mem' sid bt ept dims e+  stm $+    Try+      unpack+      [ Catch+          (Tuple [Var "TypeError", Var "AssertionError"])+          [ badInput i e $+              concat (replicate (length dims) "[]")+                ++ prettySigned (ept == Imp.TypeUnsigned) bt+          ]+      ]+entryPointInput (i, Imp.TransparentValue (Imp.ArrayValue mem _ t s dims), e) = do+  let type_is_wrong = UnOp "not" $ BinOp "in" (simpleCall "type" [e]) $ List [Var "np.ndarray"]+  let dtype_is_wrong = UnOp "not" $ BinOp "==" (Field e "dtype") $ Var $ compilePrimToExtNp t s+  let dim_is_wrong = UnOp "not" $ BinOp "==" (Field e "ndim") $ Integer $ toInteger $ length dims+  stm $+    If+      type_is_wrong+      [ badInput i e $+          concat (replicate (length dims) "[]")+            ++ prettySigned (s == Imp.TypeUnsigned) t+      ]+      []+  stm $+    If+      dtype_is_wrong+      [ badInputType+          i+          e+          (concat (replicate (length dims) "[]") ++ prettySigned (s == Imp.TypeUnsigned) t)+          (simpleCall "np.dtype" [Var (compilePrimToExtNp t s)])+          (Field e "dtype")+      ]+      []+  stm $+    If+      dim_is_wrong+      [badInputDim i e (prettySigned (s == Imp.TypeUnsigned) t) (length dims)]+      []++  zipWithM_ (unpackDim e) dims [0 ..]+  dest <- compileVar mem+  let unwrap_call = simpleCall "unwrapArray" [e]++  stm $ Assign dest unwrap_call++extValueDescName :: Imp.ExternalValue -> String+extValueDescName (Imp.TransparentValue v) = extName $ valueDescName v+extValueDescName (Imp.OpaqueValue desc []) = extName $ zEncodeString desc+extValueDescName (Imp.OpaqueValue desc (v : _)) =+  extName $ zEncodeString desc ++ "_" ++ pretty (baseTag (valueDescVName v))++extName :: String -> String+extName = (++ "_ext")++valueDescName :: Imp.ValueDesc -> String+valueDescName = compileName . valueDescVName++valueDescVName :: Imp.ValueDesc -> VName+valueDescVName (Imp.ScalarValue _ _ vname) = vname+valueDescVName (Imp.ArrayValue vname _ _ _ _) = vname++-- Key into the FUTHARK_PRIMTYPES dict.+readTypeEnum :: PrimType -> Imp.Signedness -> String+readTypeEnum (IntType Int8) Imp.TypeUnsigned = "u8"+readTypeEnum (IntType Int16) Imp.TypeUnsigned = "u16"+readTypeEnum (IntType Int32) Imp.TypeUnsigned = "u32"+readTypeEnum (IntType Int64) Imp.TypeUnsigned = "u64"+readTypeEnum (IntType Int8) Imp.TypeDirect = "i8"+readTypeEnum (IntType Int16) Imp.TypeDirect = "i16"+readTypeEnum (IntType Int32) Imp.TypeDirect = "i32"+readTypeEnum (IntType Int64) Imp.TypeDirect = "i64"+readTypeEnum (FloatType Float32) _ = "f32"+readTypeEnum (FloatType Float64) _ = "f64"+readTypeEnum Imp.Bool _ = "bool"+readTypeEnum Cert _ = error "readTypeEnum: cert"++readInput :: Imp.ExternalValue -> PyStmt+readInput (Imp.OpaqueValue desc _) =+  Raise $+    simpleCall+      "Exception"+      [String $ "Cannot read argument of type " ++ desc ++ "."]+readInput decl@(Imp.TransparentValue (Imp.ScalarValue bt ept _)) =+  let type_name = readTypeEnum bt ept+   in Assign (Var $ extValueDescName decl) $ simpleCall "read_value" [String type_name]+readInput decl@(Imp.TransparentValue (Imp.ArrayValue _ _ bt ept dims)) =+  let type_name = readTypeEnum bt ept+   in Assign (Var $ extValueDescName decl) $+        simpleCall+          "read_value"+          [String $ concat (replicate (length dims) "[]") ++ type_name]++printValue :: [(Imp.ExternalValue, PyExp)] -> CompilerM op s [PyStmt]+printValue = fmap concat . mapM (uncurry printValue')+  where+    -- We copy non-host arrays to the host before printing.  This is+    -- done in a hacky way - we assume the value has a .get()-method+    -- that returns an equivalent Numpy array.  This works for PyOpenCL,+    -- but we will probably need yet another plugin mechanism here in+    -- the future.+    printValue' (Imp.OpaqueValue desc _) _ =+      return+        [ Exp $+            simpleCall+              "sys.stdout.write"+              [String $ "#<opaque " ++ desc ++ ">"]+        ]+    printValue' (Imp.TransparentValue (Imp.ArrayValue mem (Space _) bt ept shape)) e =+      printValue' (Imp.TransparentValue (Imp.ArrayValue mem DefaultSpace bt ept shape)) $+        simpleCall (pretty e ++ ".get") []+    printValue' (Imp.TransparentValue _) e =+      return+        [ Exp $+            Call+              (Var "write_value")+              [ Arg e,+                ArgKeyword "binary" (Var "binary_output")+              ],+          Exp $ simpleCall "sys.stdout.write" [String "\n"]+        ]++prepareEntry ::+  (Name, Imp.Function op) ->+  CompilerM+    op+    s+    ( String,+      [String],+      [PyStmt],+      [PyStmt],+      [PyStmt],+      [PyStmt],+      [(Imp.ExternalValue, PyExp)],+      [PyStmt]+    )+prepareEntry (fname, Imp.Function _ outputs inputs _ results args) = do+  let output_paramNames = map (compileName . Imp.paramName) outputs+      funTuple = tupleOrSingle $ fmap Var output_paramNames++  (argexps_mem_copies, prepare_run) <- collect' $+    forM inputs $ \case+      Imp.MemParam name space -> do+        -- A program might write to its input parameters, so create a new memory+        -- block and copy the source there.  This way the program can be run more+        -- than once.+        name' <- newVName $ baseString name <> "_copy"+        copy <- asks envCopy+        allocate <- asks envAllocate+        let size = Var (extName (compileName name) ++ ".nbytes") -- FIXME+            dest = name'+            src = name+            offset = Integer 0+        case space of+          Space sid ->+            allocate (Var (compileName name')) size sid+          _ ->+            stm $+              Assign+                (Var (compileName name'))+                (simpleCall "allocateMem" [size]) -- FIXME+        dest' <- compileVar dest+        src' <- compileVar src+        copy dest' offset space src' offset space size (IntType Int32) -- FIXME+        return $ Just $ compileName name'+      _ -> return Nothing++  prepareIn <-+    collect $+      mapM_ entryPointInput $+        zip3 [0 ..] args $+          map (Var . extValueDescName) args+  (res, prepareOut) <- collect' $ mapM entryPointOutput results++  let argexps_lib = map (compileName . Imp.paramName) inputs+      argexps_bin = zipWith fromMaybe argexps_lib argexps_mem_copies+      fname' = "self." ++ futharkFun (nameToString fname)+      call_lib = [Assign funTuple $ simpleCall fname' (fmap Var argexps_lib)]+      call_bin = [Assign funTuple $ simpleCall fname' (fmap Var argexps_bin)]++  return+    ( nameToString fname,+      map extValueDescName args,+      prepareIn,+      call_lib,+      call_bin,+      prepareOut,+      zip results res,+      prepare_run+    )++copyMemoryDefaultSpace ::+  PyExp ->+  PyExp ->+  PyExp ->+  PyExp ->+  PyExp ->+  CompilerM op s ()+copyMemoryDefaultSpace destmem destidx srcmem srcidx nbytes = do+  let offset_call1 =+        simpleCall+          "addressOffset"+          [destmem, destidx, Var "ct.c_byte"]+  let offset_call2 =+        simpleCall+          "addressOffset"+          [srcmem, srcidx, Var "ct.c_byte"]+  stm $ Exp $ simpleCall "ct.memmove" [offset_call1, offset_call2, nbytes]++compileEntryFun ::+  [PyStmt] ->+  (Name, Imp.Function op) ->+  CompilerM op s (PyFunDef, (PyExp, PyExp))+compileEntryFun sync entry = do+  (fname', params, prepareIn, body_lib, _, prepareOut, res, _) <- prepareEntry entry+  let ret = Return $ tupleOrSingle $ map snd res+      (pts, rts) = entryTypes $ snd entry+  return+    ( Def fname' ("self" : params) $+        prepareIn ++ body_lib ++ prepareOut ++ sync ++ [ret],+      (String fname', Tuple [List (map String pts), List (map String rts)])+    )++entryTypes :: Imp.Function op -> ([String], [String])+entryTypes func =+  ( map desc $ Imp.functionArgs func,+    map desc $ Imp.functionResult func+  )+  where+    desc (Imp.OpaqueValue d _) = d+    desc (Imp.TransparentValue (Imp.ScalarValue pt s _)) = readTypeEnum pt s+    desc (Imp.TransparentValue (Imp.ArrayValue _ _ pt s dims)) =+      concat (replicate (length dims) "[]") ++ readTypeEnum pt s++callEntryFun ::+  [PyStmt] ->+  (Name, Imp.Function op) ->+  CompilerM op s (PyFunDef, String, PyExp)+callEntryFun pre_timing entry@(fname, Imp.Function _ _ _ _ _ decl_args) = do+  (_, _, prepare_in, _, body_bin, _, res, prepare_run) <- prepareEntry entry++  let str_input = map readInput decl_args+      end_of_input = [Exp $ simpleCall "end_of_input" [String $ pretty fname]]++      exitcall = [Exp $ simpleCall "sys.exit" [Field (String "Assertion.{} failed") "format(e)"]]+      except' = Catch (Var "AssertionError") exitcall+      do_run = body_bin ++ pre_timing+      (do_run_with_timing, close_runtime_file) = addTiming do_run++      -- We ignore overflow errors and the like for executable entry+      -- points.  These are (somewhat) well-defined in Futhark.+      ignore s = ArgKeyword s $ String "ignore"+      errstate = Call (Var "np.errstate") $ map ignore ["divide", "over", "under", "invalid"]++      do_warmup_run =+        If (Var "do_warmup_run") (prepare_run ++ do_run) []++      do_num_runs =+        For+          "i"+          (simpleCall "range" [simpleCall "int" [Var "num_runs"]])+          (prepare_run ++ do_run_with_timing)++  str_output <- printValue res++  let fname' = "entry_" ++ nameToString fname++  return+    ( Def fname' [] $+        str_input ++ end_of_input ++ prepare_in+          ++ [Try [With errstate [do_warmup_run, do_num_runs]] [except']]+          ++ [close_runtime_file]+          ++ str_output,+      nameToString fname,+      Var fname'+    )++addTiming :: [PyStmt] -> ([PyStmt], PyStmt)+addTiming statements =+  ( [Assign (Var "time_start") $ simpleCall "time.time" []]+      ++ statements+      ++ [ Assign (Var "time_end") $ simpleCall "time.time" [],+           If (Var "runtime_file") print_runtime []+         ],+    If (Var "runtime_file") [Exp $ simpleCall "runtime_file.close" []] []+  )+  where+    print_runtime =+      [ Exp $+          simpleCall+            "runtime_file.write"+            [ simpleCall+                "str"+                [ BinOp+                    "-"+                    (toMicroseconds (Var "time_end"))+                    (toMicroseconds (Var "time_start"))+                ]+            ],+        Exp $ simpleCall "runtime_file.write" [String "\n"],+        Exp $ simpleCall "runtime_file.flush" []+      ]+    toMicroseconds x =+      simpleCall "int" [BinOp "*" x $ Integer 1000000]++compileUnOp :: Imp.UnOp -> String+compileUnOp op =+  case op of+    Not -> "not"+    Complement {} -> "~"+    Abs {} -> "abs"+    FAbs {} -> "abs"+    SSignum {} -> "ssignum"+    USignum {} -> "usignum"++compileBinOpLike ::+  Monad m =>+  (v -> m PyExp) ->+  Imp.PrimExp v ->+  Imp.PrimExp v ->+  m (PyExp, PyExp, String -> m PyExp)+compileBinOpLike f x y = do+  x' <- compilePrimExp f x+  y' <- compilePrimExp f y+  let simple s = return $ BinOp s x' y'+  return (x', y', simple)++-- | The ctypes type corresponding to a 'PrimType'.+compilePrimType :: PrimType -> String+compilePrimType t =+  case t of+    IntType Int8 -> "ct.c_int8"+    IntType Int16 -> "ct.c_int16"+    IntType Int32 -> "ct.c_int32"+    IntType Int64 -> "ct.c_int64"+    FloatType Float32 -> "ct.c_float"+    FloatType Float64 -> "ct.c_double"+    Imp.Bool -> "ct.c_bool"+    Cert -> "ct.c_bool"++-- | The ctypes type corresponding to a 'PrimType', taking sign into account.+compilePrimTypeExt :: PrimType -> Imp.Signedness -> String+compilePrimTypeExt t ept =+  case (t, ept) of+    (IntType Int8, Imp.TypeUnsigned) -> "ct.c_uint8"+    (IntType Int16, Imp.TypeUnsigned) -> "ct.c_uint16"+    (IntType Int32, Imp.TypeUnsigned) -> "ct.c_uint32"+    (IntType Int64, Imp.TypeUnsigned) -> "ct.c_uint64"+    (IntType Int8, _) -> "ct.c_int8"+    (IntType Int16, _) -> "ct.c_int16"+    (IntType Int32, _) -> "ct.c_int32"+    (IntType Int64, _) -> "ct.c_int64"+    (FloatType Float32, _) -> "ct.c_float"+    (FloatType Float64, _) -> "ct.c_double"+    (Imp.Bool, _) -> "ct.c_bool"+    (Cert, _) -> "ct.c_byte"++-- | The Numpy type corresponding to a 'PrimType'.+compilePrimToNp :: Imp.PrimType -> String+compilePrimToNp bt =+  case bt of+    IntType Int8 -> "np.int8"+    IntType Int16 -> "np.int16"+    IntType Int32 -> "np.int32"+    IntType Int64 -> "np.int64"+    FloatType Float32 -> "np.float32"+    FloatType Float64 -> "np.float64"+    Imp.Bool -> "np.byte"+    Cert -> "np.byte"++-- | The Numpy type corresponding to a 'PrimType', taking sign into account.+compilePrimToExtNp :: Imp.PrimType -> Imp.Signedness -> String+compilePrimToExtNp bt ept =+  case (bt, ept) of+    (IntType Int8, Imp.TypeUnsigned) -> "np.uint8"+    (IntType Int16, Imp.TypeUnsigned) -> "np.uint16"+    (IntType Int32, Imp.TypeUnsigned) -> "np.uint32"+    (IntType Int64, Imp.TypeUnsigned) -> "np.uint64"+    (IntType Int8, _) -> "np.int8"+    (IntType Int16, _) -> "np.int16"+    (IntType Int32, _) -> "np.int32"+    (IntType Int64, _) -> "np.int64"+    (FloatType Float32, _) -> "np.float32"+    (FloatType Float64, _) -> "np.float64"+    (Imp.Bool, _) -> "np.bool_"+    (Cert, _) -> "np.byte"++compilePrimValue :: Imp.PrimValue -> PyExp+compilePrimValue (IntValue (Int8Value v)) =+  simpleCall "np.int8" [Integer $ toInteger v]+compilePrimValue (IntValue (Int16Value v)) =+  simpleCall "np.int16" [Integer $ toInteger v]+compilePrimValue (IntValue (Int32Value v)) =+  simpleCall "np.int32" [Integer $ toInteger v]+compilePrimValue (IntValue (Int64Value v)) =+  simpleCall "np.int64" [Integer $ toInteger v]+compilePrimValue (FloatValue (Float32Value v))+  | isInfinite v =+    if v > 0 then Var "np.inf" else Var "-np.inf"+  | isNaN v =+    Var "np.nan"+  | otherwise = simpleCall "np.float32" [Float $ fromRational $ toRational v]+compilePrimValue (FloatValue (Float64Value v))+  | isInfinite v =+    if v > 0 then Var "np.inf" else Var "-np.inf"+  | isNaN v =+    Var "np.nan"+  | otherwise = simpleCall "np.float64" [Float $ fromRational $ toRational v]+compilePrimValue (BoolValue v) = Bool v+compilePrimValue Checked = Var "True"++compileVar :: VName -> CompilerM op s PyExp+compileVar v =+  asks $ fromMaybe (Var $ compileName v) . M.lookup v . envVarExp++-- | Tell me how to compile a @v@, and I'll Compile any @PrimExp v@ for you.+compilePrimExp :: Monad m => (v -> m PyExp) -> Imp.PrimExp v -> m PyExp+compilePrimExp _ (Imp.ValueExp v) = return $ compilePrimValue v+compilePrimExp f (Imp.LeafExp v _) = f v+compilePrimExp f (Imp.BinOpExp op x y) = do+  (x', y', simple) <- compileBinOpLike f x y+  case op of+    Add {} -> simple "+"+    Sub {} -> simple "-"+    Mul {} -> simple "*"+    FAdd {} -> simple "+"+    FSub {} -> simple "-"+    FMul {} -> simple "*"+    FDiv {} -> simple "/"+    FMod {} -> simple "%"+    Xor {} -> simple "^"+    And {} -> simple "&"+    Or {} -> simple "|"+    Shl {} -> simple "<<"+    LogAnd {} -> simple "and"+    LogOr {} -> simple "or"+    _ -> return $ simpleCall (pretty op) [x', y']+compilePrimExp f (Imp.ConvOpExp conv x) = do+  x' <- compilePrimExp f x+  return $ simpleCall (pretty conv) [x']+compilePrimExp f (Imp.CmpOpExp cmp x y) = do+  (x', y', simple) <- compileBinOpLike f x y+  case cmp of+    CmpEq {} -> simple "=="+    FCmpLt {} -> simple "<"+    FCmpLe {} -> simple "<="+    CmpLlt -> simple "<"+    CmpLle -> simple "<="+    _ -> return $ simpleCall (pretty cmp) [x', y']+compilePrimExp f (Imp.UnOpExp op exp1) =+  UnOp (compileUnOp op) <$> compilePrimExp f exp1+compilePrimExp f (Imp.FunExp h args _) =+  simpleCall (futharkFun (pretty h)) <$> mapM (compilePrimExp f) args++compileExp :: Imp.Exp -> CompilerM op s PyExp+compileExp = compilePrimExp compileLeaf+  where+    compileLeaf (Imp.ScalarVar vname) =+      compileVar vname+    compileLeaf (Imp.SizeOf t) =+      return $ simpleCall (compilePrimToNp $ IntType Int32) [Integer $ primByteSize t]+    compileLeaf (Imp.Index src (Imp.Count iexp) restype (Imp.Space space) _) =+      join $+        asks envReadScalar+          <*> compileVar src+          <*> compileExp (Imp.untyped iexp)+          <*> pure restype+          <*> pure space+    compileLeaf (Imp.Index src (Imp.Count iexp) bt _ _) = do+      iexp' <- compileExp $ Imp.untyped iexp+      let bt' = compilePrimType bt+          nptype = compilePrimToNp bt+      src' <- compileVar src+      return $ simpleCall "indexArray" [src', iexp', Var bt', Var nptype]++compileCode :: Imp.Code op -> CompilerM op s ()+compileCode Imp.DebugPrint {} =+  return ()+compileCode (Imp.Op op) =+  join $ asks envOpCompiler <*> pure op+compileCode (Imp.If cond tb fb) = do+  cond' <- compileExp $ Imp.untyped cond+  tb' <- collect $ compileCode tb+  fb' <- collect $ compileCode fb+  stm $ If cond' tb' fb'+compileCode (c1 Imp.:>>: c2) = do+  compileCode c1+  compileCode c2+compileCode (Imp.While cond body) = do+  cond' <- compileExp $ Imp.untyped cond+  body' <- collect $ compileCode body+  stm $ While cond' body'+compileCode (Imp.For i bound body) = do+  bound' <- compileExp bound+  let i' = compileName i+  body' <- collect $ compileCode body+  counter <- pretty <$> newVName "counter"+  one <- pretty <$> newVName "one"+  stm $ Assign (Var i') $ simpleCall (compilePrimToNp (Imp.primExpType bound)) [Integer 0]+  stm $ Assign (Var one) $ simpleCall (compilePrimToNp (Imp.primExpType bound)) [Integer 1]+  stm $+    For counter (simpleCall "range" [bound']) $+      body' ++ [AssignOp "+" (Var i') (Var one)]+compileCode (Imp.SetScalar name exp1) =+  stm =<< Assign <$> compileVar name <*> compileExp exp1+compileCode Imp.DeclareMem {} = return ()+compileCode (Imp.DeclareScalar v _ Cert) = do+  v' <- compileVar v+  stm $ Assign v' $ Var "True"+compileCode Imp.DeclareScalar {} = return ()+compileCode (Imp.DeclareArray name (Space space) t vs) =+  join $+    asks envStaticArray+      <*> pure name+      <*> pure space+      <*> pure t+      <*> pure vs+compileCode (Imp.DeclareArray name _ t vs) = do+  let arr_name = compileName name <> "_arr"+  -- It is important to store the Numpy array in a temporary variable+  -- to prevent it from going "out-of-scope" before calling+  -- unwrapArray (which internally uses the .ctype method); see+  -- https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.ctypes.html+  atInit $+    Assign (Field (Var "self") arr_name) $ case vs of+      Imp.ArrayValues vs' ->+        Call+          (Var "np.array")+          [ Arg $ List $ map compilePrimValue vs',+            ArgKeyword "dtype" $ Var $ compilePrimToNp t+          ]+      Imp.ArrayZeros n ->+        Call+          (Var "np.zeros")+          [ Arg $ Integer $ fromIntegral n,+            ArgKeyword "dtype" $ Var $ compilePrimToNp t+          ]+  atInit $+    Assign (Field (Var "self") (compileName name)) $+      simpleCall "unwrapArray" [Field (Var "self") arr_name]+  name' <- compileVar name+  stm $ Assign name' $ Field (Var "self") (compileName name)+compileCode (Imp.Comment s code) = do+  code' <- collect $ compileCode code+  stm $ Comment s code'+compileCode (Imp.Assert e (Imp.ErrorMsg parts) (loc, locs)) = do+  e' <- compileExp e+  let onPart (Imp.ErrorString s) = return ("%s", String s)+      onPart (Imp.ErrorInt32 x) = ("%d",) <$> compileExp x+      onPart (Imp.ErrorInt64 x) = ("%d",) <$> compileExp x+  (formatstrs, formatargs) <- unzip <$> mapM onPart parts+  stm $+    Assert+      e'+      ( BinOp+          "%"+          (String $ "Error: " ++ concat formatstrs ++ "\n\nBacktrace:\n" ++ stacktrace)+          (Tuple formatargs)+      )+  where+    stacktrace = prettyStacktrace 0 $ map locStr $ loc : locs+compileCode (Imp.Call dests fname args) = do+  args' <- mapM compileArg args+  dests' <- tupleOrSingle <$> mapM compileVar dests+  let fname'+        | isBuiltInFunction fname = futharkFun (pretty fname)+        | otherwise = "self." ++ futharkFun (pretty fname)+      call' = simpleCall fname' args'+  -- If the function returns nothing (is called only for side+  -- effects), take care not to assign to an empty tuple.+  stm $+    if null dests+      then Exp call'+      else Assign dests' call'+  where+    compileArg (Imp.MemArg m) = compileVar m+    compileArg (Imp.ExpArg e) = compileExp e+compileCode (Imp.SetMem dest src _) =+  stm =<< Assign <$> compileVar dest <*> compileVar src+compileCode (Imp.Allocate name (Imp.Count (Imp.TPrimExp e)) (Imp.Space space)) =+  join $+    asks envAllocate+      <*> compileVar name+      <*> compileExp e+      <*> pure space+compileCode (Imp.Allocate name (Imp.Count (Imp.TPrimExp e)) _) = do+  e' <- compileExp e+  let allocate' = simpleCall "allocateMem" [e']+  stm =<< Assign <$> compileVar name <*> pure allocate'+compileCode (Imp.Free name _) =+  stm =<< Assign <$> compileVar name <*> pure None+compileCode (Imp.Copy dest (Imp.Count destoffset) DefaultSpace src (Imp.Count srcoffset) DefaultSpace (Imp.Count size)) = do+  destoffset' <- compileExp $ Imp.untyped destoffset+  srcoffset' <- compileExp $ Imp.untyped srcoffset+  dest' <- compileVar dest+  src' <- compileVar src+  size' <- compileExp $ Imp.untyped size+  let offset_call1 = simpleCall "addressOffset" [dest', destoffset', Var "ct.c_byte"]+  let offset_call2 = simpleCall "addressOffset" [src', srcoffset', Var "ct.c_byte"]+  stm $ Exp $ simpleCall "ct.memmove" [offset_call1, offset_call2, size']+compileCode (Imp.Copy dest (Imp.Count destoffset) destspace src (Imp.Count srcoffset) srcspace (Imp.Count size)) = do+  copy <- asks envCopy+  join $+    copy+      <$> compileVar dest+      <*> compileExp (Imp.untyped destoffset)+      <*> pure destspace+      <*> compileVar src+      <*> compileExp (Imp.untyped srcoffset)+      <*> pure srcspace+      <*> compileExp (Imp.untyped size)+      <*> pure (IntType Int32) -- FIXME+compileCode (Imp.Write dest (Imp.Count idx) elemtype (Imp.Space space) _ elemexp) =+  join $+    asks envWriteScalar+      <*> compileVar dest+      <*> compileExp (Imp.untyped idx)+      <*> pure elemtype+      <*> pure space+      <*> compileExp elemexp+compileCode (Imp.Write dest (Imp.Count idx) elemtype _ _ elemexp) = do+  idx' <- compileExp $ Imp.untyped idx+  elemexp' <- compileExp elemexp+  dest' <- compileVar dest+  let elemtype' = compilePrimType elemtype+      ctype = simpleCall elemtype' [elemexp']+  stm $ Exp $ simpleCall "writeScalarArray" [dest', idx', ctype] compileCode Imp.Skip = return ()
src/Futhark/CodeGen/Backends/GenericPython/AST.hs view
@@ -1,88 +1,94 @@ module Futhark.CodeGen.Backends.GenericPython.AST-  ( PyExp(..)-  , PyIdx (..)-  , PyArg (..)-  , PyStmt(..)-  , module Language.Futhark.Core-  , PyProg(..)-  , PyExcept(..)-  , PyFunDef(..)-  , PyClassDef(..)+  ( PyExp (..),+    PyIdx (..),+    PyArg (..),+    PyStmt (..),+    module Language.Futhark.Core,+    PyProg (..),+    PyExcept (..),+    PyFunDef (..),+    PyClassDef (..),   )-  where+where -import Language.Futhark.Core import Futhark.Util.Pretty---data UnOp = Not -- ^ Boolean negation.-          | Complement -- ^ Bitwise complement.-          | Negate -- ^ Numerical negation.-          | Abs -- ^ Absolute/numerical value.-            deriving (Eq, Show)--data PyExp = Integer Integer-           | Bool Bool-           | Float Double-           | String String-           | RawStringLiteral String-           | Var String-           | BinOp String PyExp PyExp-           | UnOp String PyExp-           | Cond PyExp PyExp PyExp-           | Index PyExp PyIdx-           | Call PyExp [PyArg]-           | Cast PyExp String-           | Tuple [PyExp]-           | List [PyExp]-           | Field PyExp String-           | Dict [(PyExp, PyExp)]-           | Lambda String PyExp-           | None-             deriving (Eq, Show)+import Language.Futhark.Core -data PyIdx = IdxRange PyExp PyExp-           | IdxExp PyExp-             deriving (Eq, Show)+data UnOp+  = -- | Boolean negation.+    Not+  | -- | Bitwise complement.+    Complement+  | -- | Numerical negation.+    Negate+  | -- | Absolute/numerical value.+    Abs+  deriving (Eq, Show) -data PyArg = ArgKeyword String PyExp-           | Arg PyExp-             deriving (Eq, Show)+data PyExp+  = Integer Integer+  | Bool Bool+  | Float Double+  | String String+  | RawStringLiteral String+  | Var String+  | BinOp String PyExp PyExp+  | UnOp String PyExp+  | Cond PyExp PyExp PyExp+  | Index PyExp PyIdx+  | Call PyExp [PyArg]+  | Cast PyExp String+  | Tuple [PyExp]+  | List [PyExp]+  | Field PyExp String+  | Dict [(PyExp, PyExp)]+  | Lambda String PyExp+  | None+  deriving (Eq, Show) -data PyStmt = If PyExp [PyStmt] [PyStmt]-            | Try [PyStmt] [PyExcept]-            | While PyExp [PyStmt]-            | For String PyExp [PyStmt]-            | With PyExp [PyStmt]-            | Assign PyExp PyExp-            | AssignOp String PyExp PyExp-            | Comment String [PyStmt]-            | Assert PyExp PyExp-            | Raise PyExp-            | Exp PyExp-            | Return PyExp-            | Pass+data PyIdx+  = IdxRange PyExp PyExp+  | IdxExp PyExp+  deriving (Eq, Show) -              -- Definition-like statements.-            | Import String (Maybe String)-            | FunDef PyFunDef-            | ClassDef PyClassDef+data PyArg+  = ArgKeyword String PyExp+  | Arg PyExp+  deriving (Eq, Show) -              -- Some arbitrary string of Python code.-            | Escape String-            deriving (Eq, Show)+data PyStmt+  = If PyExp [PyStmt] [PyStmt]+  | Try [PyStmt] [PyExcept]+  | While PyExp [PyStmt]+  | For String PyExp [PyStmt]+  | With PyExp [PyStmt]+  | Assign PyExp PyExp+  | AssignOp String PyExp PyExp+  | Comment String [PyStmt]+  | Assert PyExp PyExp+  | Raise PyExp+  | Exp PyExp+  | Return PyExp+  | Pass+  | -- Definition-like statements.+    Import String (Maybe String)+  | FunDef PyFunDef+  | ClassDef PyClassDef+  | -- Some arbitrary string of Python code.+    Escape String+  deriving (Eq, Show)  data PyExcept = Catch PyExp [PyStmt]-              deriving (Eq, Show)+  deriving (Eq, Show)  data PyFunDef = Def String [String] [PyStmt]-              deriving (Eq, Show)+  deriving (Eq, Show)  data PyClassDef = Class String [PyStmt]-                deriving (Eq, Show)+  deriving (Eq, Show)  newtype PyProg = PyProg [PyStmt]-            deriving (Eq, Show)+  deriving (Eq, Show)  instance Pretty PyIdx where   ppr (IdxExp e) = ppr e@@ -92,7 +98,6 @@   ppr (ArgKeyword k e) = text k <> equals <> ppr e   ppr (Arg e) = ppr e - instance Pretty PyExp where   ppr (Integer x) = ppr x   ppr (Bool x) = ppr x@@ -103,102 +108,86 @@   ppr (RawStringLiteral s) = text "\"\"\"" <> text s <> text "\"\"\""   ppr (Var n) = text $ map (\x -> if x == '\'' then 'm' else x) n   ppr (Field e s) = ppr e <> text "." <> text s-  ppr (BinOp s e1 e2) = parens(ppr e1 <+> text s <+> ppr e2)+  ppr (BinOp s e1 e2) = parens (ppr e1 <+> text s <+> ppr e2)   ppr (UnOp s e) = text s <> parens (ppr e)   ppr (Cond e1 e2 e3) = ppr e2 <+> text "if" <+> ppr e1 <+> text "else" <+> ppr e3-  ppr (Cast src bt) = text "ct.cast" <>-                      parens (ppr src <> text "," <+>-                              text "ct.POINTER" <> parens(text bt))-  ppr (Index src idx) = ppr src <> brackets(ppr idx)-  ppr (Call fun exps) = ppr fun <> parens(commasep $ map ppr exps)-  ppr (Tuple [dim]) = parens(ppr dim <> text ",")-  ppr (Tuple dims) = parens(commasep $ map ppr dims)+  ppr (Cast src bt) =+    text "ct.cast"+      <> parens+        ( ppr src <> text ","+            <+> text "ct.POINTER" <> parens (text bt)+        )+  ppr (Index src idx) = ppr src <> brackets (ppr idx)+  ppr (Call fun exps) = ppr fun <> parens (commasep $ map ppr exps)+  ppr (Tuple [dim]) = parens (ppr dim <> text ",")+  ppr (Tuple dims) = parens (commasep $ map ppr dims)   ppr (List es) = brackets $ commasep $ map ppr es   ppr (Dict kvs) = braces $ commasep $ map ppElem kvs-    where ppElem (k, v) = ppr k <> colon <+> ppr v+    where+      ppElem (k, v) = ppr k <> colon <+> ppr v   ppr (Lambda p e) = text "lambda" <+> text p <> text ":" <+> ppr e   ppr None = text "None"  instance Pretty PyStmt where   ppr (If cond [] []) =-    text "if" <+> ppr cond <> text ":" </>-    indent 2 (text "pass")-+    text "if" <+> ppr cond <> text ":"+      </> indent 2 (text "pass")   ppr (If cond [] fbranch) =-    text "if" <+> ppr cond <> text ":" </>-    indent 2 (text "pass") </>-    text "else:" </>-    indent 2 (stack $ map ppr fbranch)-+    text "if" <+> ppr cond <> text ":"+      </> indent 2 (text "pass")+      </> text "else:"+      </> indent 2 (stack $ map ppr fbranch)   ppr (If cond tbranch []) =-    text "if" <+> ppr cond <> text ":" </>-    indent 2 (stack $ map ppr tbranch)-+    text "if" <+> ppr cond <> text ":"+      </> indent 2 (stack $ map ppr tbranch)   ppr (If cond tbranch fbranch) =-    text "if" <+> ppr cond <> text ":" </>-    indent 2 (stack $ map ppr tbranch) </>-    text "else:" </>-    indent 2 (stack $ map ppr fbranch)-+    text "if" <+> ppr cond <> text ":"+      </> indent 2 (stack $ map ppr tbranch)+      </> text "else:"+      </> indent 2 (stack $ map ppr fbranch)   ppr (Try pystms pyexcepts) =-    text "try:" </>-    indent 2 (stack $ map ppr pystms) </>-    stack (map ppr pyexcepts)-+    text "try:"+      </> indent 2 (stack $ map ppr pystms)+      </> stack (map ppr pyexcepts)   ppr (While cond body) =-    text "while" <+> ppr cond <> text ":" </>-    indent 2 (stack $ map ppr body)-+    text "while" <+> ppr cond <> text ":"+      </> indent 2 (stack $ map ppr body)   ppr (For i what body) =-    text  "for" <+> ppr i <+> text "in" <+> ppr what <> text ":" </>-    indent 2 (stack $ map ppr body)-+    text "for" <+> ppr i <+> text "in" <+> ppr what <> text ":"+      </> indent 2 (stack $ map ppr body)   ppr (With what body) =-    text "with" <+> ppr what <> text ":" </>-    indent 2 (stack $ map ppr body)-+    text "with" <+> ppr what <> text ":"+      </> indent 2 (stack $ map ppr body)   ppr (Assign e1 e2) = ppr e1 <+> text "=" <+> ppr e2-   ppr (AssignOp op e1 e2) = ppr e1 <+> text (op ++ "=") <+> ppr e2-   ppr (Comment s body) = text "#" <> text s </> stack (map ppr body)-   ppr (Assert e1 e2) = text "assert" <+> ppr e1 <> text "," <+> ppr e2-   ppr (Raise e) = text "raise" <+> ppr e-   ppr (Exp c) = ppr c-   ppr (Return e) = text "return" <+> ppr e-   ppr Pass = text "pass"-   ppr (Import from (Just as)) =     text "import" <+> text from <+> text "as" <+> text as-   ppr (Import from Nothing) =     text "import" <+> text from-   ppr (FunDef d) = ppr d-   ppr (ClassDef d) = ppr d-   ppr (Escape s) = stack $ map text $ lines s  instance Pretty PyFunDef where   ppr (Def fname params body) =-    text "def" <+> text fname <> parens (commasep $ map ppr params) <> text ":" </>-    indent 2 (stack (map ppr body))+    text "def" <+> text fname <> parens (commasep $ map ppr params) <> text ":"+      </> indent 2 (stack (map ppr body))  instance Pretty PyClassDef where   ppr (Class cname body) =-    text "class" <+> text cname <> text ":" </>-    indent 2 (stack (map ppr body))+    text "class" <+> text cname <> text ":"+      </> indent 2 (stack (map ppr body))  instance Pretty PyExcept where   ppr (Catch pyexp stms) =-    text "except" <+> ppr pyexp <+> text "as e:" </>-    indent 2 (stack $ map ppr stms)+    text "except" <+> ppr pyexp <+> text "as e:"+      </> indent 2 (stack $ map ppr stms)  instance Pretty PyProg where   ppr (PyProg stms) = stack (map ppr stms)
src/Futhark/CodeGen/Backends/GenericPython/Definitions.hs view
@@ -1,11 +1,13 @@ {-# LANGUAGE TemplateHaskell #-}+ module Futhark.CodeGen.Backends.GenericPython.Definitions-  ( pyFunctions-  , pyUtility-  , pyValues-  , pyPanic-  , pyTuning-  ) where+  ( pyFunctions,+    pyUtility,+    pyValues,+    pyPanic,+    pyTuning,+  )+where  import Data.FileEmbed 
src/Futhark/CodeGen/Backends/GenericPython/Options.hs view
@@ -3,11 +3,11 @@ -- Python code that will perform side effects, usually by setting some -- global variables. module Futhark.CodeGen.Backends.GenericPython.Options-       ( Option (..)-       , OptionArgument (..)-       , generateOptionParser-       )-       where+  ( Option (..),+    OptionArgument (..),+    generateOptionParser,+  )+where  import Futhark.CodeGen.Backends.GenericPython.AST @@ -16,16 +16,18 @@ -- -- When the statement is being executed, the argument (if any) will be -- stored in the variable @optarg@.-data Option = Option { optionLongName :: String-                     , optionShortName :: Maybe Char-                     , optionArgument :: OptionArgument-                     , optionAction :: [PyStmt]-                     }+data Option = Option+  { optionLongName :: String,+    optionShortName :: Maybe Char,+    optionArgument :: OptionArgument,+    optionAction :: [PyStmt]+  }  -- | Whether an option accepts an argument.-data OptionArgument = NoArgument-                    | RequiredArgument String-                    | OptionalArgument+data OptionArgument+  = NoArgument+  | RequiredArgument String+  | OptionalArgument  -- | Generate option parsing code that accepts the given command line options.  Will read from @sys.argv@. --@@ -33,41 +35,59 @@ -- terminate with error code 1. generateOptionParser :: [Option] -> [PyStmt] generateOptionParser options =-  [Assign (Var "parser")-   (Call (Var "argparse.ArgumentParser")-    [ArgKeyword "description" $-     String "A compiled Futhark program."])] ++-  map parseOption options ++-  [Assign (Var "parser_result") $-   Call (Var "vars") [Arg $ Call (Var "parser.parse_args") [Arg $ Var "sys.argv[1:]"]]] ++-  map executeOption options-  where parseOption option =-          Exp $ Call (Var "parser.add_argument") $-          map (Arg . String) name_args ++-          argument_args-          where name_args = maybe id ((:) . ('-':) . (:[])) (optionShortName option)-                            ["--" ++ optionLongName option]-                argument_args = case optionArgument option of-                  RequiredArgument t ->-                    [ArgKeyword "action" (String "append"),-                     ArgKeyword "default" $ List [],-                     ArgKeyword "type" $ Var t]--                  NoArgument ->-                    [ArgKeyword "action" (String "append_const"),-                     ArgKeyword "default" $ List [],-                     ArgKeyword "const" None]--                  OptionalArgument ->-                    [ArgKeyword "action" (String "append"),-                     ArgKeyword "default" $ List [],-                     ArgKeyword "nargs" $ String "?"]+  [ Assign+      (Var "parser")+      ( Call+          (Var "argparse.ArgumentParser")+          [ ArgKeyword "description" $+              String "A compiled Futhark program."+          ]+      )+  ]+    ++ map parseOption options+    ++ [ Assign (Var "parser_result") $+           Call (Var "vars") [Arg $ Call (Var "parser.parse_args") [Arg $ Var "sys.argv[1:]"]]+       ]+    ++ map executeOption options+  where+    parseOption option =+      Exp $+        Call (Var "parser.add_argument") $+          map (Arg . String) name_args+            ++ argument_args+      where+        name_args =+          maybe+            id+            ((:) . ('-' :) . (: []))+            (optionShortName option)+            ["--" ++ optionLongName option]+        argument_args = case optionArgument option of+          RequiredArgument t ->+            [ ArgKeyword "action" (String "append"),+              ArgKeyword "default" $ List [],+              ArgKeyword "type" $ Var t+            ]+          NoArgument ->+            [ ArgKeyword "action" (String "append_const"),+              ArgKeyword "default" $ List [],+              ArgKeyword "const" None+            ]+          OptionalArgument ->+            [ ArgKeyword "action" (String "append"),+              ArgKeyword "default" $ List [],+              ArgKeyword "nargs" $ String "?"+            ] -        executeOption option =-          For "optarg" (Index (Var "parser_result") $-                        IdxExp $ String $ fieldName option) $-            optionAction option+    executeOption option =+      For+        "optarg"+        ( Index (Var "parser_result") $+            IdxExp $ String $ fieldName option+        )+        $ optionAction option -        fieldName = map escape . optionLongName-          where escape '-' = '_'-                escape c = c+    fieldName = map escape . optionLongName+      where+        escape '-' = '_'+        escape c = c
src/Futhark/CodeGen/Backends/PyOpenCL.hs view
@@ -1,131 +1,179 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TupleSections #-}+ module Futhark.CodeGen.Backends.PyOpenCL-  ( compileProg-  ) where+  ( compileProg,+  )+where  import Control.Monad import qualified Data.Map as M--import Futhark.IR.KernelsMem (Prog, KernelsMem)-import Futhark.CodeGen.Backends.PyOpenCL.Boilerplate import qualified Futhark.CodeGen.Backends.GenericPython as Py-import qualified Futhark.CodeGen.ImpCode.OpenCL as Imp-import qualified Futhark.CodeGen.ImpGen.OpenCL as ImpGen import Futhark.CodeGen.Backends.GenericPython.AST-import Futhark.CodeGen.Backends.GenericPython.Options import Futhark.CodeGen.Backends.GenericPython.Definitions+import Futhark.CodeGen.Backends.GenericPython.Options+import Futhark.CodeGen.Backends.PyOpenCL.Boilerplate+import qualified Futhark.CodeGen.ImpCode.OpenCL as Imp+import qualified Futhark.CodeGen.ImpGen.OpenCL as ImpGen+import Futhark.IR.KernelsMem (KernelsMem, Prog) import Futhark.MonadFreshNames import Futhark.Util (zEncodeString)  --maybe pass the config file rather than multiple arguments-compileProg :: MonadFreshNames m =>-               Maybe String -> Prog KernelsMem -> m (ImpGen.Warnings, String)+compileProg ::+  MonadFreshNames m =>+  Maybe String ->+  Prog KernelsMem ->+  m (ImpGen.Warnings, String) compileProg module_name prog = do-  (ws, Imp.Program opencl_code opencl_prelude-       kernels types sizes failures prog') <-+  ( ws,+    Imp.Program+      opencl_code+      opencl_prelude+      kernels+      types+      sizes+      failures+      prog'+    ) <-     ImpGen.compileProg prog   --prepare the strings for assigning the kernels and set them as global-  let assign = unlines $-               map (\x -> pretty $-                          Assign (Var ("self."++zEncodeString (nameToString x)++"_var"))-                          (Var $ "program."++zEncodeString (nameToString x))) $-        M.keys kernels+  let assign =+        unlines $+          map+            ( \x ->+                pretty $+                  Assign+                    (Var ("self." ++ zEncodeString (nameToString x) ++ "_var"))+                    (Var $ "program." ++ zEncodeString (nameToString x))+            )+            $ M.keys kernels    let defines =-        [Assign (Var "synchronous") $ Bool False,-         Assign (Var "preferred_platform") None,-         Assign (Var "preferred_device") None,-         Assign (Var "default_threshold") None,-         Assign (Var "default_group_size") None,-         Assign (Var "default_num_groups") None,-         Assign (Var "default_tile_size") None,-         Assign (Var "fut_opencl_src") $ RawStringLiteral $ opencl_prelude ++ opencl_code,-         Escape pyValues,-         Escape pyFunctions,-         Escape pyPanic,-         Escape pyTuning]+        [ Assign (Var "synchronous") $ Bool False,+          Assign (Var "preferred_platform") None,+          Assign (Var "preferred_device") None,+          Assign (Var "default_threshold") None,+          Assign (Var "default_group_size") None,+          Assign (Var "default_num_groups") None,+          Assign (Var "default_tile_size") None,+          Assign (Var "fut_opencl_src") $ RawStringLiteral $ opencl_prelude ++ opencl_code,+          Escape pyValues,+          Escape pyFunctions,+          Escape pyPanic,+          Escape pyTuning+        ] -  let imports = [Import "sys" Nothing,-                 Import "numpy" $ Just "np",-                 Import "ctypes" $ Just "ct",-                 Escape openClPrelude,-                 Import "pyopencl.array" Nothing,-                 Import "time" Nothing]+  let imports =+        [ Import "sys" Nothing,+          Import "numpy" $ Just "np",+          Import "ctypes" $ Just "ct",+          Escape openClPrelude,+          Import "pyopencl.array" Nothing,+          Import "time" Nothing+        ] -  let constructor = Py.Constructor [ "self"-                                   , "command_queue=None"-                                   , "interactive=False"-                                   , "platform_pref=preferred_platform"-                                   , "device_pref=preferred_device"-                                   , "default_group_size=default_group_size"-                                   , "default_num_groups=default_num_groups"-                                   , "default_tile_size=default_tile_size"-                                   , "default_threshold=default_threshold"-                                   , "sizes=sizes"]-                    [Escape $ openClInit types assign sizes failures]-      options = [ Option { optionLongName = "platform"-                         , optionShortName = Just 'p'-                         , optionArgument = RequiredArgument "str"-                         , optionAction =-                           [ Assign (Var "preferred_platform") $ Var "optarg" ]-                         }-                , Option { optionLongName = "device"-                         , optionShortName = Just 'd'-                         , optionArgument = RequiredArgument "str"-                         , optionAction =-                           [ Assign (Var "preferred_device") $ Var "optarg" ]-                         }-                , Option { optionLongName = "default-threshold"-                         , optionShortName = Nothing-                         , optionArgument = RequiredArgument "int"-                         , optionAction =-                           [ Assign (Var "default_threshold") $ Var "optarg" ]-                         }-                , Option { optionLongName = "default-group-size"-                         , optionShortName = Nothing-                         , optionArgument = RequiredArgument "int"-                         , optionAction =-                           [ Assign (Var "default_group_size") $ Var "optarg" ]-                         }-                , Option { optionLongName = "default-num-groups"-                         , optionShortName = Nothing-                         , optionArgument = RequiredArgument "int"-                         , optionAction =-                           [ Assign (Var "default_num_groups") $ Var "optarg" ]-                         }-                , Option { optionLongName = "default-tile-size"-                         , optionShortName = Nothing-                         , optionArgument = RequiredArgument "int"-                         , optionAction =-                           [ Assign (Var "default_tile_size") $ Var "optarg" ]-                         }-                , Option { optionLongName = "size"-                         , optionShortName = Nothing-                         , optionArgument = RequiredArgument "size_assignment"-                         , optionAction =-                             [Assign (Index (Var "sizes")-                                      (IdxExp (Index (Var "optarg")-                                               (IdxExp (Integer 0)))))-                               (Index (Var "optarg") (IdxExp (Integer 1)))-                             ]-                         }+  let constructor =+        Py.Constructor+          [ "self",+            "command_queue=None",+            "interactive=False",+            "platform_pref=preferred_platform",+            "device_pref=preferred_device",+            "default_group_size=default_group_size",+            "default_num_groups=default_num_groups",+            "default_tile_size=default_tile_size",+            "default_threshold=default_threshold",+            "sizes=sizes"+          ]+          [Escape $ openClInit types assign sizes failures]+      options =+        [ Option+            { optionLongName = "platform",+              optionShortName = Just 'p',+              optionArgument = RequiredArgument "str",+              optionAction =+                [Assign (Var "preferred_platform") $ Var "optarg"]+            },+          Option+            { optionLongName = "device",+              optionShortName = Just 'd',+              optionArgument = RequiredArgument "str",+              optionAction =+                [Assign (Var "preferred_device") $ Var "optarg"]+            },+          Option+            { optionLongName = "default-threshold",+              optionShortName = Nothing,+              optionArgument = RequiredArgument "int",+              optionAction =+                [Assign (Var "default_threshold") $ Var "optarg"]+            },+          Option+            { optionLongName = "default-group-size",+              optionShortName = Nothing,+              optionArgument = RequiredArgument "int",+              optionAction =+                [Assign (Var "default_group_size") $ Var "optarg"]+            },+          Option+            { optionLongName = "default-num-groups",+              optionShortName = Nothing,+              optionArgument = RequiredArgument "int",+              optionAction =+                [Assign (Var "default_num_groups") $ Var "optarg"]+            },+          Option+            { optionLongName = "default-tile-size",+              optionShortName = Nothing,+              optionArgument = RequiredArgument "int",+              optionAction =+                [Assign (Var "default_tile_size") $ Var "optarg"]+            },+          Option+            { optionLongName = "size",+              optionShortName = Nothing,+              optionArgument = RequiredArgument "size_assignment",+              optionAction =+                [ Assign+                    ( Index+                        (Var "sizes")+                        ( IdxExp+                            ( Index+                                (Var "optarg")+                                (IdxExp (Integer 0))+                            )+                        )+                    )+                    (Index (Var "optarg") (IdxExp (Integer 1)))                 ]+            }+        ] -  (ws,) <$>-    Py.compileProg module_name constructor imports defines operations ()-    [Exp $ Py.simpleCall "sync" [Var "self"]] options prog'-  where operations :: Py.Operations Imp.OpenCL ()-        operations = Py.Operations-                     { Py.opsCompiler = callKernel-                     , Py.opsWriteScalar = writeOpenCLScalar-                     , Py.opsReadScalar = readOpenCLScalar-                     , Py.opsAllocate = allocateOpenCLBuffer-                     , Py.opsCopy = copyOpenCLMemory-                     , Py.opsStaticArray = staticOpenCLArray-                     , Py.opsEntryOutput = packArrayOutput-                     , Py.opsEntryInput = unpackArrayInput-                     }+  (ws,)+    <$> Py.compileProg+      module_name+      constructor+      imports+      defines+      operations+      ()+      [Exp $ Py.simpleCall "sync" [Var "self"]]+      options+      prog'+  where+    operations :: Py.Operations Imp.OpenCL ()+    operations =+      Py.Operations+        { Py.opsCompiler = callKernel,+          Py.opsWriteScalar = writeOpenCLScalar,+          Py.opsReadScalar = readOpenCLScalar,+          Py.opsAllocate = allocateOpenCLBuffer,+          Py.opsCopy = copyOpenCLMemory,+          Py.opsStaticArray = staticOpenCLArray,+          Py.opsEntryOutput = packArrayOutput,+          Py.opsEntryInput = unpackArrayInput+        }  -- We have many casts to 'long', because PyOpenCL may get confused at -- the 32-bit numbers that ImpCode uses for offsets and the like.@@ -135,18 +183,20 @@ callKernel :: Py.OpCompiler Imp.OpenCL () callKernel (Imp.GetSize v key) = do   v' <- Py.compileVar v-  Py.stm $ Assign v' $-    Index (Var "self.sizes") (IdxExp $ String $ pretty key)+  Py.stm $+    Assign v' $+      Index (Var "self.sizes") (IdxExp $ String $ pretty key) callKernel (Imp.CmpSizeLe v key x) = do   v' <- Py.compileVar v   x' <- Py.compileExp x-  Py.stm $ Assign v' $-    BinOp "<=" (Index (Var "self.sizes") (IdxExp $ String $ pretty key)) x'+  Py.stm $+    Assign v' $+      BinOp "<=" (Index (Var "self.sizes") (IdxExp $ String $ pretty key)) x' callKernel (Imp.GetSizeMax v size_class) = do   v' <- Py.compileVar v-  Py.stm $ Assign v' $-    Var $ "self.max_" ++ pretty size_class-+  Py.stm $+    Assign v' $+      Var $ "self.max_" ++ pretty size_class callKernel (Imp.LaunchKernel safety name args num_workgroups workgroup_size) = do   num_workgroups' <- mapM (fmap asLong . Py.compileExp) num_workgroups   workgroup_size' <- mapM (fmap asLong . Py.compileExp) workgroup_size@@ -158,45 +208,67 @@   Py.stm $ If cond body []    when (safety >= Imp.SafetyFull) $-    Py.stm $ Assign (Var "self.failure_is_an_option") $-    Py.compilePrimValue (Imp.IntValue (Imp.Int32Value 1))--  where mult_exp = BinOp "*"+    Py.stm $+      Assign (Var "self.failure_is_an_option") $+        Py.compilePrimValue (Imp.IntValue (Imp.Int32Value 1))+  where+    mult_exp = BinOp "*" -launchKernel :: Imp.KernelName -> Imp.KernelSafety -> [PyExp] -> [PyExp] -> [Imp.KernelArg]-             -> Py.CompilerM op s ()+launchKernel ::+  Imp.KernelName ->+  Imp.KernelSafety ->+  [PyExp] ->+  [PyExp] ->+  [Imp.KernelArg] ->+  Py.CompilerM op s () launchKernel kernel_name safety kernel_dims workgroup_dims args = do   let kernel_dims' = Tuple kernel_dims       workgroup_dims' = Tuple workgroup_dims       kernel_name' = "self." ++ zEncodeString (nameToString kernel_name) ++ "_var"   args' <- mapM processKernelArg args-  let failure_args = take (Imp.numFailureParams safety)-                     [Var "self.global_failure",-                      Var "self.failure_is_an_option",-                      Var "self.global_failure_args"]-  Py.stm $ Exp $ Py.simpleCall (kernel_name' ++ ".set_args") $-    failure_args ++ args'-  Py.stm $ Exp $ Py.simpleCall "cl.enqueue_nd_range_kernel"-    [Var "self.queue", Var kernel_name', kernel_dims', workgroup_dims']+  let failure_args =+        take+          (Imp.numFailureParams safety)+          [ Var "self.global_failure",+            Var "self.failure_is_an_option",+            Var "self.global_failure_args"+          ]+  Py.stm $+    Exp $+      Py.simpleCall (kernel_name' ++ ".set_args") $+        failure_args ++ args'+  Py.stm $+    Exp $+      Py.simpleCall+        "cl.enqueue_nd_range_kernel"+        [Var "self.queue", Var kernel_name', kernel_dims', workgroup_dims']   finishIfSynchronous-  where processKernelArg :: Imp.KernelArg -> Py.CompilerM op s PyExp-        processKernelArg (Imp.ValueKArg e bt) = do-          e' <- Py.compileExp e-          return $ Py.simpleCall (Py.compilePrimToNp bt) [e']-        processKernelArg (Imp.MemKArg v) = Py.compileVar v-        processKernelArg (Imp.SharedMemoryKArg (Imp.Count num_bytes)) = do-          num_bytes' <- Py.compileExp num_bytes-          return $ Py.simpleCall "cl.LocalMemory" [asLong num_bytes']+  where+    processKernelArg :: Imp.KernelArg -> Py.CompilerM op s PyExp+    processKernelArg (Imp.ValueKArg e bt) = do+      e' <- Py.compileExp e+      return $ Py.simpleCall (Py.compilePrimToNp bt) [e']+    processKernelArg (Imp.MemKArg v) = Py.compileVar v+    processKernelArg (Imp.SharedMemoryKArg (Imp.Count num_bytes)) = do+      num_bytes' <- Py.compileExp num_bytes+      return $ Py.simpleCall "cl.LocalMemory" [asLong num_bytes']  writeOpenCLScalar :: Py.WriteScalar Imp.OpenCL () writeOpenCLScalar mem i bt "device" val = do-  let nparr = Call (Var "np.array")-              [Arg val, ArgKeyword "dtype" $ Var $ Py.compilePrimType bt]-  Py.stm $ Exp $ Call (Var "cl.enqueue_copy")-    [Arg $ Var "self.queue", Arg mem, Arg nparr,-     ArgKeyword "device_offset" $ BinOp "*" (asLong i) (Integer $ Imp.primByteSize bt),-     ArgKeyword "is_blocking" $ Var "synchronous"]-+  let nparr =+        Call+          (Var "np.array")+          [Arg val, ArgKeyword "dtype" $ Var $ Py.compilePrimType bt]+  Py.stm $+    Exp $+      Call+        (Var "cl.enqueue_copy")+        [ Arg $ Var "self.queue",+          Arg mem,+          Arg nparr,+          ArgKeyword "device_offset" $ BinOp "*" (asLong i) (Integer $ Imp.primByteSize bt),+          ArgKeyword "is_blocking" $ Var "synchronous"+        ] writeOpenCLScalar _ _ _ space _ =   error $ "Cannot write to '" ++ space ++ "' memory space." @@ -204,25 +276,33 @@ readOpenCLScalar mem i bt "device" = do   val <- newVName "read_res"   let val' = Var $ pretty val-  let nparr = Call (Var "np.empty")-              [Arg $ Integer 1,-               ArgKeyword "dtype" (Var $ Py.compilePrimType bt)]+  let nparr =+        Call+          (Var "np.empty")+          [ Arg $ Integer 1,+            ArgKeyword "dtype" (Var $ Py.compilePrimType bt)+          ]   Py.stm $ Assign val' nparr-  Py.stm $ Exp $ Call (Var "cl.enqueue_copy")-    [Arg $ Var "self.queue", Arg val', Arg mem,-     ArgKeyword "device_offset" $ BinOp "*" (asLong i) (Integer $ Imp.primByteSize bt),-     ArgKeyword "is_blocking" $ Var "synchronous"]+  Py.stm $+    Exp $+      Call+        (Var "cl.enqueue_copy")+        [ Arg $ Var "self.queue",+          Arg val',+          Arg mem,+          ArgKeyword "device_offset" $ BinOp "*" (asLong i) (Integer $ Imp.primByteSize bt),+          ArgKeyword "is_blocking" $ Var "synchronous"+        ]   Py.stm $ Exp $ Py.simpleCall "sync" [Var "self"]   return $ Index val' $ IdxExp $ Integer 0- readOpenCLScalar _ _ _ space =   error $ "Cannot read from '" ++ space ++ "' memory space."  allocateOpenCLBuffer :: Py.Allocate Imp.OpenCL () allocateOpenCLBuffer mem size "device" =-  Py.stm $ Assign mem $-  Py.simpleCall "opencl_alloc" [Var "self", size, String $ pretty mem]-+  Py.stm $+    Assign mem $+      Py.simpleCall "opencl_alloc" [Var "self", size, String $ pretty mem] allocateOpenCLBuffer _ _ space =   error $ "Cannot allocate in '" ++ space ++ "' space" @@ -231,53 +311,73 @@   let divide = BinOp "//" nbytes (Integer $ Imp.primByteSize bt)       end = BinOp "+" destidx divide       dest = Index destmem (IdxRange destidx end)-  Py.stm $ ifNotZeroSize nbytes $-    Exp $ Call (Var "cl.enqueue_copy")-    [Arg $ Var "self.queue", Arg dest, Arg srcmem,-     ArgKeyword "device_offset" $ asLong srcidx,-     ArgKeyword "is_blocking" $ Var "synchronous"]-+  Py.stm $+    ifNotZeroSize nbytes $+      Exp $+        Call+          (Var "cl.enqueue_copy")+          [ Arg $ Var "self.queue",+            Arg dest,+            Arg srcmem,+            ArgKeyword "device_offset" $ asLong srcidx,+            ArgKeyword "is_blocking" $ Var "synchronous"+          ] copyOpenCLMemory destmem destidx (Imp.Space "device") srcmem srcidx Imp.DefaultSpace nbytes bt = do   let divide = BinOp "//" nbytes (Integer $ Imp.primByteSize bt)       end = BinOp "+" srcidx divide       src = Index srcmem (IdxRange srcidx end)-  Py.stm $ ifNotZeroSize nbytes $-    Exp $ Call (Var "cl.enqueue_copy")-    [Arg $ Var "self.queue", Arg destmem, Arg src,-     ArgKeyword "device_offset" $ asLong destidx,-     ArgKeyword "is_blocking" $ Var "synchronous"]-+  Py.stm $+    ifNotZeroSize nbytes $+      Exp $+        Call+          (Var "cl.enqueue_copy")+          [ Arg $ Var "self.queue",+            Arg destmem,+            Arg src,+            ArgKeyword "device_offset" $ asLong destidx,+            ArgKeyword "is_blocking" $ Var "synchronous"+          ] copyOpenCLMemory destmem destidx (Imp.Space "device") srcmem srcidx (Imp.Space "device") nbytes _ = do-  Py.stm $ ifNotZeroSize nbytes $-    Exp $ Call (Var "cl.enqueue_copy")-    [Arg $ Var "self.queue", Arg destmem, Arg srcmem,-     ArgKeyword "dest_offset" $ asLong destidx,-     ArgKeyword "src_offset" $ asLong srcidx,-     ArgKeyword "byte_count" $ asLong nbytes]+  Py.stm $+    ifNotZeroSize nbytes $+      Exp $+        Call+          (Var "cl.enqueue_copy")+          [ Arg $ Var "self.queue",+            Arg destmem,+            Arg srcmem,+            ArgKeyword "dest_offset" $ asLong destidx,+            ArgKeyword "src_offset" $ asLong srcidx,+            ArgKeyword "byte_count" $ asLong nbytes+          ]   finishIfSynchronous- copyOpenCLMemory destmem destidx Imp.DefaultSpace srcmem srcidx Imp.DefaultSpace nbytes _ =   Py.copyMemoryDefaultSpace destmem destidx srcmem srcidx nbytes--copyOpenCLMemory _ _ destspace _ _ srcspace _ _=+copyOpenCLMemory _ _ destspace _ _ srcspace _ _ =   error $ "Cannot copy to " ++ show destspace ++ " from " ++ show srcspace  staticOpenCLArray :: Py.StaticArray Imp.OpenCL () staticOpenCLArray name "device" t vs = do   mapM_ Py.atInit <=< Py.collect $ do     -- Create host-side Numpy array with intended values.-    Py.stm $ Assign (Var name') $ case vs of-      Imp.ArrayValues vs' ->-        Call (Var "np.array")-        [Arg $ List $ map Py.compilePrimValue vs',-         ArgKeyword "dtype" $ Var $ Py.compilePrimToNp t]-      Imp.ArrayZeros n ->-        Call (Var "np.zeros")-        [Arg $ Integer $ fromIntegral n,-         ArgKeyword "dtype" $ Var $ Py.compilePrimToNp t]+    Py.stm $+      Assign (Var name') $ case vs of+        Imp.ArrayValues vs' ->+          Call+            (Var "np.array")+            [ Arg $ List $ map Py.compilePrimValue vs',+              ArgKeyword "dtype" $ Var $ Py.compilePrimToNp t+            ]+        Imp.ArrayZeros n ->+          Call+            (Var "np.zeros")+            [ Arg $ Integer $ fromIntegral n,+              ArgKeyword "dtype" $ Var $ Py.compilePrimToNp t+            ] -    let num_elems = case vs of Imp.ArrayValues vs' -> length vs'-                               Imp.ArrayZeros n -> n+    let num_elems = case vs of+          Imp.ArrayValues vs' -> length vs'+          Imp.ArrayZeros n -> n      -- Create memory block on the device.     static_mem <- newVName "static_mem"@@ -285,58 +385,74 @@     allocateOpenCLBuffer (Var (Py.compileName static_mem)) size "device"      -- Copy Numpy array to the device memory block.-    Py.stm $ ifNotZeroSize size $-      Exp $ Call (Var "cl.enqueue_copy")-      [Arg $ Var "self.queue",-       Arg $ Var $ Py.compileName static_mem,-       Arg $ Call (Var "normaliseArray") [Arg (Var name')],-       ArgKeyword "is_blocking" $ Var "synchronous"]+    Py.stm $+      ifNotZeroSize size $+        Exp $+          Call+            (Var "cl.enqueue_copy")+            [ Arg $ Var "self.queue",+              Arg $ Var $ Py.compileName static_mem,+              Arg $ Call (Var "normaliseArray") [Arg (Var name')],+              ArgKeyword "is_blocking" $ Var "synchronous"+            ]      -- Store the memory block for later reference.-    Py.stm $ Assign (Field (Var "self") name') $-      Var $ Py.compileName static_mem+    Py.stm $+      Assign (Field (Var "self") name') $+        Var $ Py.compileName static_mem    Py.stm $ Assign (Var name') (Field (Var "self") name')-  where name' = Py.compileName name+  where+    name' = Py.compileName name staticOpenCLArray _ space _ _ =   error $ "PyOpenCL backend cannot create static array in memory space '" ++ space ++ "'"  packArrayOutput :: Py.EntryOutput Imp.OpenCL () packArrayOutput mem "device" bt ept dims = do   mem' <- Py.compileVar mem-  return $ Call (Var "cl.array.Array")-    [Arg $ Var "self.queue",-     Arg $ Tuple $ map Py.compileDim dims,-     Arg $ Var $ Py.compilePrimTypeExt bt ept,-     ArgKeyword "data" mem']+  return $+    Call+      (Var "cl.array.Array")+      [ Arg $ Var "self.queue",+        Arg $ Tuple $ map Py.compileDim dims,+        Arg $ Var $ Py.compilePrimTypeExt bt ept,+        ArgKeyword "data" mem'+      ] packArrayOutput _ sid _ _ _ =   error $ "Cannot return array from " ++ sid ++ " space."  unpackArrayInput :: Py.EntryInput Imp.OpenCL () unpackArrayInput mem "device" t s dims e = do   let type_is_ok =-        BinOp "and"-        (BinOp "in" (Py.simpleCall "type" [e]) (List [Var "np.ndarray", Var "cl.array.Array"]))-        (BinOp "==" (Field e "dtype") (Var (Py.compilePrimToExtNp t s)))+        BinOp+          "and"+          (BinOp "in" (Py.simpleCall "type" [e]) (List [Var "np.ndarray", Var "cl.array.Array"]))+          (BinOp "==" (Field e "dtype") (Var (Py.compilePrimToExtNp t s)))   Py.stm $ Assert type_is_ok $ String "Parameter has unexpected type" -  zipWithM_ (Py.unpackDim e) dims [0..]+  zipWithM_ (Py.unpackDim e) dims [0 ..]    let memsize' = Py.simpleCall "np.int64" [Field e "nbytes"]       pyOpenCLArrayCase =         [Assign mem $ Field e "data"]   numpyArrayCase <- Py.collect $ do     allocateOpenCLBuffer mem memsize' "device"-    Py.stm $ ifNotZeroSize memsize' $-      Exp $ Call (Var "cl.enqueue_copy")-      [Arg $ Var "self.queue",-       Arg mem,-       Arg $ Call (Var "normaliseArray") [Arg e],-       ArgKeyword "is_blocking" $ Var "synchronous"]+    Py.stm $+      ifNotZeroSize memsize' $+        Exp $+          Call+            (Var "cl.enqueue_copy")+            [ Arg $ Var "self.queue",+              Arg mem,+              Arg $ Call (Var "normaliseArray") [Arg e],+              ArgKeyword "is_blocking" $ Var "synchronous"+            ] -  Py.stm $ If (BinOp "==" (Py.simpleCall "type" [e]) (Var "cl.array.Array"))-    pyOpenCLArrayCase-    numpyArrayCase+  Py.stm $+    If+      (BinOp "==" (Py.simpleCall "type" [e]) (Var "cl.array.Array"))+      pyOpenCLArrayCase+      numpyArrayCase unpackArrayInput _ sid _ _ _ _ =   error $ "Cannot accept array from " ++ sid ++ " space." 
src/Futhark/CodeGen/Backends/PyOpenCL/Boilerplate.hs view
@@ -1,25 +1,33 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-}+ -- | Various boilerplate definitions for the PyOpenCL backend. module Futhark.CodeGen.Backends.PyOpenCL.Boilerplate-  ( openClInit-  , openClPrelude-  ) where+  ( openClInit,+    openClPrelude,+  )+where  import Control.Monad.Identity import Data.FileEmbed-import qualified Data.Text as T import qualified Data.Map as M-import NeatInterpolation (text)-+import qualified Data.Text as T+import qualified Futhark.CodeGen.Backends.GenericPython as Py+import Futhark.CodeGen.Backends.GenericPython.AST import Futhark.CodeGen.ImpCode.OpenCL-  (PrimType(..), SizeClass(..), sizeDefault,-   FailureMsg(..), ErrorMsg(..), ErrorMsgPart(..), errorMsgArgTypes)+  ( ErrorMsg (..),+    ErrorMsgPart (..),+    FailureMsg (..),+    PrimType (..),+    SizeClass (..),+    errorMsgArgTypes,+    sizeDefault,+    untyped,+  ) import Futhark.CodeGen.OpenCL.Heuristics-import Futhark.CodeGen.Backends.GenericPython.AST-import qualified Futhark.CodeGen.Backends.GenericPython as Py import Futhark.Util.Pretty (prettyText)+import NeatInterpolation (text)  errorMsgNumArgs :: ErrorMsg a -> Int errorMsgNumArgs = length . errorMsgArgTypes@@ -32,7 +40,9 @@ -- @initiatialize_opencl_object@ procedure.  Should be put in the -- class constructor. openClInit :: [PrimType] -> String -> M.Map Name SizeClass -> [FailureMsg] -> String-openClInit types assign sizes failures = T.unpack [text|+openClInit types assign sizes failures =+  T.unpack+    [text| size_heuristics=$size_heuristics self.global_failure_args_max = $max_num_args self.failure_msgs=$failure_msgs@@ -52,51 +62,70 @@                                    all_sizes=$sizes') $assign' |]-  where assign' = T.pack assign-        size_heuristics = prettyText $ sizeHeuristicsToPython sizeHeuristicsTable-        types' = prettyText $ map (show . pretty) types -- Looks enough like Python.-        sizes' = prettyText $ sizeClassesToPython sizes-        max_num_args = prettyText $ foldl max 0 $ map (errorMsgNumArgs . failureError) failures-        failure_msgs = prettyText $ List $ map formatFailure failures+  where+    assign' = T.pack assign+    size_heuristics = prettyText $ sizeHeuristicsToPython sizeHeuristicsTable+    types' = prettyText $ map (show . pretty) types -- Looks enough like Python.+    sizes' = prettyText $ sizeClassesToPython sizes+    max_num_args = prettyText $ foldl max 0 $ map (errorMsgNumArgs . failureError) failures+    failure_msgs = prettyText $ List $ map formatFailure failures  formatFailure :: FailureMsg -> PyExp formatFailure (FailureMsg (ErrorMsg parts) backtrace) =   String $ concatMap onPart parts ++ "\n" ++ formatEscape backtrace-  where formatEscape = let escapeChar '{' = "{{"-                           escapeChar '}' = "}}"-                           escapeChar c = [c]-                       in concatMap escapeChar+  where+    formatEscape =+      let escapeChar '{' = "{{"+          escapeChar '}' = "}}"+          escapeChar c = [c]+       in concatMap escapeChar -        onPart (ErrorString s) = formatEscape s-        onPart ErrorInt32{} = "{}"+    onPart (ErrorString s) = formatEscape s+    onPart ErrorInt32 {} = "{}"+    onPart ErrorInt64 {} = "{}"  sizeClassesToPython :: M.Map Name SizeClass -> PyExp sizeClassesToPython = Dict . map f . M.toList-  where f (size_name, size_class) =-          (String $ pretty size_name,-           Dict [(String "class", String $ pretty size_class),-                 (String "value", maybe None (Integer . fromIntegral) $-                                  sizeDefault size_class)])+  where+    f (size_name, size_class) =+      ( String $ pretty size_name,+        Dict+          [ (String "class", String $ pretty size_class),+            ( String "value",+              maybe None (Integer . fromIntegral) $+                sizeDefault size_class+            )+          ]+      )  sizeHeuristicsToPython :: [SizeHeuristic] -> PyExp sizeHeuristicsToPython = List . map f-  where f (SizeHeuristic platform_name device_type which what) =-          Tuple [String platform_name,-                 clDeviceType device_type,-                 which',-                 what']--          where clDeviceType DeviceGPU = Var "cl.device_type.GPU"-                clDeviceType DeviceCPU = Var "cl.device_type.CPU"+  where+    f (SizeHeuristic platform_name device_type which what) =+      Tuple+        [ String platform_name,+          clDeviceType device_type,+          which',+          what'+        ]+      where+        clDeviceType DeviceGPU = Var "cl.device_type.GPU"+        clDeviceType DeviceCPU = Var "cl.device_type.CPU" -                which' = case which of LockstepWidth -> String "lockstep_width"-                                       NumGroups     -> String "num_groups"-                                       GroupSize     -> String "group_size"-                                       TileSize      -> String "tile_size"-                                       Threshold     -> String "threshold"+        which' = case which of+          LockstepWidth -> String "lockstep_width"+          NumGroups -> String "num_groups"+          GroupSize -> String "group_size"+          TileSize -> String "tile_size"+          Threshold -> String "threshold" -                what' = Lambda "device" $ runIdentity $ Py.compilePrimExp onLeaf what+        what' =+          Lambda "device" $+            runIdentity $+              Py.compilePrimExp onLeaf $ untyped what -                onLeaf (DeviceInfo s) =-                  pure $ Py.simpleCall "device.get_info"-                  [Py.simpleCall "getattr" [Var "cl.device_info", String s]]+        onLeaf (DeviceInfo s) =+          pure $+            Py.simpleCall+              "device.get_info"+              [Py.simpleCall "getattr" [Var "cl.device_info", String s]]
src/Futhark/CodeGen/Backends/SequentialC.hs view
@@ -1,76 +1,83 @@ {-# LANGUAGE QuasiQuotes #-}+ -- | C code generator.  This module can convert a correct ImpCode -- program to an equivalent C program. The C code is strictly -- sequential, but can handle the full Futhark language. module Futhark.CodeGen.Backends.SequentialC-  ( compileProg-  , GC.CParts(..)-  , GC.asLibrary-  , GC.asExecutable-  ) where+  ( compileProg,+    GC.CParts (..),+    GC.asLibrary,+    GC.asExecutable,+  )+where  import Control.Monad--import qualified Language.C.Quote.OpenCL as C--import Futhark.IR.SeqMem+import qualified Futhark.CodeGen.Backends.GenericC as GC import qualified Futhark.CodeGen.ImpCode.Sequential as Imp import qualified Futhark.CodeGen.ImpGen.Sequential as ImpGen-import qualified Futhark.CodeGen.Backends.GenericC as GC+import Futhark.IR.SeqMem import Futhark.MonadFreshNames+import qualified Language.C.Quote.OpenCL as C  -- | Compile the program to sequential C. compileProg :: MonadFreshNames m => Prog SeqMem -> m (ImpGen.Warnings, GC.CParts) compileProg =   traverse-  (GC.compileProg "c" operations generateContext "" [DefaultSpace] []) <=<-  ImpGen.compileProg-  where operations :: GC.Operations Imp.Sequential ()-        operations = GC.defaultOperations-                     { GC.opsCompiler = const $ return ()-                     , GC.opsCopy = copySequentialMemory-                     }+    (GC.compileProg "c" operations generateContext "" [DefaultSpace] [])+    <=< ImpGen.compileProg+  where+    operations :: GC.Operations Imp.Sequential ()+    operations =+      GC.defaultOperations+        { GC.opsCompiler = const $ return (),+          GC.opsCopy = copySequentialMemory+        } -        generateContext = do-          cfg <- GC.publicDef "context_config" GC.InitDecl $ \s ->-            ([C.cedecl|struct $id:s;|],-             [C.cedecl|struct $id:s { int debugging; };|])+    generateContext = do+      cfg <- GC.publicDef "context_config" GC.InitDecl $ \s ->+        ( [C.cedecl|struct $id:s;|],+          [C.cedecl|struct $id:s { int debugging; };|]+        ) -          GC.publicDef_ "context_config_new" GC.InitDecl $ \s ->-            ([C.cedecl|struct $id:cfg* $id:s(void);|],-             [C.cedecl|struct $id:cfg* $id:s(void) {+      GC.publicDef_ "context_config_new" GC.InitDecl $ \s ->+        ( [C.cedecl|struct $id:cfg* $id:s(void);|],+          [C.cedecl|struct $id:cfg* $id:s(void) {                                  struct $id:cfg *cfg = (struct $id:cfg*) malloc(sizeof(struct $id:cfg));                                  if (cfg == NULL) {                                    return NULL;                                  }                                  cfg->debugging = 0;                                  return cfg;-                               }|])+                               }|]+        ) -          GC.publicDef_ "context_config_free" GC.InitDecl $ \s ->-            ([C.cedecl|void $id:s(struct $id:cfg* cfg);|],-             [C.cedecl|void $id:s(struct $id:cfg* cfg) {+      GC.publicDef_ "context_config_free" GC.InitDecl $ \s ->+        ( [C.cedecl|void $id:s(struct $id:cfg* cfg);|],+          [C.cedecl|void $id:s(struct $id:cfg* cfg) {                                  free(cfg);-                               }|])+                               }|]+        ) -          GC.publicDef_ "context_config_set_debugging" GC.InitDecl $ \s ->-             ([C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],-              [C.cedecl|void $id:s(struct $id:cfg* cfg, int detail) {+      GC.publicDef_ "context_config_set_debugging" GC.InitDecl $ \s ->+        ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],+          [C.cedecl|void $id:s(struct $id:cfg* cfg, int detail) {                           cfg->debugging = detail;-                        }|])+                        }|]+        ) -          GC.publicDef_ "context_config_set_logging" GC.InitDecl $ \s ->-             ([C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],-              [C.cedecl|void $id:s(struct $id:cfg* cfg, int detail) {+      GC.publicDef_ "context_config_set_logging" GC.InitDecl $ \s ->+        ( [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|],+          [C.cedecl|void $id:s(struct $id:cfg* cfg, int detail) {                                  /* Does nothing for this backend. */                                  (void)cfg; (void)detail;-                               }|])+                               }|]+        ) -          (fields, init_fields) <- GC.contextContents+      (fields, init_fields) <- GC.contextContents -          ctx <- GC.publicDef "context" GC.InitDecl $ \s ->-            ([C.cedecl|struct $id:s;|],-             [C.cedecl|struct $id:s {+      ctx <- GC.publicDef "context" GC.InitDecl $ \s ->+        ( [C.cedecl|struct $id:s;|],+          [C.cedecl|struct $id:s {                           int detail_memory;                           int debugging;                           int profiling;@@ -78,11 +85,12 @@                           char *error;                           int profiling_paused;                           $sdecls:fields-                        };|])+                        };|]+        ) -          GC.publicDef_ "context_new" GC.InitDecl $ \s ->-            ([C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg);|],-             [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg) {+      GC.publicDef_ "context_new" GC.InitDecl $ \s ->+        ( [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg);|],+          [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg) {                                   struct $id:ctx* ctx = (struct $id:ctx*) malloc(sizeof(struct $id:ctx));                                   if (ctx == NULL) {                                     return NULL;@@ -95,30 +103,33 @@                                   $stms:init_fields                                   init_constants(ctx);                                   return ctx;-                               }|])+                               }|]+        ) -          GC.publicDef_ "context_free" GC.InitDecl $ \s ->-            ([C.cedecl|void $id:s(struct $id:ctx* ctx);|],-             [C.cedecl|void $id:s(struct $id:ctx* ctx) {+      GC.publicDef_ "context_free" GC.InitDecl $ \s ->+        ( [C.cedecl|void $id:s(struct $id:ctx* ctx);|],+          [C.cedecl|void $id:s(struct $id:ctx* ctx) {                                  free_constants(ctx);                                  free_lock(&ctx->lock);                                  free(ctx);-                               }|])+                               }|]+        ) -          GC.publicDef_ "context_sync" GC.MiscDecl $ \s ->-            ([C.cedecl|int $id:s(struct $id:ctx* ctx);|],-             [C.cedecl|int $id:s(struct $id:ctx* ctx) {+      GC.publicDef_ "context_sync" GC.MiscDecl $ \s ->+        ( [C.cedecl|int $id:s(struct $id:ctx* ctx);|],+          [C.cedecl|int $id:s(struct $id:ctx* ctx) {                                  (void)ctx;                                  return 0;-                               }|])+                               }|]+        ) -          GC.publicDef_ "context_clear_caches" GC.MiscDecl $ \s ->-            ([C.cedecl|int $id:s(struct $id:ctx* ctx);|],-             [C.cedecl|int $id:s(struct $id:ctx* ctx) {+      GC.publicDef_ "context_clear_caches" GC.MiscDecl $ \s ->+        ( [C.cedecl|int $id:s(struct $id:ctx* ctx);|],+          [C.cedecl|int $id:s(struct $id:ctx* ctx) {                                  (void)ctx;                                  return 0;-                               }|])-+                               }|]+        )  copySequentialMemory :: GC.Copy Imp.Sequential () copySequentialMemory destmem destidx DefaultSpace srcmem srcidx DefaultSpace nbytes =
src/Futhark/CodeGen/Backends/SequentialPython.hs view
@@ -1,37 +1,49 @@ module Futhark.CodeGen.Backends.SequentialPython-     ( compileProg-     ) where+  ( compileProg,+  )+where  import Control.Monad--import Futhark.IR.SeqMem-import qualified Futhark.CodeGen.ImpCode.Sequential as Imp-import qualified Futhark.CodeGen.ImpGen.Sequential as ImpGen import qualified Futhark.CodeGen.Backends.GenericPython as GenericPython-import Futhark.CodeGen.Backends.GenericPython.Definitions import Futhark.CodeGen.Backends.GenericPython.AST+import Futhark.CodeGen.Backends.GenericPython.Definitions+import qualified Futhark.CodeGen.ImpCode.Sequential as Imp+import qualified Futhark.CodeGen.ImpGen.Sequential as ImpGen+import Futhark.IR.SeqMem import Futhark.MonadFreshNames -compileProg :: MonadFreshNames m =>-               Maybe String -> Prog SeqMem -> m (ImpGen.Warnings, String)+compileProg ::+  MonadFreshNames m =>+  Maybe String ->+  Prog SeqMem ->+  m (ImpGen.Warnings, String) compileProg module_name =-  ImpGen.compileProg >=>-  traverse (GenericPython.compileProg-            module_name-            GenericPython.emptyConstructor-            imports-            defines-            operations () [] [])-  where imports = [Import "sys" Nothing,-                   Import "numpy" $ Just "np",-                   Import "ctypes" $ Just "ct",-                   Import "time" Nothing]-        defines = [Escape pyValues, Escape pyFunctions, Escape pyPanic, Escape pyTuning]-        operations :: GenericPython.Operations Imp.Sequential ()-        operations = GenericPython.defaultOperations-                     { GenericPython.opsCompiler = const $ return ()-                     , GenericPython.opsCopy = copySequentialMemory-                     }+  ImpGen.compileProg+    >=> traverse+      ( GenericPython.compileProg+          module_name+          GenericPython.emptyConstructor+          imports+          defines+          operations+          ()+          []+          []+      )+  where+    imports =+      [ Import "sys" Nothing,+        Import "numpy" $ Just "np",+        Import "ctypes" $ Just "ct",+        Import "time" Nothing+      ]+    defines = [Escape pyValues, Escape pyFunctions, Escape pyPanic, Escape pyTuning]+    operations :: GenericPython.Operations Imp.Sequential ()+    operations =+      GenericPython.defaultOperations+        { GenericPython.opsCompiler = const $ return (),+          GenericPython.opsCopy = copySequentialMemory+        }  copySequentialMemory :: GenericPython.Copy Imp.Sequential () copySequentialMemory destmem destidx DefaultSpace srcmem srcidx DefaultSpace nbytes _bt =
src/Futhark/CodeGen/Backends/SimpleRep.hs view
@@ -1,27 +1,29 @@ {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE Trustworthy #-}+ -- | Simple C runtime representation. module Futhark.CodeGen.Backends.SimpleRep-  ( tupleField-  , funName-  , defaultMemBlockType-  , primTypeToCType-  , signedPrimTypeToCType+  ( tupleField,+    funName,+    defaultMemBlockType,+    primTypeToCType,+    signedPrimTypeToCType,      -- * Primitive value operations-  , cIntOps-  , cFloat32Ops, cFloat32Funs-  , cFloat64Ops, cFloat64Funs-  , cFloatConvOps+    cIntOps,+    cFloat32Ops,+    cFloat32Funs,+    cFloat64Ops,+    cFloat64Funs,+    cFloatConvOps,   )-  where--import qualified Language.C.Syntax as C-import qualified Language.C.Quote.C as C+where  import Futhark.CodeGen.ImpCode-import Futhark.Util.Pretty (prettyOneLine) import Futhark.Util (zEncodeString)+import Futhark.Util.Pretty (prettyOneLine)+import qualified Language.C.Quote.C as C+import qualified Language.C.Syntax as C  -- | The C type corresponding to a signed integer type. intTypeToCType :: IntType -> C.Type@@ -64,7 +66,7 @@ -- | @funName f@ is the name of the C function corresponding to -- the Futhark function @f@. funName :: Name -> String-funName = ("futrts_"++) . zEncodeString . nameToString+funName = ("futrts_" ++) . zEncodeString . nameToString  funName' :: String -> String funName' = funName . nameFromString@@ -74,92 +76,120 @@ defaultMemBlockType = [C.cty|char*|]  cIntOps :: [C.Definition]-cIntOps = concatMap (`map` [minBound..maxBound]) ops-          ++ cIntPrimFuns-  where ops = [mkAdd, mkSub, mkMul,-               mkUDiv, mkUDivUp, mkUMod, mkUDivSafe, mkUDivUpSafe, mkUModSafe,-               mkSDiv, mkSDivUp, mkSMod, mkSDivSafe, mkSDivUpSafe, mkSModSafe,-               mkSQuot, mkSRem, mkSQuotSafe, mkSRemSafe,-               mkSMin, mkUMin,-               mkSMax, mkUMax,-               mkShl, mkLShr, mkAShr,-               mkAnd, mkOr, mkXor,-               mkUlt, mkUle,  mkSlt, mkSle,-               mkPow,-               mkIToB, mkBToI-              ] ++-              map mkSExt [minBound..maxBound] ++-              map mkZExt [minBound..maxBound]+cIntOps =+  concatMap (`map` [minBound .. maxBound]) ops+    ++ cIntPrimFuns+  where+    ops =+      [ mkAdd,+        mkSub,+        mkMul,+        mkUDiv,+        mkUDivUp,+        mkUMod,+        mkUDivSafe,+        mkUDivUpSafe,+        mkUModSafe,+        mkSDiv,+        mkSDivUp,+        mkSMod,+        mkSDivSafe,+        mkSDivUpSafe,+        mkSModSafe,+        mkSQuot,+        mkSRem,+        mkSQuotSafe,+        mkSRemSafe,+        mkSMin,+        mkUMin,+        mkSMax,+        mkUMax,+        mkShl,+        mkLShr,+        mkAShr,+        mkAnd,+        mkOr,+        mkXor,+        mkUlt,+        mkUle,+        mkSlt,+        mkSle,+        mkPow,+        mkIToB,+        mkBToI+      ]+        ++ map mkSExt [minBound .. maxBound]+        ++ map mkZExt [minBound .. maxBound] -        taggedI s Int8 = s ++ "8"-        taggedI s Int16 = s ++ "16"-        taggedI s Int32 = s ++ "32"-        taggedI s Int64 = s ++ "64"+    taggedI s Int8 = s ++ "8"+    taggedI s Int16 = s ++ "16"+    taggedI s Int32 = s ++ "32"+    taggedI s Int64 = s ++ "64" -        -- Use unsigned types for add/sub/mul so we can do-        -- well-defined overflow.-        mkAdd = simpleUintOp "add" [C.cexp|x + y|]-        mkSub = simpleUintOp "sub" [C.cexp|x - y|]-        mkMul = simpleUintOp "mul" [C.cexp|x * y|]-        mkUDiv = simpleUintOp "udiv" [C.cexp|x / y|]-        mkUDivUp = simpleUintOp "udiv_up" [C.cexp|(x+y-1) / y|]-        mkUMod = simpleUintOp "umod" [C.cexp|x % y|]-        mkUDivSafe = simpleUintOp "udiv_safe" [C.cexp|y == 0 ? 0 : x / y|]-        mkUDivUpSafe = simpleUintOp "udiv_up_safe" [C.cexp|y == 0 ? 0 : (x+y-1) / y|]-        mkUModSafe = simpleUintOp "umod_safe" [C.cexp|y == 0 ? 0 : x % y|]-        mkUMax = simpleUintOp "umax" [C.cexp|x < y ? y : x|]-        mkUMin = simpleUintOp "umin" [C.cexp|x < y ? x : y|]+    -- Use unsigned types for add/sub/mul so we can do+    -- well-defined overflow.+    mkAdd = simpleUintOp "add" [C.cexp|x + y|]+    mkSub = simpleUintOp "sub" [C.cexp|x - y|]+    mkMul = simpleUintOp "mul" [C.cexp|x * y|]+    mkUDiv = simpleUintOp "udiv" [C.cexp|x / y|]+    mkUDivUp = simpleUintOp "udiv_up" [C.cexp|(x+y-1) / y|]+    mkUMod = simpleUintOp "umod" [C.cexp|x % y|]+    mkUDivSafe = simpleUintOp "udiv_safe" [C.cexp|y == 0 ? 0 : x / y|]+    mkUDivUpSafe = simpleUintOp "udiv_up_safe" [C.cexp|y == 0 ? 0 : (x+y-1) / y|]+    mkUModSafe = simpleUintOp "umod_safe" [C.cexp|y == 0 ? 0 : x % y|]+    mkUMax = simpleUintOp "umax" [C.cexp|x < y ? y : x|]+    mkUMin = simpleUintOp "umin" [C.cexp|x < y ? x : y|] -        mkSDiv t =-          let ct = intTypeToCType t-          in [C.cedecl|static inline $ty:ct $id:(taggedI "sdiv" t)($ty:ct x, $ty:ct y) {+    mkSDiv t =+      let ct = intTypeToCType t+       in [C.cedecl|static inline $ty:ct $id:(taggedI "sdiv" t)($ty:ct x, $ty:ct y) {                          $ty:ct q = x / y;                          $ty:ct r = x % y;                          return q -                            (((r != 0) && ((r < 0) != (y < 0))) ? 1 : 0);              }|]-        mkSDivUp t =-          simpleIntOp "sdiv_up" [C.cexp|$id:(taggedI "sdiv" t)(x+y-1,y)|] t-        mkSMod t =-          let ct = intTypeToCType t-          in [C.cedecl|static inline $ty:ct $id:(taggedI "smod" t)($ty:ct x, $ty:ct y) {+    mkSDivUp t =+      simpleIntOp "sdiv_up" [C.cexp|$id:(taggedI "sdiv" t)(x+y-1,y)|] t+    mkSMod t =+      let ct = intTypeToCType t+       in [C.cedecl|static inline $ty:ct $id:(taggedI "smod" t)($ty:ct x, $ty:ct y) {                          $ty:ct r = x % y;                          return r +                            ((r == 0 || (x > 0 && y > 0) || (x < 0 && y < 0)) ? 0 : y);               }|]-        mkSDivSafe t =-          simpleIntOp "sdiv_safe" [C.cexp|y == 0 ? 0 : $id:(taggedI "sdiv" t)(x,y)|] t-        mkSDivUpSafe t =-          simpleIntOp "sdiv_up_safe" [C.cexp|$id:(taggedI "sdiv_safe" t)(x+y-1,y)|] t-        mkSModSafe t =-          simpleIntOp "smod_safe" [C.cexp|y == 0 ? 0 : $id:(taggedI "smod" t)(x,y)|] t+    mkSDivSafe t =+      simpleIntOp "sdiv_safe" [C.cexp|y == 0 ? 0 : $id:(taggedI "sdiv" t)(x,y)|] t+    mkSDivUpSafe t =+      simpleIntOp "sdiv_up_safe" [C.cexp|$id:(taggedI "sdiv_safe" t)(x+y-1,y)|] t+    mkSModSafe t =+      simpleIntOp "smod_safe" [C.cexp|y == 0 ? 0 : $id:(taggedI "smod" t)(x,y)|] t -        mkSQuot = simpleIntOp "squot" [C.cexp|x / y|]-        mkSRem = simpleIntOp "srem" [C.cexp|x % y|]-        mkSQuotSafe = simpleIntOp "squot_safe" [C.cexp|y == 0 ? 0 : x / y|]-        mkSRemSafe = simpleIntOp "srem_safe" [C.cexp|y == 0 ? 0 : x % y|]-        mkSMax = simpleIntOp "smax" [C.cexp|x < y ? y : x|]-        mkSMin = simpleIntOp "smin" [C.cexp|x < y ? x : y|]-        mkShl = simpleUintOp "shl" [C.cexp|x << y|]-        mkLShr = simpleUintOp "lshr" [C.cexp|x >> y|]-        mkAShr = simpleIntOp "ashr" [C.cexp|x >> y|]-        mkAnd = simpleUintOp "and" [C.cexp|x & y|]-        mkOr = simpleUintOp "or" [C.cexp|x | y|]-        mkXor = simpleUintOp "xor" [C.cexp|x ^ y|]-        mkUlt = uintCmpOp "ult" [C.cexp|x < y|]-        mkUle = uintCmpOp "ule" [C.cexp|x <= y|]-        mkSlt = intCmpOp "slt" [C.cexp|x < y|]-        mkSle = intCmpOp "sle" [C.cexp|x <= y|]+    mkSQuot = simpleIntOp "squot" [C.cexp|x / y|]+    mkSRem = simpleIntOp "srem" [C.cexp|x % y|]+    mkSQuotSafe = simpleIntOp "squot_safe" [C.cexp|y == 0 ? 0 : x / y|]+    mkSRemSafe = simpleIntOp "srem_safe" [C.cexp|y == 0 ? 0 : x % y|]+    mkSMax = simpleIntOp "smax" [C.cexp|x < y ? y : x|]+    mkSMin = simpleIntOp "smin" [C.cexp|x < y ? x : y|]+    mkShl = simpleUintOp "shl" [C.cexp|x << y|]+    mkLShr = simpleUintOp "lshr" [C.cexp|x >> y|]+    mkAShr = simpleIntOp "ashr" [C.cexp|x >> y|]+    mkAnd = simpleUintOp "and" [C.cexp|x & y|]+    mkOr = simpleUintOp "or" [C.cexp|x | y|]+    mkXor = simpleUintOp "xor" [C.cexp|x ^ y|]+    mkUlt = uintCmpOp "ult" [C.cexp|x < y|]+    mkUle = uintCmpOp "ule" [C.cexp|x <= y|]+    mkSlt = intCmpOp "slt" [C.cexp|x < y|]+    mkSle = intCmpOp "sle" [C.cexp|x <= y|] -        -- We define some operations as macros rather than functions,-        -- because this allows us to use them as constant expressions-        -- in things like array sizes and static initialisers.-        macro name rhs =-          [C.cedecl|$esc:("#define " ++ name ++ "(x) (" ++ prettyOneLine rhs ++ ")")|]+    -- We define some operations as macros rather than functions,+    -- because this allows us to use them as constant expressions+    -- in things like array sizes and static initialisers.+    macro name rhs =+      [C.cedecl|$esc:("#define " ++ name ++ "(x) (" ++ prettyOneLine rhs ++ ")")|] -        mkPow t =-          let ct = intTypeToCType t-          in [C.cedecl|static inline $ty:ct $id:(taggedI "pow" t)($ty:ct x, $ty:ct y) {+    mkPow t =+      let ct = intTypeToCType t+       in [C.cedecl|static inline $ty:ct $id:(taggedI "pow" t)($ty:ct x, $ty:ct y) {                          $ty:ct res = 1, rem = y;                          while (rem != 0) {                            if (rem & 1) {@@ -171,42 +201,50 @@                          return res;               }|] -        mkSExt from_t to_t = macro name [C.cexp|($ty:to_ct)(($ty:from_ct)x)|]-          where name = "sext_"++pretty from_t++"_"++pretty to_t-                from_ct = intTypeToCType from_t-                to_ct = intTypeToCType to_t+    mkSExt from_t to_t = macro name [C.cexp|($ty:to_ct)(($ty:from_ct)x)|]+      where+        name = "sext_" ++ pretty from_t ++ "_" ++ pretty to_t+        from_ct = intTypeToCType from_t+        to_ct = intTypeToCType to_t -        mkZExt from_t to_t = macro name [C.cexp|($ty:to_ct)(($ty:from_ct)x)|]-          where name = "zext_"++pretty from_t++"_"++pretty to_t-                from_ct = uintTypeToCType from_t-                to_ct = intTypeToCType to_t+    mkZExt from_t to_t = macro name [C.cexp|($ty:to_ct)(($ty:from_ct)x)|]+      where+        name = "zext_" ++ pretty from_t ++ "_" ++ pretty to_t+        from_ct = uintTypeToCType from_t+        to_ct = intTypeToCType to_t -        mkBToI to_t =-          [C.cedecl|static inline $ty:to_ct+    mkBToI to_t =+      [C.cedecl|static inline $ty:to_ct                     $id:name($ty:from_ct x) { return x; } |]-          where name = "btoi_bool_"++pretty to_t-                from_ct = primTypeToCType Bool-                to_ct = intTypeToCType to_t+      where+        name = "btoi_bool_" ++ pretty to_t+        from_ct = primTypeToCType Bool+        to_ct = intTypeToCType to_t -        mkIToB from_t =-          [C.cedecl|static inline $ty:to_ct+    mkIToB from_t =+      [C.cedecl|static inline $ty:to_ct                     $id:name($ty:from_ct x) { return x; } |]-          where name = "itob_"++pretty from_t++"_bool"-                to_ct = primTypeToCType Bool-                from_ct = intTypeToCType from_t+      where+        name = "itob_" ++ pretty from_t ++ "_bool"+        to_ct = primTypeToCType Bool+        from_ct = intTypeToCType from_t -        simpleUintOp s e t =-          [C.cedecl|static inline $ty:ct $id:(taggedI s t)($ty:ct x, $ty:ct y) { return $exp:e; }|]-            where ct = uintTypeToCType t-        simpleIntOp s e t =-          [C.cedecl|static inline $ty:ct $id:(taggedI s t)($ty:ct x, $ty:ct y) { return $exp:e; }|]-            where ct = intTypeToCType t-        intCmpOp s e t =-          [C.cedecl|static inline typename bool $id:(taggedI s t)($ty:ct x, $ty:ct y) { return $exp:e; }|]-            where ct = intTypeToCType t-        uintCmpOp s e t =-          [C.cedecl|static inline typename bool $id:(taggedI s t)($ty:ct x, $ty:ct y) { return $exp:e; }|]-            where ct = uintTypeToCType t+    simpleUintOp s e t =+      [C.cedecl|static inline $ty:ct $id:(taggedI s t)($ty:ct x, $ty:ct y) { return $exp:e; }|]+      where+        ct = uintTypeToCType t+    simpleIntOp s e t =+      [C.cedecl|static inline $ty:ct $id:(taggedI s t)($ty:ct x, $ty:ct y) { return $exp:e; }|]+      where+        ct = intTypeToCType t+    intCmpOp s e t =+      [C.cedecl|static inline typename bool $id:(taggedI s t)($ty:ct x, $ty:ct y) { return $exp:e; }|]+      where+        ct = intTypeToCType t+    uintCmpOp s e t =+      [C.cedecl|static inline typename bool $id:(taggedI s t)($ty:ct x, $ty:ct y) { return $exp:e; }|]+      where+        ct = uintTypeToCType t  cIntPrimFuns :: [C.Definition] cIntPrimFuns =@@ -479,56 +517,64 @@ cFloat64Ops :: [C.Definition] cFloatConvOps :: [C.Definition] (cFloat32Ops, cFloat64Ops, cFloatConvOps) =-  ( map ($Float32) mkOps-  , map ($Float64) mkOps-  , [ mkFPConvFF "fpconv" from to |-      from <- [minBound..maxBound],-      to <- [minBound..maxBound] ])-  where taggedF s Float32 = s ++ "32"-        taggedF s Float64 = s ++ "64"-        convOp s from to = s ++ "_" ++ pretty from ++ "_" ++ pretty to+  ( map ($ Float32) mkOps,+    map ($ Float64) mkOps,+    [ mkFPConvFF "fpconv" from to+      | from <- [minBound .. maxBound],+        to <- [minBound .. maxBound]+    ]+  )+  where+    taggedF s Float32 = s ++ "32"+    taggedF s Float64 = s ++ "64"+    convOp s from to = s ++ "_" ++ pretty from ++ "_" ++ pretty to -        mkOps = [mkFDiv, mkFAdd, mkFSub, mkFMul, mkFMin, mkFMax, mkPow, mkCmpLt, mkCmpLe] ++-                map (mkFPConvIF "sitofp") [minBound..maxBound] ++-                map (mkFPConvUF "uitofp") [minBound..maxBound] ++-                map (flip $ mkFPConvFI "fptosi") [minBound..maxBound] ++-                map (flip $ mkFPConvFU "fptoui") [minBound..maxBound]+    mkOps =+      [mkFDiv, mkFAdd, mkFSub, mkFMul, mkFMin, mkFMax, mkPow, mkCmpLt, mkCmpLe]+        ++ map (mkFPConvIF "sitofp") [minBound .. maxBound]+        ++ map (mkFPConvUF "uitofp") [minBound .. maxBound]+        ++ map (flip $ mkFPConvFI "fptosi") [minBound .. maxBound]+        ++ map (flip $ mkFPConvFU "fptoui") [minBound .. maxBound] -        mkFDiv = simpleFloatOp "fdiv" [C.cexp|x / y|]-        mkFAdd = simpleFloatOp "fadd" [C.cexp|x + y|]-        mkFSub = simpleFloatOp "fsub" [C.cexp|x - y|]-        mkFMul = simpleFloatOp "fmul" [C.cexp|x * y|]-        mkFMin = simpleFloatOp "fmin" [C.cexp|fmin(x, y)|]-        mkFMax = simpleFloatOp "fmax" [C.cexp|fmax(x, y)|]-        mkCmpLt = floatCmpOp "cmplt" [C.cexp|x < y|]-        mkCmpLe = floatCmpOp "cmple" [C.cexp|x <= y|]+    mkFDiv = simpleFloatOp "fdiv" [C.cexp|x / y|]+    mkFAdd = simpleFloatOp "fadd" [C.cexp|x + y|]+    mkFSub = simpleFloatOp "fsub" [C.cexp|x - y|]+    mkFMul = simpleFloatOp "fmul" [C.cexp|x * y|]+    mkFMin = simpleFloatOp "fmin" [C.cexp|fmin(x, y)|]+    mkFMax = simpleFloatOp "fmax" [C.cexp|fmax(x, y)|]+    mkCmpLt = floatCmpOp "cmplt" [C.cexp|x < y|]+    mkCmpLe = floatCmpOp "cmple" [C.cexp|x <= y|] -        mkPow Float32 =-          [C.cedecl|static inline float fpow32(float x, float y) { return pow(x, y); }|]-        mkPow Float64 =-          [C.cedecl|static inline double fpow64(double x, double y) { return pow(x, y); }|]+    mkPow Float32 =+      [C.cedecl|static inline float fpow32(float x, float y) { return pow(x, y); }|]+    mkPow Float64 =+      [C.cedecl|static inline double fpow64(double x, double y) { return pow(x, y); }|] -        mkFPConv from_f to_f s from_t to_t =-          [C.cedecl|static inline $ty:to_ct+    mkFPConv from_f to_f s from_t to_t =+      [C.cedecl|static inline $ty:to_ct                     $id:(convOp s from_t to_t)($ty:from_ct x) { return ($ty:to_ct)x;} |]-          where from_ct = from_f from_t-                to_ct = to_f to_t+      where+        from_ct = from_f from_t+        to_ct = to_f to_t -        mkFPConvFF = mkFPConv floatTypeToCType floatTypeToCType-        mkFPConvFI = mkFPConv floatTypeToCType intTypeToCType-        mkFPConvIF = mkFPConv intTypeToCType floatTypeToCType-        mkFPConvFU = mkFPConv floatTypeToCType uintTypeToCType-        mkFPConvUF = mkFPConv uintTypeToCType floatTypeToCType+    mkFPConvFF = mkFPConv floatTypeToCType floatTypeToCType+    mkFPConvFI = mkFPConv floatTypeToCType intTypeToCType+    mkFPConvIF = mkFPConv intTypeToCType floatTypeToCType+    mkFPConvFU = mkFPConv floatTypeToCType uintTypeToCType+    mkFPConvUF = mkFPConv uintTypeToCType floatTypeToCType -        simpleFloatOp s e t =-          [C.cedecl|static inline $ty:ct $id:(taggedF s t)($ty:ct x, $ty:ct y) { return $exp:e; }|]-            where ct = floatTypeToCType t-        floatCmpOp s e t =-          [C.cedecl|static inline typename bool $id:(taggedF s t)($ty:ct x, $ty:ct y) { return $exp:e; }|]-            where ct = floatTypeToCType t+    simpleFloatOp s e t =+      [C.cedecl|static inline $ty:ct $id:(taggedF s t)($ty:ct x, $ty:ct y) { return $exp:e; }|]+      where+        ct = floatTypeToCType t+    floatCmpOp s e t =+      [C.cedecl|static inline typename bool $id:(taggedF s t)($ty:ct x, $ty:ct y) { return $exp:e; }|]+      where+        ct = floatTypeToCType t  cFloat32Funs :: [C.Definition]-cFloat32Funs = [C.cunit|+cFloat32Funs =+  [C.cunit|     static inline float $id:(funName' "log32")(float x) {       return log(x);     }@@ -683,7 +729,8 @@ |]  cFloat64Funs :: [C.Definition]-cFloat64Funs = [C.cunit|+cFloat64Funs =+  [C.cunit|     static inline double $id:(funName' "log64")(double x) {       return log(x);     }
src/Futhark/CodeGen/ImpCode.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TupleSections #-} {-# LANGUAGE Safe #-} {-# LANGUAGE Strict #-}+{-# LANGUAGE TupleSections #-}+ -- | Imperative intermediate language used as a stepping stone in code generation. -- -- This is a generic representation parametrised on an extensible@@ -10,69 +11,74 @@ -- Originally inspired by the paper "Defunctionalizing Push Arrays" -- (FHPC '14). module Futhark.CodeGen.ImpCode-  ( Definitions (..)-  , Functions (..)-  , Function-  , FunctionT (..)-  , Constants (..)-  , ValueDesc (..)-  , Signedness (..)-  , ExternalValue (..)-  , Param (..)-  , paramName-  , SubExp(..)-  , MemSize-  , DimSize-  , Space (..)-  , SpaceId-  , Code (..)-  , PrimValue (..)-  , ExpLeaf (..)-  , Exp-  , Volatility (..)-  , Arg (..)-  , var-  , vi32-  , index-  , ErrorMsg(..)-  , ErrorMsgPart(..)-  , errorMsgArgTypes-  , ArrayContents(..)--  , lexicalMemoryUsage-  , calledFuncs+  ( Definitions (..),+    Functions (..),+    Function,+    FunctionT (..),+    Constants (..),+    ValueDesc (..),+    Signedness (..),+    ExternalValue (..),+    Param (..),+    paramName,+    SubExp (..),+    MemSize,+    DimSize,+    Space (..),+    SpaceId,+    Code (..),+    PrimValue (..),+    ExpLeaf (..),+    Exp,+    TExp,+    Volatility (..),+    Arg (..),+    var,+    vi32,+    vi64,+    index,+    ErrorMsg (..),+    ErrorMsgPart (..),+    errorMsgArgTypes,+    ArrayContents (..),+    lexicalMemoryUsage,+    calledFuncs,      -- * Typed enumerations-  , Bytes-  , Elements-  , elements-  , bytes-  , withElemType+    Bytes,+    Elements,+    elements,+    bytes,+    withElemType,      -- * Re-exports from other modules.-  , module Language.Futhark.Core-  , module Futhark.IR.Primitive-  , module Futhark.Analysis.PrimExp-  , module Futhark.IR.Kernels.Sizes-  , module Futhark.IR.Prop.Names+    module Language.Futhark.Core,+    module Futhark.IR.Primitive,+    module Futhark.Analysis.PrimExp,+    module Futhark.IR.Kernels.Sizes,+    module Futhark.IR.Prop.Names,   )-  where+where  import Data.List (intersperse)+import qualified Data.Map as M import qualified Data.Set as S import Data.Traversable-import qualified Data.Map as M--import Language.Futhark.Core+import Futhark.Analysis.PrimExp+import Futhark.IR.Kernels.Sizes (Count (..))+import Futhark.IR.Pretty () import Futhark.IR.Primitive-import Futhark.IR.Syntax-  (SubExp(..), Space(..), SpaceId,-   ErrorMsg(..), ErrorMsgPart(..), errorMsgArgTypes) import Futhark.IR.Prop.Names-import Futhark.IR.Pretty ()-import Futhark.Analysis.PrimExp+import Futhark.IR.Syntax+  ( ErrorMsg (..),+    ErrorMsgPart (..),+    Space (..),+    SpaceId,+    SubExp (..),+    errorMsgArgTypes,+  ) import Futhark.Util.Pretty hiding (space)-import Futhark.IR.Kernels.Sizes (Count(..))+import Language.Futhark.Core  -- | The size of a memory block. type MemSize = SubExp@@ -81,9 +87,10 @@ type DimSize = SubExp  -- | An ImpCode function parameter.-data Param = MemParam VName Space-           | ScalarParam VName PrimType-             deriving (Show)+data Param+  = MemParam VName Space+  | ScalarParam VName PrimType+  deriving (Show)  -- | The name of a parameter. paramName :: Param -> VName@@ -91,9 +98,10 @@ paramName (ScalarParam name _) = name  -- | A collection of imperative functions and constants.-data Definitions a = Definitions { defConsts :: Constants a-                                 , defFuns :: Functions a-                                 }+data Definitions a = Definitions+  { defConsts :: Constants a,+    defFuns :: Functions a+  }  -- | A collection of imperative functions. newtype Functions a = Functions [(Name, Function a)]@@ -106,51 +114,55 @@  -- | A collection of imperative constants. data Constants a = Constants-  { constsDecl :: [Param]-    -- ^ The constants that are made available to the functions.-  , constsInit :: Code a-    -- ^ Setting the value of the constants.  Note that this must not+  { -- | The constants that are made available to the functions.+    constsDecl :: [Param],+    -- | Setting the value of the constants.  Note that this must not     -- contain declarations of the names defined in 'constsDecl'.+    constsInit :: Code a   }  -- | Since the core language does not care for signedness, but the -- source language does, entry point input/output information has -- metadata for integer types (and arrays containing these) that -- indicate whether they are really unsigned integers.-data Signedness = TypeUnsigned-                | TypeDirect-                deriving (Eq, Show)+data Signedness+  = TypeUnsigned+  | TypeDirect+  deriving (Eq, Show)  -- | A description of an externally meaningful value.-data ValueDesc = ArrayValue VName Space PrimType Signedness [DimSize]-               -- ^ An array with memory block, memory block size,-               -- memory space, element type, signedness of element-               -- type (if applicable), and shape.-               | ScalarValue PrimType Signedness VName-               -- ^ A scalar value with signedness if applicable.-               deriving (Eq, Show)+data ValueDesc+  = -- | An array with memory block, memory block size,+    -- memory space, element type, signedness of element+    -- type (if applicable), and shape.+    ArrayValue VName Space PrimType Signedness [DimSize]+  | -- | A scalar value with signedness if applicable.+    ScalarValue PrimType Signedness VName+  deriving (Eq, Show)  -- | ^ An externally visible value.  This can be an opaque value -- (covering several physical internal values), or a single value that -- can be used externally.-data ExternalValue = OpaqueValue String [ValueDesc]-                     -- ^ The string is a human-readable description-                     -- with no other semantics.-                   | TransparentValue ValueDesc-                 deriving (Show)+data ExternalValue+  = -- | The string is a human-readable description+    -- with no other semantics.+    OpaqueValue String [ValueDesc]+  | TransparentValue ValueDesc+  deriving (Show)  -- | A imperative function, containing the body as well as its -- low-level inputs and outputs, as well as its high-level arguments -- and results.  The latter are only used if the function is an entry -- point.-data FunctionT a = Function { functionEntry :: Bool-                            , functionOutput :: [Param]-                            , functionInput :: [Param]-                            , functionBody :: Code a-                            , functionResult :: [ExternalValue]-                            , functionArgs :: [ExternalValue]-                            }-                 deriving (Show)+data FunctionT a = Function+  { functionEntry :: Bool,+    functionOutput :: [Param],+    functionInput :: [Param],+    functionBody :: Code a,+    functionResult :: [ExternalValue],+    functionArgs :: [ExternalValue]+  }+  deriving (Show)  -- | Type alias for namespace control. type Function = FunctionT@@ -158,100 +170,109 @@ -- | The contents of a statically declared constant array.  Such -- arrays are always unidimensional, and reshaped if necessary in the -- code that uses them.-data ArrayContents = ArrayValues [PrimValue]-                     -- ^ Precisely these values.-                   | ArrayZeros Int-                     -- ^ This many zeroes.-                     deriving (Show)+data ArrayContents+  = -- | Precisely these values.+    ArrayValues [PrimValue]+  | -- | This many zeroes.+    ArrayZeros Int+  deriving (Show)  -- | A block of imperative code.  Parameterised by an 'Op', which -- allows extensibility.  Concrete uses of this type will instantiate -- the type parameter with e.g. a construct for launching GPU kernels.-data Code a = Skip-              -- ^ No-op.  Crucial for the 'Monoid' instance.-            | Code a :>>: Code a-              -- ^ Statement composition.  Crucial for the 'Semigroup' instance.-            | For VName IntType Exp (Code a)-              -- ^ A for-loop iterating the given number of times.  The-              -- loop parameter starts counting from zero and will have-              -- the given type.  The bound is evaluated just once,-              -- before the loop is entered.-            | While Exp (Code a)-              -- ^ While loop.  The conditional is (of course)-              -- re-evaluated before every iteration of the loop.-            | DeclareMem VName Space-              -- ^ Declare a memory block variable that will point to-              -- memory in the given memory space.  Note that this is-              -- distinct from allocation.  The memory block must be the-              -- target of either an 'Allocate' or a 'SetMem' before it-              -- can be used for reading or writing.-            | DeclareScalar VName Volatility PrimType-              -- ^ Declare a scalar variable with an initially undefined value.-            | DeclareArray VName Space PrimType ArrayContents-              -- ^ Create an array containing the given values.  The-              -- lifetime of the array will be the entire application.-              -- This is mostly used for constant arrays, but also for-              -- some bookkeeping data, like the synchronisation-              -- counts used to implement reduction.-            | Allocate VName (Count Bytes Exp) Space-              -- ^ Memory space must match the corresponding-              -- 'DeclareMem'.-            | Free VName Space-              -- ^ Indicate that some memory block will never again be-              -- referenced via the indicated variable.  However, it-              -- may still be accessed through aliases.  It is only-              -- safe to actually deallocate the memory block if this-              -- is the last reference.  There is no guarantee that-              -- all memory blocks will be freed with this statement.-              -- Backends are free to ignore it entirely.-            | Copy VName (Count Bytes Exp) Space VName (Count Bytes Exp) Space (Count Bytes Exp)-              -- ^ Destination, offset in destination, destination-              -- space, source, offset in source, offset space, number-              -- of bytes.-            | Write VName (Count Elements Exp) PrimType Space Volatility Exp-              -- ^ @Write mem i t space vol v@ writes the value @v@ to-              -- @mem@ offset by @i@ elements of type @t@.  The-              -- 'Space' argument is the memory space of @mem@-              -- (technically redundant, but convenient).  Note that-              -- /reading/ is done with an 'Exp' ('Index').-            | SetScalar VName Exp-              -- ^ Set a scalar variable.-            | SetMem VName VName Space-              -- ^ Must be in same space.-            | Call [VName] Name [Arg]-              -- ^ Function call.  The results are written to the-              -- provided 'VName' variables.-            | If Exp (Code a) (Code a)-              -- ^ Conditional execution.-            | Assert Exp (ErrorMsg Exp) (SrcLoc, [SrcLoc])-              -- ^ Assert that something must be true.  Should it turn-              -- out not to be true, then report a failure along with-              -- the given error message.-            | Comment String (Code a)-              -- ^ Has the same semantics as the contained code, but-              -- the comment should show up in generated code for ease-              -- of inspection.-            | DebugPrint String (Maybe Exp)-              -- ^ Print the given value to the screen, somehow-              -- annotated with the given string as a description.  If-              -- no type/value pair, just print the string.  This has-              -- no semantic meaning, but is used entirely for-              -- debugging.  Code generators are free to ignore this-              -- statement.-            | Op a-              -- ^ Perform an extensible operation.-            deriving (Show)+data Code a+  = -- | No-op.  Crucial for the 'Monoid' instance.+    Skip+  | -- | Statement composition.  Crucial for the 'Semigroup' instance.+    Code a :>>: Code a+  | -- | A for-loop iterating the given number of times.+    -- The loop parameter starts counting from zero and will+    -- have the same (integer) type as the bound.  The bound+    -- is evaluated just once, before the loop is entered.+    For VName Exp (Code a)+  | -- | While loop.  The conditional is (of course)+    -- re-evaluated before every iteration of the loop.+    While (TExp Bool) (Code a)+  | -- | Declare a memory block variable that will point to+    -- memory in the given memory space.  Note that this is+    -- distinct from allocation.  The memory block must be the+    -- target of either an 'Allocate' or a 'SetMem' before it+    -- can be used for reading or writing.+    DeclareMem VName Space+  | -- | Declare a scalar variable with an initially undefined value.+    DeclareScalar VName Volatility PrimType+  | -- | Create an array containing the given values.  The+    -- lifetime of the array will be the entire application.+    -- This is mostly used for constant arrays, but also for+    -- some bookkeeping data, like the synchronisation+    -- counts used to implement reduction.+    DeclareArray VName Space PrimType ArrayContents+  | -- | Memory space must match the corresponding+    -- 'DeclareMem'.+    Allocate VName (Count Bytes (TExp Int64)) Space+  | -- | Indicate that some memory block will never again be+    -- referenced via the indicated variable.  However, it+    -- may still be accessed through aliases.  It is only+    -- safe to actually deallocate the memory block if this+    -- is the last reference.  There is no guarantee that+    -- all memory blocks will be freed with this statement.+    -- Backends are free to ignore it entirely.+    Free VName Space+  | -- | Destination, offset in destination, destination+    -- space, source, offset in source, offset space, number+    -- of bytes.+    Copy+      VName+      (Count Bytes (TExp Int64))+      Space+      VName+      (Count Bytes (TExp Int64))+      Space+      (Count Bytes (TExp Int64))+  | -- | @Write mem i t space vol v@ writes the value @v@ to+    -- @mem@ offset by @i@ elements of type @t@.  The+    -- 'Space' argument is the memory space of @mem@+    -- (technically redundant, but convenient).  Note that+    -- /reading/ is done with an 'Exp' ('Index').+    Write VName (Count Elements (TExp Int64)) PrimType Space Volatility Exp+  | -- | Set a scalar variable.+    SetScalar VName Exp+  | -- | Must be in same space.+    SetMem VName VName Space+  | -- | Function call.  The results are written to the+    -- provided 'VName' variables.+    Call [VName] Name [Arg]+  | -- | Conditional execution.+    If (TExp Bool) (Code a) (Code a)+  | -- | Assert that something must be true.  Should it turn+    -- out not to be true, then report a failure along with+    -- the given error message.+    Assert Exp (ErrorMsg Exp) (SrcLoc, [SrcLoc])+  | -- | Has the same semantics as the contained code, but+    -- the comment should show up in generated code for ease+    -- of inspection.+    Comment String (Code a)+  | -- | Print the given value to the screen, somehow+    -- annotated with the given string as a description.  If+    -- no type/value pair, just print the string.  This has+    -- no semantic meaning, but is used entirely for+    -- debugging.  Code generators are free to ignore this+    -- statement.+    DebugPrint String (Maybe Exp)+  | -- | Perform an extensible operation.+    Op a+  deriving (Show)  -- | The volatility of a memory access or variable.  Feel free to -- ignore this for backends where it makes no sense (anything but C -- and similar low-level things) data Volatility = Volatile | Nonvolatile-                deriving (Eq, Ord, Show)+  deriving (Eq, Ord, Show)  instance Semigroup (Code a) where-  Skip <> y    = y-  x    <> Skip = x-  x    <> y    = x :>>: y+  Skip <> y = y+  x <> Skip = x+  x <> y = x :>>: y  instance Monoid (Code a) where   mempty = Skip@@ -268,57 +289,64 @@ lexicalMemoryUsage :: Function a -> M.Map VName Space lexicalMemoryUsage func =   M.filterWithKey (const . not . (`nameIn` nonlexical)) $-  declared $ functionBody func-  where nonlexical =-          set (functionBody func) <>-          namesFromList (map paramName (functionOutput func))+    declared $ functionBody func+  where+    nonlexical =+      set (functionBody func)+        <> namesFromList (map paramName (functionOutput func)) -        go f (x :>>: y) = f x <> f y-        go f (If _ x y) = f x <> f y-        go f (For _ _ _ x) = f x-        go f (While _ x) = f x-        go f (Comment _ x) = f x-        go _ _ = mempty+    go f (x :>>: y) = f x <> f y+    go f (If _ x y) = f x <> f y+    go f (For _ _ x) = f x+    go f (While _ x) = f x+    go f (Comment _ x) = f x+    go _ _ = mempty -        declared (DeclareMem mem space) = M.singleton mem space-        declared x = go declared x+    declared (DeclareMem mem space) = M.singleton mem space+    declared x = go declared x -        set (SetMem x y _) = namesFromList [x,y]-        set (Call _ _ args) = foldMap onArg args-          where onArg ExpArg{} = mempty-                onArg (MemArg x) = oneName x-        set x = go set x+    set (SetMem x y _) = namesFromList [x, y]+    set (Call _ _ args) = foldMap onArg args+      where+        onArg ExpArg {} = mempty+        onArg (MemArg x) = oneName x+    set x = go set x  -- | The set of functions that are called by this code.  Assumes there -- are no function calls in 'Op's. calledFuncs :: Code a -> S.Set Name calledFuncs (x :>>: y) = calledFuncs x <> calledFuncs y calledFuncs (If _ x y) = calledFuncs x <> calledFuncs y-calledFuncs (For _ _ _ x) = calledFuncs x+calledFuncs (For _ _ x) = calledFuncs x calledFuncs (While _ x) = calledFuncs x calledFuncs (Comment _ x) = calledFuncs x calledFuncs (Call _ f _) = S.singleton f calledFuncs _ = mempty  -- | The leaves of an 'Exp'.-data ExpLeaf = ScalarVar VName-               -- ^ A scalar variable.  The type is stored in the-               -- 'LeafExp' constructor itself.-             | SizeOf PrimType-               -- ^ The size of a primitive type.-             | Index VName (Count Elements Exp) PrimType Space Volatility-               -- ^ Reading a value from memory.  The arguments have-               -- the same meaning as with 'Write'.-           deriving (Eq, Show)+data ExpLeaf+  = -- | A scalar variable.  The type is stored in the+    -- 'LeafExp' constructor itself.+    ScalarVar VName+  | -- | The size of a primitive type.+    SizeOf PrimType+  | -- | Reading a value from memory.  The arguments have+    -- the same meaning as with 'Write'.+    Index VName (Count Elements (TExp Int64)) PrimType Space Volatility+  deriving (Eq, Show)  -- | A side-effect free expression whose execution will produce a -- single primitive value. type Exp = PrimExp ExpLeaf +-- | Like 'Exp', but with a required/known type.+type TExp t = TPrimExp t ExpLeaf+ -- | A function call argument.-data Arg = ExpArg Exp-         | MemArg VName-         deriving (Show)+data Arg+  = ExpArg Exp+  | MemArg VName+  deriving (Show)  -- | Phantom type for a count of elements. data Elements@@ -327,29 +355,33 @@ data Bytes  -- | This expression counts elements.-elements :: Exp -> Count Elements Exp+elements :: a -> Count Elements a elements = Count  -- | This expression counts bytes.-bytes :: Exp -> Count Bytes Exp+bytes :: a -> Count Bytes a bytes = Count  -- | Convert a count of elements into a count of bytes, given the -- per-element size.-withElemType :: Count Elements Exp -> PrimType -> Count Bytes Exp+withElemType :: Count Elements (TExp Int64) -> PrimType -> Count Bytes (TExp Int64) withElemType (Count e) t =-  bytes $ sExt Int64 e * LeafExp (SizeOf t) (IntType Int64)+  bytes $ sExt64 e * isInt64 (LeafExp (SizeOf t) (IntType Int64))  -- | Turn a 'VName' into a 'Imp.ScalarVar'. var :: VName -> PrimType -> Exp var = LeafExp . ScalarVar  -- | Turn a 'VName' into a v'Int32' 'Imp.ScalarVar'.-vi32 :: VName -> Exp-vi32 = flip var $ IntType Int32+vi32 :: VName -> TExp Int32+vi32 = TPrimExp . flip var (IntType Int32) +-- | Turn a 'VName' into a v'Int64' 'Imp.ScalarVar'.+vi64 :: VName -> TExp Int64+vi64 = TPrimExp . flip var (IntType Int64)+ -- | Concise wrapper for using 'Index'.-index :: VName -> Count Elements Exp -> PrimType -> Space -> Volatility -> Exp+index :: VName -> Count Elements (TExp Int64) -> PrimType -> Space -> Volatility -> Exp index arr i t s vol = LeafExp (Index arr i t s vol) t  -- Prettyprinting definitions.@@ -360,25 +392,31 @@  instance Pretty op => Pretty (Functions op) where   ppr (Functions funs) = stack $ intersperse mempty $ map ppFun funs-    where ppFun (name, fun) =-            text "Function " <> ppr name <> colon </> indent 2 (ppr fun)+    where+      ppFun (name, fun) =+        text "Function " <> ppr name <> colon </> indent 2 (ppr fun)  instance Pretty op => Pretty (Constants op) where   ppr (Constants decls code) =-    text "Constants:" </> indent 2 (stack $ map ppr decls) </>-    mempty </>-    text "Initialisation:" </>-    indent 2 (ppr code)+    text "Constants:" </> indent 2 (stack $ map ppr decls)+      </> mempty+      </> text "Initialisation:"+      </> indent 2 (ppr code)  instance Pretty op => Pretty (FunctionT op) where   ppr (Function _ outs ins body results args) =-    text "Inputs:" </> block ins </>-    text "Outputs:" </> block outs </>-    text "Arguments:" </> block args </>-    text "Result:" </> block results </>-    text "Body:" </> indent 2 (ppr body)-    where block :: Pretty a => [a] -> Doc-          block = indent 2 . stack . map ppr+    text "Inputs:" </> block ins+      </> text "Outputs:"+      </> block outs+      </> text "Arguments:"+      </> block args+      </> text "Result:"+      </> block results+      </> text "Body:"+      </> indent 2 (ppr body)+    where+      block :: Pretty a => [a] -> Doc+      block = indent 2 . stack . map ppr  instance Pretty Param where   ppr (ScalarParam name ptype) = ppr ptype <+> ppr name@@ -387,20 +425,23 @@ instance Pretty ValueDesc where   ppr (ScalarValue t ept name) =     ppr t <+> ppr name <> ept'-    where ept' = case ept of TypeUnsigned -> text " (unsigned)"-                             TypeDirect   -> mempty+    where+      ept' = case ept of+        TypeUnsigned -> text " (unsigned)"+        TypeDirect -> mempty   ppr (ArrayValue mem space et ept shape) =     foldr f (ppr et) shape <+> text "at" <+> ppr mem <> ppr space <+> ept'-    where f e s = brackets $ s <> comma <> ppr e-          ept' = case ept of TypeUnsigned -> text " (unsigned)"-                             TypeDirect   -> mempty-+    where+      f e s = brackets $ s <> comma <> ppr e+      ept' = case ept of+        TypeUnsigned -> text " (unsigned)"+        TypeDirect -> mempty  instance Pretty ExternalValue where   ppr (TransparentValue v) = ppr v   ppr (OpaqueValue desc vs) =-    text "opaque" <+> text desc <+>-    nestedBlock "{" "}" (stack $ map ppr vs)+    text "opaque" <+> text desc+      <+> nestedBlock "{" "}" (stack $ map ppr vs)  instance Pretty ArrayContents where   ppr (ArrayValues vs) = braces (commasep $ map ppr vs)@@ -408,34 +449,40 @@  instance Pretty op => Pretty (Code op) where   ppr (Op op) = ppr op-  ppr Skip   = text "skip"+  ppr Skip = text "skip"   ppr (c1 :>>: c2) = ppr c1 </> ppr c2-  ppr (For i it limit body) =-    text "for" <+> ppr i <> text ":" <> ppr it <+> langle <+> ppr limit <+> text "{" </>-    indent 2 (ppr body) </>-    text "}"+  ppr (For i limit body) =+    text "for" <+> ppr i <+> langle <+> ppr limit <+> text "{"+      </> indent 2 (ppr body)+      </> text "}"   ppr (While cond body) =-    text "while" <+> ppr cond <+> text "{" </>-    indent 2 (ppr body) </>-    text "}"+    text "while" <+> ppr cond <+> text "{"+      </> indent 2 (ppr body)+      </> text "}"   ppr (DeclareMem name space) =     text "var" <+> ppr name <> text ": mem" <> ppr space   ppr (DeclareScalar name vol t) =     text "var" <+> ppr name <> text ":" <+> vol' <> ppr t-    where vol' = case vol of Volatile -> text "volatile "-                             Nonvolatile -> mempty+    where+      vol' = case vol of+        Volatile -> text "volatile "+        Nonvolatile -> mempty   ppr (DeclareArray name space t vs) =-    text "array" <+> ppr name <> text "@" <> ppr space <+> text ":" <+> ppr t <+>-    equals <+> ppr vs+    text "array" <+> ppr name <> text "@" <> ppr space <+> text ":" <+> ppr t+      <+> equals+      <+> ppr vs   ppr (Allocate name e space) =     ppr name <+> text "<-" <+> text "malloc" <> parens (ppr e) <> ppr space   ppr (Free name space) =     text "free" <> parens (ppr name) <> ppr space   ppr (Write name i bt space vol val) =-    ppr name <> langle <> vol' <> ppr bt <> ppr space <> rangle <> brackets (ppr i) <+>-    text "<-" <+> ppr val-    where vol' = case vol of Volatile -> text "volatile "-                             Nonvolatile -> mempty+    ppr name <> langle <> vol' <> ppr bt <> ppr space <> rangle <> brackets (ppr i)+      <+> text "<-"+      <+> ppr val+    where+      vol' = case vol of+        Volatile -> text "volatile "+        Nonvolatile -> mempty   ppr (SetScalar name val) =     ppr name <+> text "<-" <+> ppr val   ppr (SetMem dest from space) =@@ -443,21 +490,24 @@   ppr (Assert e msg _) =     text "assert" <> parens (commasep [ppr msg, ppr e])   ppr (Copy dest destoffset destspace src srcoffset srcspace size) =-    text "memcpy" <>-    parens (ppMemLoc dest destoffset <> ppr destspace <> comma </>-            ppMemLoc src srcoffset <> ppr srcspace <> comma </>-            ppr size)-    where ppMemLoc base offset =-            ppr base <+> text "+" <+> ppr offset+    text "memcpy"+      <> parens+        ( ppMemLoc dest destoffset <> ppr destspace <> comma+            </> ppMemLoc src srcoffset <> ppr srcspace <> comma+            </> ppr size+        )+    where+      ppMemLoc base offset =+        ppr base <+> text "+" <+> ppr offset   ppr (If cond tbranch fbranch) =-    text "if" <+> ppr cond <+> text "then {" </>-    indent 2 (ppr tbranch) </>-    text "} else {" </>-    indent 2 (ppr fbranch) </>-    text "}"+    text "if" <+> ppr cond <+> text "then {"+      </> indent 2 (ppr tbranch)+      </> text "} else {"+      </> indent 2 (ppr fbranch)+      </> text "}"   ppr (Call dests fname args) =-    commasep (map ppr dests) <+> text "<-" <+>-    ppr fname <> parens (commasep $ map ppr args)+    commasep (map ppr dests) <+> text "<-"+      <+> ppr fname <> parens (commasep $ map ppr args)   ppr (Comment s code) =     text "--" <+> text s </> ppr code   ppr (DebugPrint desc (Just e)) =@@ -474,9 +524,10 @@     ppr v   ppr (Index v is bt space vol) =     ppr v <> langle <> vol' <> ppr bt <> ppr space <> rangle <> brackets (ppr is)-    where vol' = case vol of Volatile -> text "volatile "-                             Nonvolatile -> mempty-+    where+      vol' = case vol of+        Volatile -> text "volatile "+        Nonvolatile -> mempty   ppr (SizeOf t) =     text "sizeof" <> parens (ppr t) @@ -489,7 +540,8 @@ instance Traversable Functions where   traverse f (Functions funs) =     Functions <$> traverse f' funs-    where f' (name, fun) = (name,) <$> traverse f fun+    where+      f' (name, fun) = (name,) <$> traverse f fun  instance Functor FunctionT where   fmap = fmapDefault@@ -510,8 +562,8 @@ instance Traversable Code where   traverse f (x :>>: y) =     (:>>:) <$> traverse f x <*> traverse f y-  traverse f (For i it bound code) =-    For i it bound <$> traverse f code+  traverse f (For i bound code) =+    For i bound <$> traverse f code   traverse f (While cond code) =     While cond <$> traverse f code   traverse f (If cond x y) =@@ -553,7 +605,7 @@ declaredIn (DeclareArray name _ _ _) = oneName name declaredIn (If _ t f) = declaredIn t <> declaredIn f declaredIn (x :>>: y) = declaredIn x <> declaredIn y-declaredIn (For i _ _ body) = oneName i <> declaredIn body+declaredIn (For i _ body) = oneName i <> declaredIn body declaredIn (While _ body) = declaredIn body declaredIn (Comment _ body) = declaredIn body declaredIn _ = mempty@@ -567,15 +619,15 @@     fvBind (declaredIn x) $ freeIn' x <> freeIn' y   freeIn' Skip =     mempty-  freeIn' (For i _ bound body) =+  freeIn' (For i bound body) =     fvBind (oneName i) $ freeIn' bound <> freeIn' body   freeIn' (While cond body) =     freeIn' cond <> freeIn' body   freeIn' (DeclareMem _ space) =     freeIn' space-  freeIn' DeclareScalar{} =+  freeIn' DeclareScalar {} =     mempty-  freeIn' DeclareArray{} =+  freeIn' DeclareArray {} =     mempty   freeIn' (Allocate name size space) =     freeIn' name <> freeIn' size <> freeIn' space
src/Futhark/CodeGen/ImpCode/Kernels.hs view
@@ -1,27 +1,28 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}+ -- | Variation of "Futhark.CodeGen.ImpCode" that contains the notion -- of a kernel invocation. module Futhark.CodeGen.ImpCode.Kernels-  ( Program-  , Function-  , FunctionT (Function)-  , Code-  , KernelCode-  , KernelConst (..)-  , KernelConstExp-  , HostOp (..)-  , KernelOp (..)-  , Fence (..)-  , AtomicOp (..)-  , Kernel (..)-  , KernelUse (..)-  , module Futhark.CodeGen.ImpCode-  , module Futhark.IR.Kernels.Sizes+  ( Program,+    Function,+    FunctionT (Function),+    Code,+    KernelCode,+    KernelConst (..),+    KernelConstExp,+    HostOp (..),+    KernelOp (..),+    Fence (..),+    AtomicOp (..),+    Kernel (..),+    KernelUse (..),+    module Futhark.CodeGen.ImpCode,+    module Futhark.IR.Kernels.Sizes,   )-  where+where -import Futhark.CodeGen.ImpCode hiding (Function, Code)+import Futhark.CodeGen.ImpCode hiding (Code, Function) import qualified Futhark.CodeGen.ImpCode as Imp import Futhark.IR.Kernels.Sizes import Futhark.IR.Pretty ()@@ -41,48 +42,47 @@  -- | A run-time constant related to kernels. newtype KernelConst = SizeConst Name-                    deriving (Eq, Ord, Show)+  deriving (Eq, Ord, Show)  -- | An expression whose variables are kernel constants. type KernelConstExp = PrimExp KernelConst  -- | An operation that runs on the host (CPU).-data HostOp = CallKernel Kernel-            | GetSize VName Name SizeClass-            | CmpSizeLe VName Name SizeClass Imp.Exp-            | GetSizeMax VName SizeClass-            deriving (Show)+data HostOp+  = CallKernel Kernel+  | GetSize VName Name SizeClass+  | CmpSizeLe VName Name SizeClass Imp.Exp+  | GetSizeMax VName SizeClass+  deriving (Show)  -- | A generic kernel containing arbitrary kernel code. data Kernel = Kernel-              { kernelBody :: Imp.Code KernelOp--              , kernelUses :: [KernelUse]-                -- ^ The host variables referenced by the kernel.--              , kernelNumGroups :: [Imp.Exp]-              , kernelGroupSize :: [Imp.Exp]-              , kernelName :: Name-               -- ^ A short descriptive and _unique_ name - should be-               -- alphanumeric and without spaces.--              , kernelFailureTolerant :: Bool-                -- ^ If true, this kernel does not need to check-                -- whether we are in a failing state, as it can cope.-                -- Intuitively, it means that the kernel does not-                -- depend on any non-scalar parameters to make control-                -- flow decisions.  Replication, transpose, and copy-                -- kernels are examples of this.-              }-            deriving (Show)+  { kernelBody :: Imp.Code KernelOp,+    -- | The host variables referenced by the kernel.+    kernelUses :: [KernelUse],+    kernelNumGroups :: [Imp.Exp],+    kernelGroupSize :: [Imp.Exp],+    -- | A short descriptive and _unique_ name - should be+    -- alphanumeric and without spaces.+    kernelName :: Name,+    -- | If true, this kernel does not need to check+    -- whether we are in a failing state, as it can cope.+    -- Intuitively, it means that the kernel does not+    -- depend on any non-scalar parameters to make control+    -- flow decisions.  Replication, transpose, and copy+    -- kernels are examples of this.+    kernelFailureTolerant :: Bool+  }+  deriving (Show)  -- | Information about a host-level variable that is used inside this -- kernel.  When generating the actual kernel code, this is used to -- deduce which parameters are needed.-data KernelUse = ScalarUse VName PrimType-               | MemoryUse VName-               | ConstUse VName KernelConstExp-                 deriving (Eq, Show)+data KernelUse+  = ScalarUse VName PrimType+  | MemoryUse VName+  | ConstUse VName KernelConstExp+  deriving (Eq, Show)  instance Pretty KernelConst where   ppr (SizeConst key) = text "get_size" <> parens (ppr key)@@ -97,14 +97,15 @@  instance Pretty HostOp where   ppr (GetSize dest key size_class) =-    ppr dest <+> text "<-" <+>-    text "get_size" <> parens (commasep [ppr key, ppr size_class])+    ppr dest <+> text "<-"+      <+> text "get_size" <> parens (commasep [ppr key, ppr size_class])   ppr (GetSizeMax dest size_class) =     ppr dest <+> text "<-" <+> text "get_size_max" <> parens (ppr size_class)   ppr (CmpSizeLe dest name size_class x) =-    ppr dest <+> text "<-" <+>-    text "get_size" <> parens (commasep [ppr name, ppr size_class]) <+>-    text "<" <+> ppr x+    ppr dest <+> text "<-"+      <+> text "get_size" <> parens (commasep [ppr name, ppr size_class])+      <+> text "<"+      <+> ppr x   ppr (CallKernel c) =     ppr c @@ -119,58 +120,63 @@     freeIn' dest  instance FreeIn Kernel where-  freeIn' kernel = freeIn' (kernelBody kernel) <>-                   freeIn' [kernelNumGroups kernel, kernelGroupSize kernel]+  freeIn' kernel =+    freeIn' (kernelBody kernel)+      <> freeIn' [kernelNumGroups kernel, kernelGroupSize kernel]  instance Pretty Kernel where   ppr kernel =-    text "kernel" <+> brace-    (text "groups" <+> brace (ppr $ kernelNumGroups kernel) </>-     text "group_size" <+> brace (ppr $ kernelGroupSize kernel) </>-     text "uses" <+> brace (commasep $ map ppr $ kernelUses kernel) </>-     text "failure_tolerant" <+> brace (ppr $ kernelFailureTolerant kernel) </>-     text "body" <+> brace (ppr $ kernelBody kernel))+    text "kernel"+      <+> brace+        ( text "groups" <+> brace (ppr $ kernelNumGroups kernel)+            </> text "group_size" <+> brace (ppr $ kernelGroupSize kernel)+            </> text "uses" <+> brace (commasep $ map ppr $ kernelUses kernel)+            </> text "failure_tolerant" <+> brace (ppr $ kernelFailureTolerant kernel)+            </> text "body" <+> brace (ppr $ kernelBody kernel)+        )  -- | When we do a barrier or fence, is it at the local or global -- level? data Fence = FenceLocal | FenceGlobal-           deriving (Show)+  deriving (Show)  -- | An operation that occurs within a kernel body.-data KernelOp = GetGroupId VName Int-              | GetLocalId VName Int-              | GetLocalSize VName Int-              | GetGlobalSize VName Int-              | GetGlobalId VName Int-              | GetLockstepWidth VName-              | Atomic Space AtomicOp-              | Barrier Fence-              | MemFence Fence-              | LocalAlloc VName (Count Bytes Imp.Exp)-              | ErrorSync Fence-                -- ^ Perform a barrier and also check whether any-                -- threads have failed an assertion.  Make sure all-                -- threads would reach all 'ErrorSync's if any of them-                -- do.  A failing assertion will jump to the next-                -- following 'ErrorSync', so make sure it's not inside-                -- control flow or similar.-              deriving (Show)+data KernelOp+  = GetGroupId VName Int+  | GetLocalId VName Int+  | GetLocalSize VName Int+  | GetGlobalSize VName Int+  | GetGlobalId VName Int+  | GetLockstepWidth VName+  | Atomic Space AtomicOp+  | Barrier Fence+  | MemFence Fence+  | LocalAlloc VName (Count Bytes (Imp.TExp Int64))+  | -- | Perform a barrier and also check whether any+    -- threads have failed an assertion.  Make sure all+    -- threads would reach all 'ErrorSync's if any of them+    -- do.  A failing assertion will jump to the next+    -- following 'ErrorSync', so make sure it's not inside+    -- control flow or similar.+    ErrorSync Fence+  deriving (Show)  -- | Atomic operations return the value stored before the update. -- This old value is stored in the first 'VName'.  The second 'VName' -- is the memory block to update.  The 'Exp' is the new value.-data AtomicOp = AtomicAdd IntType VName VName (Count Elements Imp.Exp) Exp-              | AtomicFAdd FloatType VName VName (Count Elements Imp.Exp) Exp-              | AtomicSMax IntType VName VName (Count Elements Imp.Exp) Exp-              | AtomicSMin IntType VName VName (Count Elements Imp.Exp) Exp-              | AtomicUMax IntType VName VName (Count Elements Imp.Exp) Exp-              | AtomicUMin IntType VName VName (Count Elements Imp.Exp) Exp-              | AtomicAnd IntType VName VName (Count Elements Imp.Exp) Exp-              | AtomicOr IntType VName VName (Count Elements Imp.Exp) Exp-              | AtomicXor IntType VName VName (Count Elements Imp.Exp) Exp-              | AtomicCmpXchg PrimType VName VName (Count Elements Imp.Exp) Exp Exp-              | AtomicXchg PrimType VName VName (Count Elements Imp.Exp) Exp-              deriving (Show)+data AtomicOp+  = AtomicAdd IntType VName VName (Count Elements (Imp.TExp Int64)) Exp+  | AtomicFAdd FloatType VName VName (Count Elements (Imp.TExp Int64)) Exp+  | AtomicSMax IntType VName VName (Count Elements (Imp.TExp Int64)) Exp+  | AtomicSMin IntType VName VName (Count Elements (Imp.TExp Int64)) Exp+  | AtomicUMax IntType VName VName (Count Elements (Imp.TExp Int64)) Exp+  | AtomicUMin IntType VName VName (Count Elements (Imp.TExp Int64)) Exp+  | AtomicAnd IntType VName VName (Count Elements (Imp.TExp Int64)) Exp+  | AtomicOr IntType VName VName (Count Elements (Imp.TExp Int64)) Exp+  | AtomicXor IntType VName VName (Count Elements (Imp.TExp Int64)) Exp+  | AtomicCmpXchg PrimType VName VName (Count Elements (Imp.TExp Int64)) Exp Exp+  | AtomicXchg PrimType VName VName (Count Elements (Imp.TExp Int64)) Exp+  deriving (Show)  instance FreeIn AtomicOp where   freeIn' (AtomicAdd _ _ arr i x) = freeIn' arr <> freeIn' i <> freeIn' x@@ -187,74 +193,74 @@  instance Pretty KernelOp where   ppr (GetGroupId dest i) =-    ppr dest <+>  "<-" <+>-     "get_group_id" <> parens (ppr i)+    ppr dest <+> "<-"+      <+> "get_group_id" <> parens (ppr i)   ppr (GetLocalId dest i) =-    ppr dest <+>  "<-" <+>-     "get_local_id" <> parens (ppr i)+    ppr dest <+> "<-"+      <+> "get_local_id" <> parens (ppr i)   ppr (GetLocalSize dest i) =-    ppr dest <+>  "<-" <+>-     "get_local_size" <> parens (ppr i)+    ppr dest <+> "<-"+      <+> "get_local_size" <> parens (ppr i)   ppr (GetGlobalSize dest i) =-    ppr dest <+>  "<-" <+>-     "get_global_size" <> parens (ppr i)+    ppr dest <+> "<-"+      <+> "get_global_size" <> parens (ppr i)   ppr (GetGlobalId dest i) =-    ppr dest <+>  "<-" <+>-     "get_global_id" <> parens (ppr i)+    ppr dest <+> "<-"+      <+> "get_global_id" <> parens (ppr i)   ppr (GetLockstepWidth dest) =-    ppr dest <+>  "<-" <+>-     "get_lockstep_width()"+    ppr dest <+> "<-"+      <+> "get_lockstep_width()"   ppr (Barrier FenceLocal) =-     "local_barrier()"+    "local_barrier()"   ppr (Barrier FenceGlobal) =-     "global_barrier()"+    "global_barrier()"   ppr (MemFence FenceLocal) =-     "mem_fence_local()"+    "mem_fence_local()"   ppr (MemFence FenceGlobal) =-     "mem_fence_global()"+    "mem_fence_global()"   ppr (LocalAlloc name size) =-    ppr name <+> equals <+>  "local_alloc" <> parens (ppr size)+    ppr name <+> equals <+> "local_alloc" <> parens (ppr size)   ppr (ErrorSync FenceLocal) =-     "error_sync_local()"+    "error_sync_local()"   ppr (ErrorSync FenceGlobal) =-     "error_sync_global()"+    "error_sync_global()"   ppr (Atomic _ (AtomicAdd t old arr ind x)) =-    ppr old <+>  "<-" <+>  "atomic_add_" <> ppr t <>-    parens (commasep [ppr arr <> brackets (ppr ind), ppr x])+    ppr old <+> "<-" <+> "atomic_add_" <> ppr t+      <> parens (commasep [ppr arr <> brackets (ppr ind), ppr x])   ppr (Atomic _ (AtomicFAdd t old arr ind x)) =-    ppr old <+>  "<-" <+>  "atomic_fadd_" <> ppr t <>-    parens (commasep [ppr arr <> brackets (ppr ind), ppr x])+    ppr old <+> "<-" <+> "atomic_fadd_" <> ppr t+      <> parens (commasep [ppr arr <> brackets (ppr ind), ppr x])   ppr (Atomic _ (AtomicSMax t old arr ind x)) =-    ppr old <+>  "<-" <+>  "atomic_smax" <> ppr t <>-    parens (commasep [ppr arr <> brackets (ppr ind), ppr x])+    ppr old <+> "<-" <+> "atomic_smax" <> ppr t+      <> parens (commasep [ppr arr <> brackets (ppr ind), ppr x])   ppr (Atomic _ (AtomicSMin t old arr ind x)) =-    ppr old <+>  "<-" <+>  "atomic_smin" <> ppr t <>-    parens (commasep [ppr arr <> brackets (ppr ind), ppr x])+    ppr old <+> "<-" <+> "atomic_smin" <> ppr t+      <> parens (commasep [ppr arr <> brackets (ppr ind), ppr x])   ppr (Atomic _ (AtomicUMax t old arr ind x)) =-    ppr old <+>  "<-" <+>  "atomic_umax" <> ppr t <>-    parens (commasep [ppr arr <> brackets (ppr ind), ppr x])+    ppr old <+> "<-" <+> "atomic_umax" <> ppr t+      <> parens (commasep [ppr arr <> brackets (ppr ind), ppr x])   ppr (Atomic _ (AtomicUMin t old arr ind x)) =-    ppr old <+>  "<-" <+>  "atomic_umin" <> ppr t <>-    parens (commasep [ppr arr <> brackets (ppr ind), ppr x])+    ppr old <+> "<-" <+> "atomic_umin" <> ppr t+      <> parens (commasep [ppr arr <> brackets (ppr ind), ppr x])   ppr (Atomic _ (AtomicAnd t old arr ind x)) =-    ppr old <+>  "<-" <+>  "atomic_and" <> ppr t <>-    parens (commasep [ppr arr <> brackets (ppr ind), ppr x])+    ppr old <+> "<-" <+> "atomic_and" <> ppr t+      <> parens (commasep [ppr arr <> brackets (ppr ind), ppr x])   ppr (Atomic _ (AtomicOr t old arr ind x)) =-    ppr old <+>  "<-" <+>  "atomic_or" <> ppr t <>-    parens (commasep [ppr arr <> brackets (ppr ind), ppr x])+    ppr old <+> "<-" <+> "atomic_or" <> ppr t+      <> parens (commasep [ppr arr <> brackets (ppr ind), ppr x])   ppr (Atomic _ (AtomicXor t old arr ind x)) =-    ppr old <+>  "<-" <+>  "atomic_xor" <> ppr t <>-    parens (commasep [ppr arr <> brackets (ppr ind), ppr x])+    ppr old <+> "<-" <+> "atomic_xor" <> ppr t+      <> parens (commasep [ppr arr <> brackets (ppr ind), ppr x])   ppr (Atomic _ (AtomicCmpXchg t old arr ind x y)) =-    ppr old <+>  "<-" <+>  "atomic_cmp_xchg" <> ppr t <>-    parens (commasep [ppr arr <> brackets (ppr ind), ppr x, ppr y])+    ppr old <+> "<-" <+> "atomic_cmp_xchg" <> ppr t+      <> parens (commasep [ppr arr <> brackets (ppr ind), ppr x, ppr y])   ppr (Atomic _ (AtomicXchg t old arr ind x)) =-    ppr old <+>  "<-" <+>  "atomic_xchg" <> ppr t <>-    parens (commasep [ppr arr <> brackets (ppr ind), ppr x])+    ppr old <+> "<-" <+> "atomic_xchg" <> ppr t+      <> parens (commasep [ppr arr <> brackets (ppr ind), ppr x])  instance FreeIn KernelOp where   freeIn' (Atomic _ op) = freeIn' op   freeIn' _ = mempty  brace :: Doc -> Doc-brace body =  " {" </> indent 2 body </>  "}"+brace body = " {" </> indent 2 body </> "}"
src/Futhark/CodeGen/ImpCode/OpenCL.hs view
@@ -7,49 +7,49 @@ -- The imperative code has been augmented with a 'LaunchKernel' -- operation that allows one to execute an OpenCL kernel. module Futhark.CodeGen.ImpCode.OpenCL-       ( Program (..)-       , Function-       , FunctionT (Function)-       , Code-       , KernelName-       , KernelArg (..)-       , OpenCL (..)-       , KernelSafety(..)-       , numFailureParams-       , KernelTarget (..)-       , FailureMsg(..)-       , module Futhark.CodeGen.ImpCode-       , module Futhark.IR.Kernels.Sizes-       )-       where+  ( Program (..),+    Function,+    FunctionT (Function),+    Code,+    KernelName,+    KernelArg (..),+    OpenCL (..),+    KernelSafety (..),+    numFailureParams,+    KernelTarget (..),+    FailureMsg (..),+    module Futhark.CodeGen.ImpCode,+    module Futhark.IR.Kernels.Sizes,+  )+where  import qualified Data.Map as M--import Futhark.CodeGen.ImpCode hiding (Function, Code)-import Futhark.IR.Kernels.Sizes+import Futhark.CodeGen.ImpCode hiding (Code, Function) import qualified Futhark.CodeGen.ImpCode as Imp-+import Futhark.IR.Kernels.Sizes import Futhark.Util.Pretty  -- | An program calling OpenCL kernels.-data Program = Program { openClProgram :: String-                       , openClPrelude :: String-                         -- ^ Must be prepended to the program.-                       , openClKernelNames :: M.Map KernelName KernelSafety-                       , openClUsedTypes :: [PrimType]-                         -- ^ So we can detect whether the device is capable.-                       , openClSizes :: M.Map Name SizeClass-                         -- ^ Runtime-configurable constants.-                       , openClFailures :: [FailureMsg]-                         -- ^ Assertion failure error messages.-                       , hostDefinitions :: Definitions OpenCL-                       }+data Program = Program+  { openClProgram :: String,+    -- | Must be prepended to the program.+    openClPrelude :: String,+    openClKernelNames :: M.Map KernelName KernelSafety,+    -- | So we can detect whether the device is capable.+    openClUsedTypes :: [PrimType],+    -- | Runtime-configurable constants.+    openClSizes :: M.Map Name SizeClass,+    -- | Assertion failure error messages.+    openClFailures :: [FailureMsg],+    hostDefinitions :: Definitions OpenCL+  }  -- | Something that can go wrong in a kernel.  Part of the machinery -- for reporting error messages from within kernels.-data FailureMsg = FailureMsg { failureError :: ErrorMsg Exp-                             , failureBacktrace :: String-                             }+data FailureMsg = FailureMsg+  { failureError :: ErrorMsg Exp,+    failureBacktrace :: String+  }  -- | A function calling OpenCL kernels. type Function = Imp.Function OpenCL@@ -61,31 +61,32 @@ type KernelName = Name  -- | An argument to be passed to a kernel.-data KernelArg = ValueKArg Exp PrimType-                 -- ^ Pass the value of this scalar expression as argument.-               | MemKArg VName-                 -- ^ Pass this pointer as argument.-               | SharedMemoryKArg (Count Bytes Exp)-                 -- ^ Create this much local memory per workgroup.-               deriving (Show)+data KernelArg+  = -- | Pass the value of this scalar expression as argument.+    ValueKArg Exp PrimType+  | -- | Pass this pointer as argument.+    MemKArg VName+  | -- | Create this much local memory per workgroup.+    SharedMemoryKArg (Count Bytes Exp)+  deriving (Show)  -- | Whether a kernel can potentially fail (because it contains bounds -- checks and such). data MayFail = MayFail | CannotFail-             deriving (Show)+  deriving (Show)  -- | Information about bounds checks and how sensitive it is to -- errors.  Ordered by least demanding to most. data KernelSafety-  = SafetyNone-    -- ^ Does not need to know if we are in a failing state, and also+  = -- | Does not need to know if we are in a failing state, and also     -- cannot fail.-  | SafetyCheap-    -- ^ Needs to be told if there's a global failure, and that's it,+    SafetyNone+  | -- | Needs to be told if there's a global failure, and that's it,     -- and cannot fail.-  | SafetyFull-    -- ^ Needs all parameters, may fail itself.-    deriving (Eq, Ord, Show)+    SafetyCheap+  | -- | Needs all parameters, may fail itself.+    SafetyFull+  deriving (Eq, Ord, Show)  -- | How many leading failure arguments we must pass when launching a -- kernel with these safety characteristics.@@ -95,16 +96,18 @@ numFailureParams SafetyFull = 3  -- | Host-level OpenCL operation.-data OpenCL = LaunchKernel KernelSafety KernelName [KernelArg] [Exp] [Exp]-            | GetSize VName Name-            | CmpSizeLe VName Name Exp-            | GetSizeMax VName SizeClass-            deriving (Show)+data OpenCL+  = LaunchKernel KernelSafety KernelName [KernelArg] [Exp] [Exp]+  | GetSize VName Name+  | CmpSizeLe VName Name Exp+  | GetSizeMax VName SizeClass+  deriving (Show)  -- | The target platform when compiling imperative code to a 'Program'-data KernelTarget = TargetOpenCL-                  | TargetCUDA-                  deriving (Eq)+data KernelTarget+  = TargetOpenCL+  | TargetCUDA+  deriving (Eq)  instance Pretty OpenCL where   ppr = text . show
src/Futhark/CodeGen/ImpCode/Sequential.hs view
@@ -1,17 +1,16 @@ -- | Sequential imperative code. module Futhark.CodeGen.ImpCode.Sequential-       ( Program-       , Function-       , FunctionT (Function)-       , Code-       , Sequential-       , module Futhark.CodeGen.ImpCode-       )-       where+  ( Program,+    Function,+    FunctionT (Function),+    Code,+    Sequential,+    module Futhark.CodeGen.ImpCode,+  )+where -import Futhark.CodeGen.ImpCode hiding (Function, Code)+import Futhark.CodeGen.ImpCode hiding (Code, Function) import qualified Futhark.CodeGen.ImpCode as Imp- import Futhark.Util.Pretty  -- | An imperative program.
src/Futhark/CodeGen/ImpGen.hs view
@@ -1,1418 +1,1736 @@-{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleContexts, LambdaCase, FlexibleInstances, MultiParamTypeClasses #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE Strict #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE Trustworthy #-}-module Futhark.CodeGen.ImpGen-  ( -- * Entry Points-    compileProg--    -- * Pluggable Compiler-  , OpCompiler-  , ExpCompiler-  , CopyCompiler-  , StmsCompiler-  , AllocCompiler-  , Operations (..)-  , defaultOperations-  , MemLocation (..)-  , MemEntry (..)-  , ScalarEntry (..)--    -- * Monadic Compiler Interface-  , ImpM-  , localDefaultSpace, askFunction-  , newVNameForFun, nameForFun-  , askEnv, localEnv-  , localOps-  , VTable-  , getVTable-  , localVTable-  , subImpM-  , subImpM_-  , emit-  , emitFunction-  , hasFunction-  , collect-  , collect'-  , comment-  , VarEntry (..)-  , ArrayEntry (..)--    -- * Lookups-  , lookupVar-  , lookupArray-  , lookupMemory--    -- * Building Blocks-  , ToExp(..)-  , compileAlloc-  , everythingVolatile-  , compileBody-  , compileBody'-  , compileLoopBody-  , defCompileStms-  , compileStms-  , compileExp-  , defCompileExp-  , fullyIndexArray-  , fullyIndexArray'-  , copy-  , copyDWIM-  , copyDWIMFix-  , copyElementWise-  , typeSize--  -- * Constructing code.-  , dLParams-  , dFParams-  , dScope-  , dArray-  , dPrim, dPrimVol_, dPrim_, dPrimV_, dPrimV, dPrimVE--  , sFor, sWhile-  , sComment-  , sIf, sWhen, sUnless-  , sOp-  , sDeclareMem, sAlloc, sAlloc_-  , sArray, sArrayInMem, sAllocArray, sAllocArrayPerm, sStaticArray-  , sWrite, sUpdate-  , sLoopNest-  , (<--)--  , function--  , warn-  , module Language.Futhark.Warnings-  )-  where--import Control.Monad.Reader-import Control.Monad.State-import Control.Monad.Writer-import Control.Parallel.Strategies-import Data.Bifunctor (first)-import qualified Data.DList as DL-import Data.Either-import qualified Data.Map.Strict as M-import qualified Data.Set as S-import Data.Maybe-import Data.List (find, sortOn, genericLength)--import qualified Futhark.CodeGen.ImpCode as Imp-import Futhark.CodeGen.ImpCode-  (Count, Bytes, Elements,-   bytes, elements, withElemType)-import Futhark.IR.Mem-import Futhark.IR.SOACS (SOACS)-import qualified Futhark.IR.Mem.IxFun as IxFun-import Futhark.Construct (fullSliceNum)-import Futhark.MonadFreshNames-import Futhark.Util-import Futhark.Util.Loc (noLoc)-import Language.Futhark.Warnings---- | How to compile an t'Op'.-type OpCompiler lore r op = Pattern lore -> Op lore -> ImpM lore r op ()---- | How to compile some 'Stms'.-type StmsCompiler lore r op = Names -> Stms lore -> ImpM lore r op () -> ImpM lore r op ()---- | How to compile an 'Exp'.-type ExpCompiler lore r op = Pattern lore -> Exp lore -> ImpM lore r op ()--type CopyCompiler lore r op = PrimType-                           -> MemLocation -> Slice Imp.Exp-                           -> MemLocation -> Slice Imp.Exp-                           -> ImpM lore r op ()---- | An alternate way of compiling an allocation.-type AllocCompiler lore r op = VName -> Count Bytes Imp.Exp -> ImpM lore r op ()--data Operations lore r op = Operations { opsExpCompiler :: ExpCompiler lore r op-                                     , opsOpCompiler :: OpCompiler lore r op-                                     , opsStmsCompiler :: StmsCompiler lore r op-                                     , opsCopyCompiler :: CopyCompiler lore r op-                                     , opsAllocCompilers :: M.Map Space (AllocCompiler lore r op)-                                     }---- | An operations set for which the expression compiler always--- returns 'defCompileExp'.-defaultOperations :: (Mem lore, FreeIn op) =>-                     OpCompiler lore r op -> Operations lore r op-defaultOperations opc = Operations { opsExpCompiler = defCompileExp-                                   , opsOpCompiler = opc-                                   , opsStmsCompiler = defCompileStms-                                   , opsCopyCompiler = defaultCopy-                                   , opsAllocCompilers = mempty-                                   }---- | When an array is dared, this is where it is stored.-data MemLocation = MemLocation { memLocationName :: VName-                               , memLocationShape :: [Imp.DimSize]-                               , memLocationIxFun :: IxFun.IxFun Imp.Exp-                               }-                   deriving (Eq, Show)--data ArrayEntry = ArrayEntry {-    entryArrayLocation :: MemLocation-  , entryArrayElemType :: PrimType-  }-  deriving (Show)--entryArrayShape :: ArrayEntry -> [Imp.DimSize]-entryArrayShape = memLocationShape . entryArrayLocation--newtype MemEntry = MemEntry { entryMemSpace :: Imp.Space }-  deriving (Show)--newtype ScalarEntry = ScalarEntry {-    entryScalarType    :: PrimType-  }-  deriving (Show)---- | Every non-scalar variable must be associated with an entry.-data VarEntry lore = ArrayVar (Maybe (Exp lore)) ArrayEntry-                   | ScalarVar (Maybe (Exp lore)) ScalarEntry-                   | MemVar (Maybe (Exp lore)) MemEntry-                   deriving (Show)---- | When compiling an expression, this is a description of where the--- result should end up.  The integer is a reference to the construct--- that gave rise to this destination (for patterns, this will be the--- tag of the first name in the pattern).  This can be used to make--- the generated code easier to relate to the original code.-data Destination = Destination { destinationTag :: Maybe Int-                               , valueDestinations :: [ValueDestination] }-                    deriving (Show)--data ValueDestination = ScalarDestination VName-                      | MemoryDestination VName-                      | ArrayDestination (Maybe MemLocation)-                        -- ^ The 'MemLocation' is 'Just' if a copy if-                        -- required.  If it is 'Nothing', then a-                        -- copy/assignment of a memory block somewhere-                        -- takes care of this array.-                      deriving (Show)--data Env lore r op = Env {-    envExpCompiler :: ExpCompiler lore r op-  , envStmsCompiler :: StmsCompiler lore r op-  , envOpCompiler :: OpCompiler lore r op-  , envCopyCompiler :: CopyCompiler lore r op-  , envAllocCompilers :: M.Map Space (AllocCompiler lore r op)-  , envDefaultSpace :: Imp.Space-  , envVolatility :: Imp.Volatility-  , envEnv :: r-    -- ^ User-extensible environment.-  , envFunction :: Maybe Name-    -- ^ Name of the function we are compiling, if any.-  , envAttrs :: Attrs-    -- ^ The set of attributes that are active on the enclosing-    -- statements (including the one we are currently compiling).-  }--newEnv :: r -> Operations lore r op -> Imp.Space -> Env lore r op-newEnv r ops ds =-  Env { envExpCompiler = opsExpCompiler ops-      , envStmsCompiler = opsStmsCompiler ops-      , envOpCompiler = opsOpCompiler ops-      , envCopyCompiler = opsCopyCompiler ops-      , envAllocCompilers = mempty-      , envDefaultSpace = ds-      , envVolatility = Imp.Nonvolatile-      , envEnv = r-      , envFunction = Nothing-      , envAttrs = mempty-      }---- | The symbol table used during compilation.-type VTable lore = M.Map VName (VarEntry lore)--data ImpState lore r op =-  ImpState { stateVTable :: VTable lore-           , stateFunctions :: Imp.Functions op-           , stateCode :: Imp.Code op-           , stateWarnings :: Warnings-           , stateNameSource :: VNameSource-           }--newState :: VNameSource -> ImpState lore r op-newState = ImpState mempty mempty mempty mempty--newtype ImpM lore r op a =-  ImpM (ReaderT (Env lore r op) (State (ImpState lore r op)) a)-  deriving (Functor, Applicative, Monad,-            MonadState (ImpState lore r op),-            MonadReader (Env lore r op))--instance MonadFreshNames (ImpM lore r op) where-  getNameSource = gets stateNameSource-  putNameSource src = modify $ \s -> s { stateNameSource = src }---- Cannot be an KernelsMem scope because the index functions have--- the wrong leaves (VName instead of Imp.Exp).-instance HasScope SOACS (ImpM lore r op) where-  askScope = gets $ M.map (LetName . entryType) . stateVTable-    where entryType (MemVar _ memEntry) =-            Mem (entryMemSpace memEntry)-          entryType (ArrayVar _ arrayEntry) =-            Array-            (entryArrayElemType arrayEntry)-            (Shape $ entryArrayShape arrayEntry)-            NoUniqueness-          entryType (ScalarVar _ scalarEntry) =-            Prim $ entryScalarType scalarEntry--runImpM :: ImpM lore r op a-        -> r -> Operations lore r op -> Imp.Space -> ImpState lore r op-        -> (a, ImpState lore r op)-runImpM (ImpM m) r ops space = runState (runReaderT m $ newEnv r ops space)--subImpM_ :: r' -> Operations lore r' op' -> ImpM lore r' op' a-         -> ImpM lore r op (Imp.Code op')-subImpM_ r ops m = snd <$> subImpM r ops m--subImpM :: r' -> Operations lore r' op' -> ImpM lore r' op' a-        -> ImpM lore r op (a, Imp.Code op')-subImpM r ops (ImpM m) = do-  env <- ask-  s <- get--  let env' = env { envExpCompiler = opsExpCompiler ops-                 , envStmsCompiler = opsStmsCompiler ops-                 , envCopyCompiler = opsCopyCompiler ops-                 , envOpCompiler = opsOpCompiler ops-                 , envAllocCompilers = opsAllocCompilers ops-                 , envEnv = r-                 }-      s' = ImpState { stateVTable = stateVTable s-                    , stateFunctions = mempty-                    , stateCode = mempty-                    , stateNameSource = stateNameSource s-                    , stateWarnings = mempty-                    }-      (x, s'') = runState (runReaderT m env') s'--  putNameSource $ stateNameSource s''-  warnings $ stateWarnings s''-  return (x, stateCode s'')---- | Execute a code generation action, returning the code that was--- emitted.-collect :: ImpM lore r op () -> ImpM lore r op (Imp.Code op)-collect = fmap snd . collect'--collect' :: ImpM lore r op a -> ImpM lore r op (a, Imp.Code op)-collect' m = do-  prev_code <- gets stateCode-  modify $ \s -> s { stateCode = mempty }-  x <- m-  new_code <- gets stateCode-  modify $ \s -> s { stateCode = prev_code }-  return (x, new_code)---- | Execute a code generation action, wrapping the generated code--- within a 'Imp.Comment' with the given description.-comment :: String -> ImpM lore r op () -> ImpM lore r op ()-comment desc m = do code <- collect m-                    emit $ Imp.Comment desc code---- | Emit some generated imperative code.-emit :: Imp.Code op -> ImpM lore r op ()-emit code = modify $ \s -> s { stateCode = stateCode s <> code }--warnings :: Warnings -> ImpM lore r op ()-warnings ws = modify $ \s -> s { stateWarnings = ws <> stateWarnings s}---- | Emit a warning about something the user should be aware of.-warn :: Located loc => loc -> [loc] -> String -> ImpM lore r op ()-warn loc locs problem =-  warnings $ singleWarning' (srclocOf loc) (map srclocOf locs) problem---- | Emit a function in the generated code.-emitFunction :: Name -> Imp.Function op -> ImpM lore r op ()-emitFunction fname fun = do-  Imp.Functions fs <- gets stateFunctions-  modify $ \s -> s { stateFunctions = Imp.Functions $ (fname,fun) : fs }---- | Check if a function of a given name exists.-hasFunction :: Name -> ImpM lore r op Bool-hasFunction fname = gets $ \s -> let Imp.Functions fs = stateFunctions s-                                 in isJust $ lookup fname fs--constsVTable :: Mem lore => Stms lore -> VTable lore-constsVTable = foldMap stmVtable-  where stmVtable (Let pat _ e) =-          foldMap (peVtable e) $ patternElements pat-        peVtable e (PatElem name dec) =-          M.singleton name $ memBoundToVarEntry (Just e) dec--compileProg :: (Mem lore, FreeIn op, MonadFreshNames m) =>-               r -> Operations lore r op -> Imp.Space-            -> Prog lore -> m (Warnings, Imp.Definitions op)-compileProg r ops space (Prog consts funs) =-  modifyNameSource $ \src ->-    let (_, ss) =-          unzip $ parMap rpar (compileFunDef' src) funs-        free_in_funs =-          freeIn $ mconcat $ map stateFunctions ss-        (consts', s') =-          runImpM (compileConsts free_in_funs consts) r ops space $-          combineStates ss-  in ((stateWarnings s',-       Imp.Definitions consts' (stateFunctions s')),-      stateNameSource s')-  where compileFunDef' src fdef =-          runImpM (compileFunDef fdef) r ops space-          (newState src) { stateVTable = constsVTable consts }--        combineStates ss =-          let Imp.Functions funs' = mconcat $ map stateFunctions ss-              src = mconcat (map stateNameSource ss)-          in (newState src) { stateFunctions =-                                Imp.Functions $ M.toList $ M.fromList funs'-                            , stateWarnings =-                                mconcat $ map stateWarnings ss-                            }--compileConsts :: Names -> Stms lore -> ImpM lore r op (Imp.Constants op)-compileConsts used_consts stms = do-  code <- collect $ compileStms used_consts stms $ pure ()-  pure $ uncurry Imp.Constants $ first DL.toList $ extract code-  where -- Fish out those top-level declarations in the constant-        -- initialisation code that are free in the functions.-        extract (x Imp.:>>: y) =-          extract x <> extract y-        extract (Imp.DeclareMem name space)-          | name `nameIn` used_consts =-              (DL.singleton $ Imp.MemParam name space,-               mempty)-        extract (Imp.DeclareScalar name _ t)-          | name `nameIn` used_consts =-              (DL.singleton $ Imp.ScalarParam name t,-               mempty)-        extract s =-          (mempty, s)--compileInParam :: Mem lore =>-                  FParam lore -> ImpM lore r op (Either Imp.Param ArrayDecl)-compileInParam fparam = case paramDec fparam of-  MemPrim bt ->-    return $ Left $ Imp.ScalarParam name bt-  MemMem space ->-    return $ Left $ Imp.MemParam name space-  MemArray bt shape _ (ArrayIn mem ixfun) ->-    return $ Right $ ArrayDecl name bt $-    MemLocation mem (shapeDims shape) $ fmap (toExp' int32) ixfun-  where name = paramName fparam--data ArrayDecl = ArrayDecl VName PrimType MemLocation--fparamSizes :: Typed dec => Param dec -> S.Set VName-fparamSizes = S.fromList . subExpVars . arrayDims . paramType--compileInParams :: Mem lore =>-                   [FParam lore] -> [EntryPointType]-                -> ImpM lore r op ([Imp.Param], [ArrayDecl], [Imp.ExternalValue])-compileInParams params orig_epts = do-  let (ctx_params, val_params) =-        splitAt (length params - sum (map entryPointSize orig_epts)) params-  (inparams, arrayds) <- partitionEithers <$> mapM compileInParam (ctx_params++val_params)-  let findArray x = find (isArrayDecl x) arrayds-      sizes = mconcat $ map fparamSizes $ ctx_params++val_params--      summaries = M.fromList $ mapMaybe memSummary params-        where memSummary param-                | MemMem space <- paramDec param =-                    Just (paramName param, space)-                | otherwise =-                    Nothing--      findMemInfo :: VName -> Maybe Space-      findMemInfo = flip M.lookup summaries--      mkValueDesc fparam signedness =-        case (findArray $ paramName fparam, paramType fparam) of-          (Just (ArrayDecl _ bt (MemLocation mem shape _)), _) -> do-            memspace <- findMemInfo mem-            Just $ Imp.ArrayValue mem memspace bt signedness shape-          (_, Prim bt)-            | paramName fparam `S.member` sizes ->-              Nothing-            | otherwise ->-              Just $ Imp.ScalarValue bt signedness $ paramName fparam-          _ ->-            Nothing--      mkExts (TypeOpaque desc n:epts) fparams =-        let (fparams',rest) = splitAt n fparams-        in Imp.OpaqueValue desc-           (mapMaybe (`mkValueDesc` Imp.TypeDirect) fparams') :-           mkExts epts rest-      mkExts (TypeUnsigned:epts) (fparam:fparams) =-        maybeToList (Imp.TransparentValue <$> mkValueDesc fparam Imp.TypeUnsigned) ++-        mkExts epts fparams-      mkExts (TypeDirect:epts) (fparam:fparams) =-        maybeToList (Imp.TransparentValue <$> mkValueDesc fparam Imp.TypeDirect) ++-        mkExts epts fparams-      mkExts _ _ = []--  return (inparams, arrayds, mkExts orig_epts val_params)-  where isArrayDecl x (ArrayDecl y _ _) = x == y--compileOutParams :: Mem lore =>-                    [RetType lore] -> [EntryPointType]-                 -> ImpM lore r op ([Imp.ExternalValue], [Imp.Param], Destination)-compileOutParams orig_rts orig_epts = do-  ((extvs, dests), (outparams,ctx_dests)) <--    runWriterT $ evalStateT (mkExts orig_epts orig_rts) (M.empty, M.empty)-  let ctx_dests' = map snd $ sortOn fst $ M.toList ctx_dests-  return (extvs, outparams, Destination Nothing $ ctx_dests' <> dests)-  where imp = lift . lift--        mkExts (TypeOpaque desc n:epts) rts = do-          let (rts',rest) = splitAt n rts-          (evs, dests) <- unzip <$> zipWithM mkParam rts' (repeat Imp.TypeDirect)-          (more_values, more_dests) <- mkExts epts rest-          return (Imp.OpaqueValue desc evs : more_values,-                  dests ++ more_dests)-        mkExts (TypeUnsigned:epts) (rt:rts) = do-          (ev,dest) <- mkParam rt Imp.TypeUnsigned-          (more_values, more_dests) <- mkExts epts rts-          return (Imp.TransparentValue ev : more_values,-                  dest : more_dests)-        mkExts (TypeDirect:epts) (rt:rts) = do-          (ev,dest) <- mkParam rt Imp.TypeDirect-          (more_values, more_dests) <- mkExts epts rts-          return (Imp.TransparentValue ev : more_values,-                  dest : more_dests)-        mkExts _ _ = return ([], [])--        mkParam MemMem{} _ =-          error "Functions may not explicitly return memory blocks."-        mkParam (MemPrim t) ept = do-          out <- imp $ newVName "scalar_out"-          tell ([Imp.ScalarParam out t], mempty)-          return (Imp.ScalarValue t ept out, ScalarDestination out)-        mkParam (MemArray t shape _ dec) ept = do-          space <- asks envDefaultSpace-          memout <- case dec of-            ReturnsNewBlock _ x _ixfun -> do-              memout <- imp $ newVName "out_mem"-              tell ([Imp.MemParam memout space],-                    M.singleton x $ MemoryDestination memout)-              return memout-            ReturnsInBlock memout _ ->-              return memout-          resultshape <- mapM inspectExtSize $ shapeDims shape-          return (Imp.ArrayValue memout space t ept resultshape,-                  ArrayDestination Nothing)--        inspectExtSize (Ext x) = do-          (memseen,arrseen) <- get-          case M.lookup x arrseen of-            Nothing -> do-              out <- imp $ newVName "out_arrsize"-              tell ([Imp.ScalarParam out int32],-                    M.singleton x $ ScalarDestination out)-              put (memseen, M.insert x out arrseen)-              return $ Var out-            Just out ->-              return $ Var out-        inspectExtSize (Free se) =-          return se--compileFunDef :: Mem lore =>-                 FunDef lore-              -> ImpM lore r op ()-compileFunDef (FunDef entry _ fname rettype params body) =-  local (\env -> env { envFunction = Just fname }) $ do-  ((outparams, inparams, results, args), body') <- collect' compile-  emitFunction fname $ Imp.Function (isJust entry) outparams inparams body' results args-  where params_entry = maybe (replicate (length params) TypeDirect) fst entry-        ret_entry = maybe (replicate (length rettype) TypeDirect) snd entry-        compile = do-          (inparams, arrayds, args) <- compileInParams params params_entry-          (results, outparams, Destination _ dests) <- compileOutParams rettype ret_entry-          addFParams params-          addArrays arrayds--          let Body _ stms ses = body-          compileStms (freeIn ses) stms $-            forM_ (zip dests ses) $ \(d, se) -> copyDWIMDest d [] se []--          return (outparams, inparams, results, args)--compileBody :: (Mem lore) => Pattern lore -> Body lore -> ImpM lore r op ()-compileBody pat (Body _ bnds ses) = do-  Destination _ dests <- destinationFromPattern pat-  compileStms (freeIn ses) bnds $-    forM_ (zip dests ses) $ \(d, se) -> copyDWIMDest d [] se []--compileBody' :: [Param dec] -> Body lore -> ImpM lore r op ()-compileBody' params (Body _ bnds ses) =-  compileStms (freeIn ses) bnds $-    forM_ (zip params ses) $ \(param, se) -> copyDWIM (paramName param) [] se []--compileLoopBody :: Typed dec => [Param dec] -> Body lore -> ImpM lore r op ()-compileLoopBody mergeparams (Body _ bnds ses) = do-  -- We cannot write the results to the merge parameters immediately,-  -- as some of the results may actually *be* merge parameters, and-  -- would thus be clobbered.  Therefore, we first copy to new-  -- variables mirroring the merge parameters, and then copy this-  -- buffer to the merge parameters.  This is efficient, because the-  -- operations are all scalar operations.-  tmpnames <- mapM (newVName . (++"_tmp") . baseString . paramName) mergeparams-  compileStms (freeIn ses) bnds $ do-    copy_to_merge_params <- forM (zip3 mergeparams tmpnames ses) $ \(p,tmp,se) ->-      case typeOf p of-        Prim pt  -> do-          emit $ Imp.DeclareScalar tmp Imp.Nonvolatile pt-          emit $ Imp.SetScalar tmp $ toExp' pt se-          return $ emit $ Imp.SetScalar (paramName p) $ Imp.var tmp pt-        Mem space | Var v <- se -> do-          emit $ Imp.DeclareMem tmp space-          emit $ Imp.SetMem tmp v space-          return $ emit $ Imp.SetMem (paramName p) tmp space-        _ -> return $ return ()-    sequence_ copy_to_merge_params--compileStms :: Names -> Stms lore -> ImpM lore r op () -> ImpM lore r op ()-compileStms alive_after_stms all_stms m = do-  cb <- asks envStmsCompiler-  cb alive_after_stms all_stms m--defCompileStms :: (Mem lore, FreeIn op) =>-                  Names -> Stms lore -> ImpM lore r op () -> ImpM lore r op ()-defCompileStms alive_after_stms all_stms m =-  -- We keep track of any memory blocks produced by the statements,-  -- and after the last time that memory block is used, we insert a-  -- Free.  This is very conservative, but can cut down on lifetimes-  -- in some cases.-  void $ compileStms' mempty $ stmsToList all_stms-  where compileStms' allocs (Let pat aux e:bs) = do-          dVars (Just e) (patternElements pat)--          e_code <- localAttrs (stmAuxAttrs aux) $-                    collect $ compileExp pat e-          (live_after, bs_code) <- collect' $ compileStms' (patternAllocs pat <> allocs) bs-          let dies_here v = not (v `nameIn` live_after) &&-                            v `nameIn` freeIn e_code-              to_free = S.filter (dies_here . fst) allocs--          emit e_code-          mapM_ (emit . uncurry Imp.Free) to_free-          emit bs_code--          return $ freeIn e_code <> live_after-        compileStms' _ [] = do-          code <- collect m-          emit code-          return $ freeIn code <> alive_after_stms--        patternAllocs = S.fromList . mapMaybe isMemPatElem . patternElements-        isMemPatElem pe = case patElemType pe of-                            Mem space -> Just (patElemName pe, space)-                            _         -> Nothing--compileExp :: Pattern lore -> Exp lore -> ImpM lore r op ()-compileExp pat e = do-  ec <- asks envExpCompiler-  ec pat e--defCompileExp :: (Mem lore) =>-                 Pattern lore -> Exp lore -> ImpM lore r op ()--defCompileExp pat (If cond tbranch fbranch _) = do-  tcode <- collect $ compileBody pat tbranch-  fcode <- collect $ compileBody pat fbranch-  emit $ Imp.If (toExp' Bool cond) tcode fcode--defCompileExp pat (Apply fname args _ _) = do-  dest <- destinationFromPattern pat-  targets <- funcallTargets dest-  args' <- catMaybes <$> mapM compileArg args-  emit $ Imp.Call targets fname args'-  where compileArg (se, _) = do-          t <- subExpType se-          case (se, t) of-            (_, Prim pt)   -> return $ Just $ Imp.ExpArg $ toExp' pt se-            (Var v, Mem{}) -> return $ Just $ Imp.MemArg v-            _              -> return Nothing--defCompileExp pat (BasicOp op) = defCompileBasicOp pat op--defCompileExp pat (DoLoop ctx val form body) = do-  attrs <- askAttrs-  when ("unroll" `inAttrs` attrs) $-    warn (noLoc::SrcLoc) [] "#[unroll] on loop with unknown number of iterations." -- FIXME: no location.--  dFParams mergepat-  forM_ merge $ \(p, se) ->-    when ((==0) $ arrayRank $ paramType p) $-    copyDWIM (paramName p) [] se []--  let doBody = compileLoopBody mergepat body--  case form of-    ForLoop i it bound loopvars -> do-      let setLoopParam (p,a)-            | Prim _ <- paramType p =-                copyDWIM (paramName p) [] (Var a) [DimFix $ Imp.vi32 i]-            | otherwise =-                return ()--      dLParams $ map fst loopvars-      sFor' i it (toExp' (IntType it) bound) $-        mapM_ setLoopParam loopvars >> doBody-    WhileLoop cond ->-      sWhile (Imp.var cond Bool) doBody--  Destination _ pat_dests <- destinationFromPattern pat-  forM_ (zip pat_dests $ map (Var . paramName . fst) merge) $ \(d, r) ->-    copyDWIMDest d [] r []--  where merge = ctx ++ val-        mergepat = map fst merge--defCompileExp pat (Op op) = do-  opc <- asks envOpCompiler-  opc pat op--defCompileBasicOp :: Mem lore =>-                     Pattern lore -> BasicOp -> ImpM lore r op ()--defCompileBasicOp (Pattern _ [pe]) (SubExp se) =-  copyDWIM (patElemName pe) [] se []--defCompileBasicOp (Pattern _ [pe]) (Opaque se) =-  copyDWIM (patElemName pe) [] se []--defCompileBasicOp (Pattern _ [pe]) (UnOp op e) = do-  e' <- toExp e-  patElemName pe <-- Imp.UnOpExp op e'--defCompileBasicOp (Pattern _ [pe]) (ConvOp conv e) = do-  e' <- toExp e-  patElemName pe <-- Imp.ConvOpExp conv e'--defCompileBasicOp (Pattern _ [pe]) (BinOp bop x y) = do-  x' <- toExp x-  y' <- toExp y-  patElemName pe <-- Imp.BinOpExp bop x' y'--defCompileBasicOp (Pattern _ [pe]) (CmpOp bop x y) = do-  x' <- toExp x-  y' <- toExp y-  patElemName pe <-- Imp.CmpOpExp bop x' y'--defCompileBasicOp _ (Assert e msg loc) = do-  e' <- toExp e-  msg' <- traverse toExp msg-  emit $ Imp.Assert e' msg' loc--  attrs <- askAttrs-  when (AttrComp "warn" ["safety_checks"] `inAttrs` attrs) $-    uncurry warn loc "Safety check required at run-time."--defCompileBasicOp (Pattern _ [pe]) (Index src slice)-  | Just idxs <- sliceIndices slice =-      copyDWIM (patElemName pe) [] (Var src) $ map (DimFix . toExp' int32) idxs--defCompileBasicOp _ Index{} =-  return ()--defCompileBasicOp (Pattern _ [pe]) (Update _ slice se) =-  sUpdate (patElemName pe) (map (fmap (toExp' int32)) slice) se--defCompileBasicOp (Pattern _ [pe]) (Replicate (Shape ds) se) = do-  ds' <- mapM toExp ds-  is <- replicateM (length ds) (newVName "i")-  copy_elem <- collect $ copyDWIM (patElemName pe) (map (DimFix . Imp.vi32) is) se []-  emit $ foldl (.) id (zipWith (`Imp.For` Int32) is ds') copy_elem--defCompileBasicOp _ Scratch{} =-  return ()--defCompileBasicOp (Pattern [] [pe]) (Iota n e s it) = do-  n' <- toExp n-  e' <- toExp e-  s' <- toExp s-  sFor "i" n' $ \i -> do-    let i' = sExt it i-    x <- dPrimV "x" $ e' + i' * s'-    copyDWIM (patElemName pe) [DimFix i] (Var x) []--defCompileBasicOp (Pattern _ [pe]) (Copy src) =-  copyDWIM (patElemName pe) [] (Var src) []--defCompileBasicOp (Pattern _ [pe]) (Manifest _ src) =-  copyDWIM (patElemName pe) [] (Var src) []--defCompileBasicOp (Pattern _ [pe]) (Concat i x ys _) = do-  offs_glb <- dPrim "tmp_offs" int32-  emit $ Imp.SetScalar offs_glb 0--  forM_ (x:ys) $ \y -> do-    y_dims <- arrayDims <$> lookupType y-    let rows = case drop i y_dims of-                 []  -> error $ "defCompileBasicOp Concat: empty array shape for " ++ pretty y-                 r:_ -> toExp' int32 r-        skip_dims = take i y_dims-        sliceAllDim d = DimSlice 0 d 1-        skip_slices = map (sliceAllDim . toExp' int32) skip_dims-        destslice = skip_slices ++ [DimSlice (Imp.vi32 offs_glb) rows 1]-    copyDWIM (patElemName pe) destslice (Var y) []-    emit $ Imp.SetScalar offs_glb $ Imp.var offs_glb int32 + rows--defCompileBasicOp (Pattern [] [pe]) (ArrayLit es _)-  | Just vs@(v:_) <- mapM isLiteral es = do-      dest_mem <- entryArrayLocation <$> lookupArray (patElemName pe)-      dest_space <- entryMemSpace <$> lookupMemory (memLocationName dest_mem)-      let t = primValueType v-      static_array <- newVNameForFun "static_array"-      emit $ Imp.DeclareArray static_array dest_space t $ Imp.ArrayValues vs-      let static_src = MemLocation static_array [intConst Int32 $ fromIntegral $ length es] $-                       IxFun.iota [fromIntegral $ length es]-          entry = MemVar Nothing $ MemEntry dest_space-      addVar static_array entry-      let slice = [DimSlice 0 (genericLength es) 1]-      copy t dest_mem slice static_src slice-  | otherwise =-    forM_ (zip [0..] es) $ \(i,e) ->-      copyDWIM (patElemName pe) [DimFix $ fromInteger i] e []--  where isLiteral (Constant v) = Just v-        isLiteral _ = Nothing--defCompileBasicOp _ Rearrange{} =-  return ()--defCompileBasicOp _ Rotate{} =-  return ()--defCompileBasicOp _ Reshape{} =-  return ()--defCompileBasicOp pat e =-  error $ "ImpGen.defCompileBasicOp: Invalid pattern\n  " ++-  pretty pat ++ "\nfor expression\n  " ++ pretty e---- | Note: a hack to be used only for functions.-addArrays :: [ArrayDecl] -> ImpM lore r op ()-addArrays = mapM_ addArray-  where addArray (ArrayDecl name bt location) =-          addVar name $-          ArrayVar Nothing ArrayEntry-          { entryArrayLocation = location-          , entryArrayElemType = bt-          }---- | Like 'dFParams', but does not create new declarations.--- Note: a hack to be used only for functions.-addFParams :: Mem lore => [FParam lore] -> ImpM lore r op ()-addFParams = mapM_ addFParam-  where addFParam fparam =-          addVar (paramName fparam) $-          memBoundToVarEntry Nothing $ noUniquenessReturns $ paramDec fparam---- | Another hack.-addLoopVar :: VName -> IntType -> ImpM lore r op ()-addLoopVar i it = addVar i $ ScalarVar Nothing $ ScalarEntry $ IntType it--dVars :: Mem lore =>-            Maybe (Exp lore) -> [PatElem lore] -> ImpM lore r op ()-dVars e = mapM_ dVar-  where dVar = dScope e . scopeOfPatElem--dFParams :: Mem lore => [FParam lore] -> ImpM lore r op ()-dFParams = dScope Nothing . scopeOfFParams--dLParams :: Mem lore => [LParam lore] -> ImpM lore r op ()-dLParams = dScope Nothing . scopeOfLParams--dPrimVol_ :: VName -> PrimType -> ImpM lore r op ()-dPrimVol_ name t = do- emit $ Imp.DeclareScalar name Imp.Volatile t- addVar name $ ScalarVar Nothing $ ScalarEntry t--dPrim_ :: VName -> PrimType -> ImpM lore r op ()-dPrim_ name t = do- emit $ Imp.DeclareScalar name Imp.Nonvolatile t- addVar name $ ScalarVar Nothing $ ScalarEntry t--dPrim :: String -> PrimType -> ImpM lore r op VName-dPrim name t = do name' <- newVName name-                  dPrim_ name' t-                  return name'--dPrimV_ :: VName -> Imp.Exp -> ImpM lore r op ()-dPrimV_ name e = do dPrim_ name $ primExpType e-                    name <-- e--dPrimV :: String -> Imp.Exp -> ImpM lore r op VName-dPrimV name e = do name' <- dPrim name $ primExpType e-                   name' <-- e-                   return name'--dPrimVE :: String -> Imp.Exp -> ImpM lore r op Imp.Exp-dPrimVE name e = do name' <- dPrim name $ primExpType e-                    name' <-- e-                    return $ Imp.var name' $ primExpType e--memBoundToVarEntry :: Maybe (Exp lore) -> MemBound NoUniqueness-                   -> VarEntry lore-memBoundToVarEntry e (MemPrim bt) =-  ScalarVar e ScalarEntry { entryScalarType = bt }-memBoundToVarEntry e (MemMem space) =-  MemVar e $ MemEntry space-memBoundToVarEntry e (MemArray bt shape _ (ArrayIn mem ixfun)) =-  let location = MemLocation mem (shapeDims shape) $ fmap (toExp' int32) ixfun-  in ArrayVar e ArrayEntry { entryArrayLocation = location-                           , entryArrayElemType = bt-                           }--infoDec :: Mem lore =>-            NameInfo lore-         -> MemInfo SubExp NoUniqueness MemBind-infoDec (LetName dec) = dec-infoDec (FParamName dec) = noUniquenessReturns dec-infoDec (LParamName dec) = dec-infoDec (IndexName it) = MemPrim $ IntType it--dInfo :: Mem lore =>-         Maybe (Exp lore) -> VName -> NameInfo lore-      -> ImpM lore r op ()-dInfo e name info = do-  let entry = memBoundToVarEntry e $ infoDec info-  case entry of-    MemVar _ entry' ->-      emit $ Imp.DeclareMem name $ entryMemSpace entry'-    ScalarVar _ entry' ->-      emit $ Imp.DeclareScalar name Imp.Nonvolatile $ entryScalarType entry'-    ArrayVar _ _ ->-      return ()-  addVar name entry--dScope :: Mem lore =>-          Maybe (Exp lore) -> Scope lore -> ImpM lore r op ()-dScope e = mapM_ (uncurry $ dInfo e) . M.toList--dArray :: VName -> PrimType -> ShapeBase SubExp -> MemBind -> ImpM lore r op ()-dArray name bt shape membind =-  addVar name $-  memBoundToVarEntry Nothing $ MemArray bt shape NoUniqueness membind--everythingVolatile :: ImpM lore r op a -> ImpM lore r op a-everythingVolatile = local $ \env -> env { envVolatility = Imp.Volatile }---- | Remove the array targets.-funcallTargets :: Destination -> ImpM lore r op [VName]-funcallTargets (Destination _ dests) =-  concat <$> mapM funcallTarget dests-  where funcallTarget (ScalarDestination name) =-          return [name]-        funcallTarget (ArrayDestination _) =-          return []-        funcallTarget (MemoryDestination name) =-          return [name]---- | Compile things to 'Imp.Exp'.-class ToExp a where-  -- | Compile to an 'Imp.Exp', where the type (must must still be a-  -- primitive) is deduced monadically.-  toExp :: a -> ImpM lore r op Imp.Exp-  -- | Compile where we know the type in advance.-  toExp' :: PrimType -> a -> Imp.Exp--instance ToExp SubExp where-  toExp (Constant v) =-    return $ Imp.ValueExp v-  toExp (Var v) =-    lookupVar v >>= \case-    ScalarVar _ (ScalarEntry pt) ->-      return $ Imp.var v pt-    _       -> error $ "toExp SubExp: SubExp is not a primitive type: " ++ pretty v--  toExp' _ (Constant v) = Imp.ValueExp v-  toExp' t (Var v) = Imp.var v t--instance ToExp (PrimExp VName) where-  toExp = pure . fmap Imp.ScalarVar-  toExp' _ = fmap Imp.ScalarVar--addVar :: VName -> VarEntry lore -> ImpM lore r op ()-addVar name entry =-  modify $ \s -> s { stateVTable = M.insert name entry $ stateVTable s }--localDefaultSpace :: Imp.Space -> ImpM lore r op a -> ImpM lore r op a-localDefaultSpace space = local (\env -> env { envDefaultSpace = space })--askFunction :: ImpM lore r op (Maybe Name)-askFunction = asks envFunction---- | Generate a 'VName', prefixed with 'askFunction' if it exists.-newVNameForFun :: String -> ImpM lore r op VName-newVNameForFun s = do-  fname <- fmap nameToString <$> askFunction-  newVName $ maybe "" (++".") fname ++ s---- | Generate a 'Name', prefixed with 'askFunction' if it exists.-nameForFun :: String -> ImpM lore r op Name-nameForFun s = do-  fname <- askFunction-  return $ maybe "" (<>".") fname <> nameFromString s--askEnv :: ImpM lore r op r-askEnv = asks envEnv--localEnv :: (r -> r) -> ImpM lore r op a -> ImpM lore r op a-localEnv f = local $ \env -> env { envEnv = f $ envEnv env }---- | The active attributes, including those for the statement--- currently being compiled.-askAttrs :: ImpM lore r op Attrs-askAttrs = asks envAttrs---- | Add more attributes to what is returning by 'askAttrs'.-localAttrs :: Attrs -> ImpM lore r op a -> ImpM lore r op a-localAttrs attrs = local $ \env -> env { envAttrs = attrs <> envAttrs env }--localOps :: Operations lore r op -> ImpM lore r op a -> ImpM lore r op a-localOps ops = local $ \env ->-                         env { envExpCompiler = opsExpCompiler ops-                             , envStmsCompiler = opsStmsCompiler ops-                             , envCopyCompiler = opsCopyCompiler ops-                             , envOpCompiler = opsOpCompiler ops-                             , envAllocCompilers = opsAllocCompilers ops-                             }---- | Get the current symbol table.-getVTable :: ImpM lore r op (VTable lore)-getVTable = gets stateVTable--putVTable :: VTable lore -> ImpM lore r op ()-putVTable vtable = modify $ \s -> s { stateVTable = vtable }---- | Run an action with a modified symbol table.  All changes to the--- symbol table will be reverted once the action is done!-localVTable :: (VTable lore -> VTable lore) -> ImpM lore r op a -> ImpM lore r op a-localVTable f m = do-  old_vtable <- getVTable-  putVTable $ f old_vtable-  a <- m-  putVTable old_vtable-  return a--lookupVar :: VName -> ImpM lore r op (VarEntry lore)-lookupVar name = do-  res <- gets $ M.lookup name . stateVTable-  case res of-    Just entry -> return entry-    _ -> error $ "Unknown variable: " ++ pretty name--lookupArray :: VName -> ImpM lore r op ArrayEntry-lookupArray name = do-  res <- lookupVar name-  case res of-    ArrayVar _ entry -> return entry-    _                -> error $ "ImpGen.lookupArray: not an array: " ++ pretty name--lookupMemory :: VName -> ImpM lore r op MemEntry-lookupMemory name = do-  res <- lookupVar name-  case res of-    MemVar _ entry -> return entry-    _              -> error $ "Unknown memory block: " ++ pretty name--destinationFromPattern :: Mem lore => Pattern lore -> ImpM lore r op Destination-destinationFromPattern pat =-  fmap (Destination (baseTag <$> maybeHead (patternNames pat))) . mapM inspect $-  patternElements pat-  where inspect patElem = do-          let name = patElemName patElem-          entry <- lookupVar name-          case entry of-            ArrayVar _ (ArrayEntry MemLocation{} _) ->-              return $ ArrayDestination Nothing-            MemVar{} ->-              return $ MemoryDestination name--            ScalarVar{} ->-              return $ ScalarDestination name--fullyIndexArray :: VName -> [Imp.Exp]-                -> ImpM lore r op (VName, Imp.Space, Count Elements Imp.Exp)-fullyIndexArray name indices = do-  arr <- lookupArray name-  fullyIndexArray' (entryArrayLocation arr) indices--fullyIndexArray' :: MemLocation -> [Imp.Exp]-                 -> ImpM lore r op (VName, Imp.Space, Count Elements Imp.Exp)-fullyIndexArray' (MemLocation mem _ ixfun) indices = do-  space <- entryMemSpace <$> lookupMemory mem-  let indices' = case space of-                   ScalarSpace ds _ ->-                     let (zero_is, is) = splitFromEnd (length ds) indices-                     in map (const 0) zero_is ++ is-                   _ -> indices-  return (mem, space,-          elements $ IxFun.index ixfun indices')---- More complicated read/write operations that use index functions.--copy :: CopyCompiler lore r op-copy bt dest destslice src srcslice = do-  cc <- asks envCopyCompiler-  cc bt dest destslice src srcslice---- | Use an 'Imp.Copy' if possible, otherwise 'copyElementWise'.-defaultCopy :: CopyCompiler lore r op-defaultCopy bt dest destslice src srcslice-  | Just destoffset <--      IxFun.linearWithOffset (IxFun.slice destIxFun destslice) bt_size,-    Just srcoffset  <--      IxFun.linearWithOffset (IxFun.slice srcIxFun srcslice) bt_size = do-        srcspace <- entryMemSpace <$> lookupMemory srcmem-        destspace <- entryMemSpace <$> lookupMemory destmem-        if isScalarSpace srcspace || isScalarSpace destspace-          then copyElementWise bt dest destslice src srcslice-          else emit $ Imp.Copy-               destmem (bytes destoffset) destspace-               srcmem (bytes srcoffset) srcspace $-               num_elems `withElemType` bt-  | otherwise =-      copyElementWise bt dest destslice src srcslice-  where bt_size = primByteSize bt-        num_elems = Imp.elements $ product $ sliceDims srcslice-        MemLocation destmem _ destIxFun = dest-        MemLocation srcmem _ srcIxFun = src-        isScalarSpace ScalarSpace{} = True-        isScalarSpace _ = False--copyElementWise :: CopyCompiler lore r op-copyElementWise bt dest destslice src srcslice = do-    let bounds = sliceDims srcslice-    is <- replicateM (length bounds) (newVName "i")-    let ivars = map Imp.vi32 is-    (destmem, destspace, destidx) <--      fullyIndexArray' dest $ fixSlice destslice ivars-    (srcmem, srcspace, srcidx) <--      fullyIndexArray' src $ fixSlice srcslice ivars-    vol <- asks envVolatility-    emit $ foldl (.) id (zipWith (`Imp.For` Int32) is bounds) $-      Imp.Write destmem destidx bt destspace vol $-      Imp.index srcmem srcidx bt srcspace vol---- | Copy from here to there; both destination and source may be--- indexeded.-copyArrayDWIM :: PrimType-              -> MemLocation -> [DimIndex Imp.Exp]-              -> MemLocation -> [DimIndex Imp.Exp]-              -> ImpM lore r op (Imp.Code op)-copyArrayDWIM bt-  destlocation@(MemLocation _ destshape _) destslice-  srclocation@(MemLocation _ srcshape _) srcslice--  | Just destis <- mapM dimFix destslice,-    Just srcis <- mapM dimFix srcslice,-    length srcis == length srcshape,-    length destis == length destshape = do-  (targetmem, destspace, targetoffset) <--    fullyIndexArray' destlocation destis-  (srcmem, srcspace, srcoffset) <--    fullyIndexArray' srclocation srcis-  vol <- asks envVolatility-  return $ Imp.Write targetmem targetoffset bt destspace vol $-    Imp.index srcmem srcoffset bt srcspace vol--  | otherwise = do-      let destslice' =-            fullSliceNum (map (toExp' int32) destshape) destslice-          srcslice'  =-            fullSliceNum (map (toExp' int32) srcshape) srcslice-          destrank = length $ sliceDims destslice'-          srcrank = length $ sliceDims srcslice'-      if destrank /= srcrank-        then error $ "copyArrayDWIM: cannot copy to " ++-             pretty (memLocationName destlocation) ++-             " from " ++ pretty (memLocationName srclocation) ++-             " because ranks do not match (" ++ pretty destrank ++-             " vs " ++ pretty srcrank ++ ")"-      else if destlocation == srclocation && destslice' == srcslice'-        then return mempty -- Copy would be no-op.-        else collect $ copy bt destlocation destslice' srclocation srcslice'---- | Like 'copyDWIM', but the target is a 'ValueDestination'--- instead of a variable name.-copyDWIMDest :: ValueDestination -> [DimIndex Imp.Exp] -> SubExp -> [DimIndex Imp.Exp]-             -> ImpM lore r op ()--copyDWIMDest _ _ (Constant v) (_:_) =-  error $-  unwords ["copyDWIMDest: constant source", pretty v, "cannot be indexed."]-copyDWIMDest pat dest_slice (Constant v) [] =-  case mapM dimFix dest_slice of-    Nothing ->-      error $-      unwords ["copyDWIMDest: constant source", pretty v, "with slice destination."]-    Just dest_is ->-      case pat of-        ScalarDestination name ->-          emit $ Imp.SetScalar name $ Imp.ValueExp v-        MemoryDestination{} ->-          error $-          unwords ["copyDWIMDest: constant source", pretty v, "cannot be written to memory destination."]-        ArrayDestination (Just dest_loc) -> do-          (dest_mem, dest_space, dest_i) <--            fullyIndexArray' dest_loc dest_is-          vol <- asks envVolatility-          emit $ Imp.Write dest_mem dest_i bt dest_space vol $ Imp.ValueExp v-        ArrayDestination Nothing ->-          error "copyDWIMDest: ArrayDestination Nothing"-  where bt = primValueType v--copyDWIMDest dest dest_slice (Var src) src_slice = do-  src_entry <- lookupVar src-  case (dest, src_entry) of-    (MemoryDestination mem, MemVar _ (MemEntry space)) ->-      emit $ Imp.SetMem mem src space--    (MemoryDestination{}, _) ->-      error $-      unwords ["copyDWIMDest: cannot write", pretty src, "to memory destination."]--    (_, MemVar{}) ->-      error $-      unwords ["copyDWIMDest: source", pretty src, "is a memory block."]--    (_, ScalarVar _ (ScalarEntry _)) | not $ null src_slice ->-      error $-      unwords ["copyDWIMDest: prim-typed source", pretty src, "with slice", pretty src_slice]--    (ScalarDestination name, _) | not $ null dest_slice ->-      error $-      unwords ["copyDWIMDest: prim-typed target", pretty name, "with slice", pretty dest_slice]--    (ScalarDestination name, ScalarVar _ (ScalarEntry pt)) ->-      emit $ Imp.SetScalar name $ Imp.var src pt--    (ScalarDestination name, ArrayVar _ arr)-      | Just src_is <- mapM dimFix src_slice,-        length src_slice == length (entryArrayShape arr) -> do-          let bt = entryArrayElemType arr-          (mem, space, i) <--            fullyIndexArray' (entryArrayLocation arr) src_is-          vol <- asks envVolatility-          emit $ Imp.SetScalar name $ Imp.index mem i bt space vol-      | otherwise ->-          error $-          unwords ["copyDWIMDest: prim-typed target", pretty name,-                   "and array-typed source", pretty src,-                   "with slice", pretty src_slice]--    (ArrayDestination (Just dest_loc), ArrayVar _ src_arr) -> do-      let src_loc = entryArrayLocation src_arr-          bt = entryArrayElemType src_arr-      emit =<< copyArrayDWIM bt dest_loc dest_slice src_loc src_slice--    (ArrayDestination (Just dest_loc), ScalarVar _ (ScalarEntry bt))-      | Just dest_is <- mapM dimFix dest_slice -> do-          (dest_mem, dest_space, dest_i) <- fullyIndexArray' dest_loc dest_is-          vol <- asks envVolatility-          emit $ Imp.Write dest_mem dest_i bt dest_space vol (Imp.var src bt)-      | otherwise ->-          error $-          unwords ["copyDWIMDest: array-typed target and prim-typed source", pretty src,-                   "with slice", pretty dest_slice]--    (ArrayDestination Nothing, _) ->-      return () -- Nothing to do; something else set some memory-                -- somewhere.---- | Copy from here to there; both destination and source be--- indexeded.  If so, they better be arrays of enough dimensions.--- This function will generally just Do What I Mean, and Do The Right--- Thing.  Both destination and source must be in scope.-copyDWIM :: VName -> [DimIndex Imp.Exp] -> SubExp -> [DimIndex Imp.Exp]-         -> ImpM lore r op ()-copyDWIM dest dest_slice src src_slice = do-  dest_entry <- lookupVar dest-  let dest_target =-        case dest_entry of-          ScalarVar _ _ ->-            ScalarDestination dest--          ArrayVar _ (ArrayEntry (MemLocation mem shape ixfun) _) ->-            ArrayDestination $ Just $ MemLocation mem shape ixfun--          MemVar _ _ ->-            MemoryDestination dest-  copyDWIMDest dest_target dest_slice src src_slice---- | As 'copyDWIM', but implicitly 'DimFix'es the indexes.-copyDWIMFix :: VName -> [Imp.Exp] -> SubExp -> [Imp.Exp] -> ImpM lore r op ()-copyDWIMFix dest dest_is src src_is =-  copyDWIM dest (map DimFix dest_is) src (map DimFix src_is)---- | @compileAlloc pat size space@ allocates @n@ bytes of memory in @space@,--- writing the result to @dest@, which must be a single--- 'MemoryDestination',-compileAlloc :: Mem lore =>-                Pattern lore -> SubExp -> Space-             -> ImpM lore r op ()-compileAlloc (Pattern [] [mem]) e space = do-  e' <- Imp.bytes <$> toExp e-  allocator <- asks $ M.lookup space . envAllocCompilers-  case allocator of-    Nothing -> emit $ Imp.Allocate (patElemName mem) e' space-    Just allocator' -> allocator' (patElemName mem) e'-compileAlloc pat _ _ =-  error $ "compileAlloc: Invalid pattern: " ++ pretty pat---- | The number of bytes needed to represent the array in a--- straightforward contiguous format, as an 'Int64' expression.-typeSize :: Type -> Count Bytes Imp.Exp-typeSize t =-  Imp.bytes $ sExt Int64 (Imp.LeafExp (Imp.SizeOf $ elemType t) int32) *-  product (map (sExt Int64 . toExp' int32) (arrayDims t))----- Building blocks for constructing code.--sFor' :: VName -> IntType -> Imp.Exp -> ImpM lore r op () -> ImpM lore r op ()-sFor' i it bound body = do-  addLoopVar i it-  body' <- collect body-  emit $ Imp.For i it bound body'--sFor :: String -> Imp.Exp -> (Imp.Exp -> ImpM lore r op ()) -> ImpM lore r op ()-sFor i bound body = do-  i' <- newVName i-  it <- case primExpType bound of-          IntType it -> return it-          t -> error $ "sFor: bound " ++ pretty bound ++ " is of type " ++ pretty t-  addLoopVar i' it-  body' <- collect $ body $ Imp.var i' $ IntType it-  emit $ Imp.For i' it bound body'--sWhile :: Imp.Exp -> ImpM lore r op () -> ImpM lore r op ()-sWhile cond body = do-  body' <- collect body-  emit $ Imp.While cond body'--sComment :: String -> ImpM lore r op () -> ImpM lore r op ()-sComment s code = do-  code' <- collect code-  emit $ Imp.Comment s code'--sIf :: Imp.Exp -> ImpM lore r op () -> ImpM lore r op () -> ImpM lore r op ()-sIf cond tbranch fbranch = do-  tbranch' <- collect tbranch-  fbranch' <- collect fbranch-  emit $ Imp.If cond tbranch' fbranch'--sWhen :: Imp.Exp -> ImpM lore r op () -> ImpM lore r op ()-sWhen cond tbranch = sIf cond tbranch (return ())--sUnless :: Imp.Exp -> ImpM lore r op () -> ImpM lore r op ()-sUnless cond = sIf cond (return ())--sOp :: op -> ImpM lore r op ()-sOp = emit . Imp.Op--sDeclareMem :: String -> Space -> ImpM lore r op VName-sDeclareMem name space = do-  name' <- newVName name-  emit $ Imp.DeclareMem name' space-  addVar name' $ MemVar Nothing $ MemEntry space-  return name'--sAlloc_ :: VName -> Count Bytes Imp.Exp -> Space -> ImpM lore r op ()-sAlloc_ name' size' space = do-  allocator <- asks $ M.lookup space . envAllocCompilers-  case allocator of-    Nothing -> emit $ Imp.Allocate name' size' space-    Just allocator' -> allocator' name' size'--sAlloc :: String -> Count Bytes Imp.Exp -> Space -> ImpM lore r op VName-sAlloc name size space = do-  name' <- sDeclareMem name space-  sAlloc_ name' size space-  return name'--sArray :: String -> PrimType -> ShapeBase SubExp -> MemBind -> ImpM lore r op VName-sArray name bt shape membind = do-  name' <- newVName name-  dArray name' bt shape membind-  return name'---- | Declare an array in row-major order in the given memory block.-sArrayInMem :: String -> PrimType -> ShapeBase SubExp -> VName -> ImpM lore r op VName-sArrayInMem name pt shape mem =-  sArray name pt shape $ ArrayIn mem $-  IxFun.iota $ map (primExpFromSubExp int32) $ shapeDims shape---- | Like 'sAllocArray', but permute the in-memory representation of the indices as specified.-sAllocArrayPerm :: String -> PrimType -> ShapeBase SubExp -> Space -> [Int] -> ImpM lore r op VName-sAllocArrayPerm name pt shape space perm = do-  let permuted_dims = rearrangeShape perm $ shapeDims shape-  mem <- sAlloc (name ++ "_mem") (typeSize (Array pt shape NoUniqueness)) space-  let iota_ixfun = IxFun.iota $ map (primExpFromSubExp int32) permuted_dims-  sArray name pt shape $-    ArrayIn mem $ IxFun.permute iota_ixfun $ rearrangeInverse perm---- | Uses linear/iota index function.-sAllocArray :: String -> PrimType -> ShapeBase SubExp -> Space -> ImpM lore r op VName-sAllocArray name pt shape space =-  sAllocArrayPerm name pt shape space [0..shapeRank shape-1]---- | Uses linear/iota index function.-sStaticArray :: String -> Space -> PrimType -> Imp.ArrayContents -> ImpM lore r op VName-sStaticArray name space pt vs = do-  let num_elems = case vs of Imp.ArrayValues vs' -> length vs'-                             Imp.ArrayZeros n -> fromIntegral n-      shape = Shape [intConst Int32 $ toInteger num_elems]-  mem <- newVNameForFun $ name ++ "_mem"-  emit $ Imp.DeclareArray mem space pt vs-  addVar mem $ MemVar Nothing $ MemEntry space-  sArray name pt shape $ ArrayIn mem $ IxFun.iota [fromIntegral num_elems]--sWrite :: VName -> [Imp.Exp] -> PrimExp Imp.ExpLeaf -> ImpM lore r op ()-sWrite arr is v = do-  (mem, space, offset) <- fullyIndexArray arr is-  vol <- asks envVolatility-  emit $ Imp.Write mem offset (primExpType v) space vol v--sUpdate :: VName -> Slice Imp.Exp -> SubExp -> ImpM lore r op ()-sUpdate arr slice v = copyDWIM arr slice v []--sLoopNest :: Shape-          -> ([Imp.Exp] -> ImpM lore r op ())-          -> ImpM lore r op ()-sLoopNest = sLoopNest' [] . shapeDims-  where sLoopNest' is [] f = f $ reverse is-        sLoopNest' is (d:ds) f = do-          d' <- toExp d-          sFor "nest_i" d' $ \i -> sLoopNest' (i:is) ds f---- | ASsignment.-(<--) :: VName -> Imp.Exp -> ImpM lore r op ()-x <-- e = emit $ Imp.SetScalar x e-infixl 3 <------ | Constructing an ad-hoc function that does not--- correspond to any of the IR functions in the input program.-function :: Name -> [Imp.Param] -> [Imp.Param] -> ImpM lore r op ()-         -> ImpM lore r op ()-function fname outputs inputs m = local newFunction $ do-  body <- collect $ do-    mapM_ addParam $ outputs ++ inputs-    m-  emitFunction fname $ Imp.Function False outputs inputs body [] []-  where addParam (Imp.MemParam name space) =-          addVar name $ MemVar Nothing $ MemEntry space-        addParam (Imp.ScalarParam name bt) =-          addVar name $ ScalarVar Nothing $ ScalarEntry bt-        newFunction env = env { envFunction = Just fname }+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Strict #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TypeFamilies #-}++module Futhark.CodeGen.ImpGen+  ( -- * Entry Points+    compileProg,++    -- * Pluggable Compiler+    OpCompiler,+    ExpCompiler,+    CopyCompiler,+    StmsCompiler,+    AllocCompiler,+    Operations (..),+    defaultOperations,+    MemLocation (..),+    MemEntry (..),+    ScalarEntry (..),++    -- * Monadic Compiler Interface+    ImpM,+    localDefaultSpace,+    askFunction,+    newVNameForFun,+    nameForFun,+    askEnv,+    localEnv,+    localOps,+    VTable,+    getVTable,+    localVTable,+    subImpM,+    subImpM_,+    emit,+    emitFunction,+    hasFunction,+    collect,+    collect',+    comment,+    VarEntry (..),+    ArrayEntry (..),++    -- * Lookups+    lookupVar,+    lookupArray,+    lookupMemory,++    -- * Building Blocks+    TV,+    mkTV,+    tvSize,+    tvExp,+    tvVar,+    ToExp (..),+    compileAlloc,+    everythingVolatile,+    compileBody,+    compileBody',+    compileLoopBody,+    defCompileStms,+    compileStms,+    compileExp,+    defCompileExp,+    fullyIndexArray,+    fullyIndexArray',+    copy,+    copyDWIM,+    copyDWIMFix,+    copyElementWise,+    typeSize,+    isMapTransposeCopy,++    -- * Constructing code.+    dLParams,+    dFParams,+    dScope,+    dArray,+    dPrim,+    dPrimVol,+    dPrim_,+    dPrimV_,+    dPrimV,+    dPrimVE,+    sFor,+    sWhile,+    sComment,+    sIf,+    sWhen,+    sUnless,+    sOp,+    sDeclareMem,+    sAlloc,+    sAlloc_,+    sArray,+    sArrayInMem,+    sAllocArray,+    sAllocArrayPerm,+    sStaticArray,+    sWrite,+    sUpdate,+    sLoopNest,+    (<--),+    (<~~),+    function,+    warn,+    module Language.Futhark.Warnings,+  )+where++import Control.Monad.Reader+import Control.Monad.State+import Control.Monad.Writer+import Control.Parallel.Strategies+import Data.Bifunctor (first)+import qualified Data.DList as DL+import Data.Either+import Data.List (find, genericLength, sortOn)+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Data.Set as S+import Futhark.CodeGen.ImpCode+  ( Bytes,+    Count,+    Elements,+    bytes,+    elements,+    withElemType,+  )+import qualified Futhark.CodeGen.ImpCode as Imp+import Futhark.CodeGen.ImpGen.Transpose+import Futhark.Construct hiding (ToExp (..))+import Futhark.IR.Mem+import qualified Futhark.IR.Mem.IxFun as IxFun+import Futhark.IR.SOACS (SOACS)+import Futhark.Util+import Futhark.Util.Loc (noLoc)+import Language.Futhark.Warnings++-- | How to compile an t'Op'.+type OpCompiler lore r op = Pattern lore -> Op lore -> ImpM lore r op ()++-- | How to compile some 'Stms'.+type StmsCompiler lore r op = Names -> Stms lore -> ImpM lore r op () -> ImpM lore r op ()++-- | How to compile an 'Exp'.+type ExpCompiler lore r op = Pattern lore -> Exp lore -> ImpM lore r op ()++type CopyCompiler lore r op =+  PrimType ->+  MemLocation ->+  Slice (Imp.TExp Int64) ->+  MemLocation ->+  Slice (Imp.TExp Int64) ->+  ImpM lore r op ()++-- | An alternate way of compiling an allocation.+type AllocCompiler lore r op = VName -> Count Bytes (Imp.TExp Int64) -> ImpM lore r op ()++data Operations lore r op = Operations+  { opsExpCompiler :: ExpCompiler lore r op,+    opsOpCompiler :: OpCompiler lore r op,+    opsStmsCompiler :: StmsCompiler lore r op,+    opsCopyCompiler :: CopyCompiler lore r op,+    opsAllocCompilers :: M.Map Space (AllocCompiler lore r op)+  }++-- | An operations set for which the expression compiler always+-- returns 'defCompileExp'.+defaultOperations ::+  (Mem lore, FreeIn op) =>+  OpCompiler lore r op ->+  Operations lore r op+defaultOperations opc =+  Operations+    { opsExpCompiler = defCompileExp,+      opsOpCompiler = opc,+      opsStmsCompiler = defCompileStms,+      opsCopyCompiler = defaultCopy,+      opsAllocCompilers = mempty+    }++-- | When an array is dared, this is where it is stored.+data MemLocation = MemLocation+  { memLocationName :: VName,+    memLocationShape :: [Imp.DimSize],+    memLocationIxFun :: IxFun.IxFun (Imp.TExp Int64)+  }+  deriving (Eq, Show)++data ArrayEntry = ArrayEntry+  { entryArrayLocation :: MemLocation,+    entryArrayElemType :: PrimType+  }+  deriving (Show)++entryArrayShape :: ArrayEntry -> [Imp.DimSize]+entryArrayShape = memLocationShape . entryArrayLocation++newtype MemEntry = MemEntry {entryMemSpace :: Imp.Space}+  deriving (Show)++newtype ScalarEntry = ScalarEntry+  { entryScalarType :: PrimType+  }+  deriving (Show)++-- | Every non-scalar variable must be associated with an entry.+data VarEntry lore+  = ArrayVar (Maybe (Exp lore)) ArrayEntry+  | ScalarVar (Maybe (Exp lore)) ScalarEntry+  | MemVar (Maybe (Exp lore)) MemEntry+  deriving (Show)++-- | When compiling an expression, this is a description of where the+-- result should end up.  The integer is a reference to the construct+-- that gave rise to this destination (for patterns, this will be the+-- tag of the first name in the pattern).  This can be used to make+-- the generated code easier to relate to the original code.+data Destination = Destination+  { destinationTag :: Maybe Int,+    valueDestinations :: [ValueDestination]+  }+  deriving (Show)++data ValueDestination+  = ScalarDestination VName+  | MemoryDestination VName+  | -- | The 'MemLocation' is 'Just' if a copy if+    -- required.  If it is 'Nothing', then a+    -- copy/assignment of a memory block somewhere+    -- takes care of this array.+    ArrayDestination (Maybe MemLocation)+  deriving (Show)++data Env lore r op = Env+  { envExpCompiler :: ExpCompiler lore r op,+    envStmsCompiler :: StmsCompiler lore r op,+    envOpCompiler :: OpCompiler lore r op,+    envCopyCompiler :: CopyCompiler lore r op,+    envAllocCompilers :: M.Map Space (AllocCompiler lore r op),+    envDefaultSpace :: Imp.Space,+    envVolatility :: Imp.Volatility,+    -- | User-extensible environment.+    envEnv :: r,+    -- | Name of the function we are compiling, if any.+    envFunction :: Maybe Name,+    -- | The set of attributes that are active on the enclosing+    -- statements (including the one we are currently compiling).+    envAttrs :: Attrs+  }++newEnv :: r -> Operations lore r op -> Imp.Space -> Env lore r op+newEnv r ops ds =+  Env+    { envExpCompiler = opsExpCompiler ops,+      envStmsCompiler = opsStmsCompiler ops,+      envOpCompiler = opsOpCompiler ops,+      envCopyCompiler = opsCopyCompiler ops,+      envAllocCompilers = mempty,+      envDefaultSpace = ds,+      envVolatility = Imp.Nonvolatile,+      envEnv = r,+      envFunction = Nothing,+      envAttrs = mempty+    }++-- | The symbol table used during compilation.+type VTable lore = M.Map VName (VarEntry lore)++data ImpState lore r op = ImpState+  { stateVTable :: VTable lore,+    stateFunctions :: Imp.Functions op,+    stateCode :: Imp.Code op,+    stateWarnings :: Warnings,+    stateNameSource :: VNameSource+  }++newState :: VNameSource -> ImpState lore r op+newState = ImpState mempty mempty mempty mempty++newtype ImpM lore r op a+  = ImpM (ReaderT (Env lore r op) (State (ImpState lore r op)) a)+  deriving+    ( Functor,+      Applicative,+      Monad,+      MonadState (ImpState lore r op),+      MonadReader (Env lore r op)+    )++instance MonadFreshNames (ImpM lore r op) where+  getNameSource = gets stateNameSource+  putNameSource src = modify $ \s -> s {stateNameSource = src}++-- Cannot be an KernelsMem scope because the index functions have+-- the wrong leaves (VName instead of Imp.Exp).+instance HasScope SOACS (ImpM lore r op) where+  askScope = gets $ M.map (LetName . entryType) . stateVTable+    where+      entryType (MemVar _ memEntry) =+        Mem (entryMemSpace memEntry)+      entryType (ArrayVar _ arrayEntry) =+        Array+          (entryArrayElemType arrayEntry)+          (Shape $ entryArrayShape arrayEntry)+          NoUniqueness+      entryType (ScalarVar _ scalarEntry) =+        Prim $ entryScalarType scalarEntry++runImpM ::+  ImpM lore r op a ->+  r ->+  Operations lore r op ->+  Imp.Space ->+  ImpState lore r op ->+  (a, ImpState lore r op)+runImpM (ImpM m) r ops space = runState (runReaderT m $ newEnv r ops space)++subImpM_ ::+  r' ->+  Operations lore r' op' ->+  ImpM lore r' op' a ->+  ImpM lore r op (Imp.Code op')+subImpM_ r ops m = snd <$> subImpM r ops m++subImpM ::+  r' ->+  Operations lore r' op' ->+  ImpM lore r' op' a ->+  ImpM lore r op (a, Imp.Code op')+subImpM r ops (ImpM m) = do+  env <- ask+  s <- get++  let env' =+        env+          { envExpCompiler = opsExpCompiler ops,+            envStmsCompiler = opsStmsCompiler ops,+            envCopyCompiler = opsCopyCompiler ops,+            envOpCompiler = opsOpCompiler ops,+            envAllocCompilers = opsAllocCompilers ops,+            envEnv = r+          }+      s' =+        ImpState+          { stateVTable = stateVTable s,+            stateFunctions = mempty,+            stateCode = mempty,+            stateNameSource = stateNameSource s,+            stateWarnings = mempty+          }+      (x, s'') = runState (runReaderT m env') s'++  putNameSource $ stateNameSource s''+  warnings $ stateWarnings s''+  return (x, stateCode s'')++-- | Execute a code generation action, returning the code that was+-- emitted.+collect :: ImpM lore r op () -> ImpM lore r op (Imp.Code op)+collect = fmap snd . collect'++collect' :: ImpM lore r op a -> ImpM lore r op (a, Imp.Code op)+collect' m = do+  prev_code <- gets stateCode+  modify $ \s -> s {stateCode = mempty}+  x <- m+  new_code <- gets stateCode+  modify $ \s -> s {stateCode = prev_code}+  return (x, new_code)++-- | Execute a code generation action, wrapping the generated code+-- within a 'Imp.Comment' with the given description.+comment :: String -> ImpM lore r op () -> ImpM lore r op ()+comment desc m = do+  code <- collect m+  emit $ Imp.Comment desc code++-- | Emit some generated imperative code.+emit :: Imp.Code op -> ImpM lore r op ()+emit code = modify $ \s -> s {stateCode = stateCode s <> code}++warnings :: Warnings -> ImpM lore r op ()+warnings ws = modify $ \s -> s {stateWarnings = ws <> stateWarnings s}++-- | Emit a warning about something the user should be aware of.+warn :: Located loc => loc -> [loc] -> String -> ImpM lore r op ()+warn loc locs problem =+  warnings $ singleWarning' (srclocOf loc) (map srclocOf locs) problem++-- | Emit a function in the generated code.+emitFunction :: Name -> Imp.Function op -> ImpM lore r op ()+emitFunction fname fun = do+  Imp.Functions fs <- gets stateFunctions+  modify $ \s -> s {stateFunctions = Imp.Functions $ (fname, fun) : fs}++-- | Check if a function of a given name exists.+hasFunction :: Name -> ImpM lore r op Bool+hasFunction fname = gets $ \s ->+  let Imp.Functions fs = stateFunctions s+   in isJust $ lookup fname fs++constsVTable :: Mem lore => Stms lore -> VTable lore+constsVTable = foldMap stmVtable+  where+    stmVtable (Let pat _ e) =+      foldMap (peVtable e) $ patternElements pat+    peVtable e (PatElem name dec) =+      M.singleton name $ memBoundToVarEntry (Just e) dec++compileProg ::+  (Mem lore, FreeIn op, MonadFreshNames m) =>+  r ->+  Operations lore r op ->+  Imp.Space ->+  Prog lore ->+  m (Warnings, Imp.Definitions op)+compileProg r ops space (Prog consts funs) =+  modifyNameSource $ \src ->+    let (_, ss) =+          unzip $ parMap rpar (compileFunDef' src) funs+        free_in_funs =+          freeIn $ mconcat $ map stateFunctions ss+        (consts', s') =+          runImpM (compileConsts free_in_funs consts) r ops space $+            combineStates ss+     in ( ( stateWarnings s',+            Imp.Definitions consts' (stateFunctions s')+          ),+          stateNameSource s'+        )+  where+    compileFunDef' src fdef =+      runImpM+        (compileFunDef fdef)+        r+        ops+        space+        (newState src) {stateVTable = constsVTable consts}++    combineStates ss =+      let Imp.Functions funs' = mconcat $ map stateFunctions ss+          src = mconcat (map stateNameSource ss)+       in (newState src)+            { stateFunctions =+                Imp.Functions $ M.toList $ M.fromList funs',+              stateWarnings =+                mconcat $ map stateWarnings ss+            }++compileConsts :: Names -> Stms lore -> ImpM lore r op (Imp.Constants op)+compileConsts used_consts stms = do+  code <- collect $ compileStms used_consts stms $ pure ()+  pure $ uncurry Imp.Constants $ first DL.toList $ extract code+  where+    -- Fish out those top-level declarations in the constant+    -- initialisation code that are free in the functions.+    extract (x Imp.:>>: y) =+      extract x <> extract y+    extract (Imp.DeclareMem name space)+      | name `nameIn` used_consts =+        ( DL.singleton $ Imp.MemParam name space,+          mempty+        )+    extract (Imp.DeclareScalar name _ t)+      | name `nameIn` used_consts =+        ( DL.singleton $ Imp.ScalarParam name t,+          mempty+        )+    extract s =+      (mempty, s)++compileInParam ::+  Mem lore =>+  FParam lore ->+  ImpM lore r op (Either Imp.Param ArrayDecl)+compileInParam fparam = case paramDec fparam of+  MemPrim bt ->+    return $ Left $ Imp.ScalarParam name bt+  MemMem space ->+    return $ Left $ Imp.MemParam name space+  MemArray bt shape _ (ArrayIn mem ixfun) ->+    return $+      Right $+        ArrayDecl name bt $+          MemLocation mem (shapeDims shape) $ fmap (fmap Imp.ScalarVar) ixfun+  where+    name = paramName fparam++data ArrayDecl = ArrayDecl VName PrimType MemLocation++fparamSizes :: Typed dec => Param dec -> S.Set VName+fparamSizes = S.fromList . subExpVars . arrayDims . paramType++compileInParams ::+  Mem lore =>+  [FParam lore] ->+  [EntryPointType] ->+  ImpM lore r op ([Imp.Param], [ArrayDecl], [Imp.ExternalValue])+compileInParams params orig_epts = do+  let (ctx_params, val_params) =+        splitAt (length params - sum (map entryPointSize orig_epts)) params+  (inparams, arrayds) <- partitionEithers <$> mapM compileInParam (ctx_params ++ val_params)+  let findArray x = find (isArrayDecl x) arrayds+      sizes = mconcat $ map fparamSizes $ ctx_params ++ val_params++      summaries = M.fromList $ mapMaybe memSummary params+        where+          memSummary param+            | MemMem space <- paramDec param =+              Just (paramName param, space)+            | otherwise =+              Nothing++      findMemInfo :: VName -> Maybe Space+      findMemInfo = flip M.lookup summaries++      mkValueDesc fparam signedness =+        case (findArray $ paramName fparam, paramType fparam) of+          (Just (ArrayDecl _ bt (MemLocation mem shape _)), _) -> do+            memspace <- findMemInfo mem+            Just $ Imp.ArrayValue mem memspace bt signedness shape+          (_, Prim bt)+            | paramName fparam `S.member` sizes ->+              Nothing+            | otherwise ->+              Just $ Imp.ScalarValue bt signedness $ paramName fparam+          _ ->+            Nothing++      mkExts (TypeOpaque desc n : epts) fparams =+        let (fparams', rest) = splitAt n fparams+         in Imp.OpaqueValue+              desc+              (mapMaybe (`mkValueDesc` Imp.TypeDirect) fparams') :+            mkExts epts rest+      mkExts (TypeUnsigned : epts) (fparam : fparams) =+        maybeToList (Imp.TransparentValue <$> mkValueDesc fparam Imp.TypeUnsigned)+          ++ mkExts epts fparams+      mkExts (TypeDirect : epts) (fparam : fparams) =+        maybeToList (Imp.TransparentValue <$> mkValueDesc fparam Imp.TypeDirect)+          ++ mkExts epts fparams+      mkExts _ _ = []++  return (inparams, arrayds, mkExts orig_epts val_params)+  where+    isArrayDecl x (ArrayDecl y _ _) = x == y++compileOutParams ::+  Mem lore =>+  [RetType lore] ->+  [EntryPointType] ->+  ImpM lore r op ([Imp.ExternalValue], [Imp.Param], Destination)+compileOutParams orig_rts orig_epts = do+  ((extvs, dests), (outparams, ctx_dests)) <-+    runWriterT $ evalStateT (mkExts orig_epts orig_rts) (M.empty, M.empty)+  let ctx_dests' = map snd $ sortOn fst $ M.toList ctx_dests+  return (extvs, outparams, Destination Nothing $ ctx_dests' <> dests)+  where+    imp = lift . lift++    mkExts (TypeOpaque desc n : epts) rts = do+      let (rts', rest) = splitAt n rts+      (evs, dests) <- unzip <$> zipWithM mkParam rts' (repeat Imp.TypeDirect)+      (more_values, more_dests) <- mkExts epts rest+      return+        ( Imp.OpaqueValue desc evs : more_values,+          dests ++ more_dests+        )+    mkExts (TypeUnsigned : epts) (rt : rts) = do+      (ev, dest) <- mkParam rt Imp.TypeUnsigned+      (more_values, more_dests) <- mkExts epts rts+      return+        ( Imp.TransparentValue ev : more_values,+          dest : more_dests+        )+    mkExts (TypeDirect : epts) (rt : rts) = do+      (ev, dest) <- mkParam rt Imp.TypeDirect+      (more_values, more_dests) <- mkExts epts rts+      return+        ( Imp.TransparentValue ev : more_values,+          dest : more_dests+        )+    mkExts _ _ = return ([], [])++    mkParam MemMem {} _ =+      error "Functions may not explicitly return memory blocks."+    mkParam (MemPrim t) ept = do+      out <- imp $ newVName "scalar_out"+      tell ([Imp.ScalarParam out t], mempty)+      return (Imp.ScalarValue t ept out, ScalarDestination out)+    mkParam (MemArray t shape _ dec) ept = do+      space <- asks envDefaultSpace+      memout <- case dec of+        ReturnsNewBlock _ x _ixfun -> do+          memout <- imp $ newVName "out_mem"+          tell+            ( [Imp.MemParam memout space],+              M.singleton x $ MemoryDestination memout+            )+          return memout+        ReturnsInBlock memout _ ->+          return memout+      resultshape <- mapM inspectExtSize $ shapeDims shape+      return+        ( Imp.ArrayValue memout space t ept resultshape,+          ArrayDestination Nothing+        )++    inspectExtSize (Ext x) = do+      (memseen, arrseen) <- get+      case M.lookup x arrseen of+        Nothing -> do+          out <- imp $ newVName "out_arrsize"+          tell+            ( [Imp.ScalarParam out int64],+              M.singleton x $ ScalarDestination out+            )+          put (memseen, M.insert x out arrseen)+          return $ Var out+        Just out ->+          return $ Var out+    inspectExtSize (Free se) =+      return se++compileFunDef ::+  Mem lore =>+  FunDef lore ->+  ImpM lore r op ()+compileFunDef (FunDef entry _ fname rettype params body) =+  local (\env -> env {envFunction = Just fname}) $ do+    ((outparams, inparams, results, args), body') <- collect' compile+    emitFunction fname $ Imp.Function (isJust entry) outparams inparams body' results args+  where+    params_entry = maybe (replicate (length params) TypeDirect) fst entry+    ret_entry = maybe (replicate (length rettype) TypeDirect) snd entry+    compile = do+      (inparams, arrayds, args) <- compileInParams params params_entry+      (results, outparams, Destination _ dests) <- compileOutParams rettype ret_entry+      addFParams params+      addArrays arrayds++      let Body _ stms ses = body+      compileStms (freeIn ses) stms $+        forM_ (zip dests ses) $ \(d, se) -> copyDWIMDest d [] se []++      return (outparams, inparams, results, args)++compileBody :: (Mem lore) => Pattern lore -> Body lore -> ImpM lore r op ()+compileBody pat (Body _ bnds ses) = do+  Destination _ dests <- destinationFromPattern pat+  compileStms (freeIn ses) bnds $+    forM_ (zip dests ses) $ \(d, se) -> copyDWIMDest d [] se []++compileBody' :: [Param dec] -> Body lore -> ImpM lore r op ()+compileBody' params (Body _ bnds ses) =+  compileStms (freeIn ses) bnds $+    forM_ (zip params ses) $ \(param, se) -> copyDWIM (paramName param) [] se []++compileLoopBody :: Typed dec => [Param dec] -> Body lore -> ImpM lore r op ()+compileLoopBody mergeparams (Body _ bnds ses) = do+  -- We cannot write the results to the merge parameters immediately,+  -- as some of the results may actually *be* merge parameters, and+  -- would thus be clobbered.  Therefore, we first copy to new+  -- variables mirroring the merge parameters, and then copy this+  -- buffer to the merge parameters.  This is efficient, because the+  -- operations are all scalar operations.+  tmpnames <- mapM (newVName . (++ "_tmp") . baseString . paramName) mergeparams+  compileStms (freeIn ses) bnds $ do+    copy_to_merge_params <- forM (zip3 mergeparams tmpnames ses) $ \(p, tmp, se) ->+      case typeOf p of+        Prim pt -> do+          emit $ Imp.DeclareScalar tmp Imp.Nonvolatile pt+          emit $ Imp.SetScalar tmp $ toExp' pt se+          return $ emit $ Imp.SetScalar (paramName p) $ Imp.var tmp pt+        Mem space | Var v <- se -> do+          emit $ Imp.DeclareMem tmp space+          emit $ Imp.SetMem tmp v space+          return $ emit $ Imp.SetMem (paramName p) tmp space+        _ -> return $ return ()+    sequence_ copy_to_merge_params++compileStms :: Names -> Stms lore -> ImpM lore r op () -> ImpM lore r op ()+compileStms alive_after_stms all_stms m = do+  cb <- asks envStmsCompiler+  cb alive_after_stms all_stms m++defCompileStms ::+  (Mem lore, FreeIn op) =>+  Names ->+  Stms lore ->+  ImpM lore r op () ->+  ImpM lore r op ()+defCompileStms alive_after_stms all_stms m =+  -- We keep track of any memory blocks produced by the statements,+  -- and after the last time that memory block is used, we insert a+  -- Free.  This is very conservative, but can cut down on lifetimes+  -- in some cases.+  void $ compileStms' mempty $ stmsToList all_stms+  where+    compileStms' allocs (Let pat aux e : bs) = do+      dVars (Just e) (patternElements pat)++      e_code <-+        localAttrs (stmAuxAttrs aux) $+          collect $ compileExp pat e+      (live_after, bs_code) <- collect' $ compileStms' (patternAllocs pat <> allocs) bs+      let dies_here v =+            not (v `nameIn` live_after)+              && v `nameIn` freeIn e_code+          to_free = S.filter (dies_here . fst) allocs++      emit e_code+      mapM_ (emit . uncurry Imp.Free) to_free+      emit bs_code++      return $ freeIn e_code <> live_after+    compileStms' _ [] = do+      code <- collect m+      emit code+      return $ freeIn code <> alive_after_stms++    patternAllocs = S.fromList . mapMaybe isMemPatElem . patternElements+    isMemPatElem pe = case patElemType pe of+      Mem space -> Just (patElemName pe, space)+      _ -> Nothing++compileExp :: Pattern lore -> Exp lore -> ImpM lore r op ()+compileExp pat e = do+  ec <- asks envExpCompiler+  ec pat e++defCompileExp ::+  (Mem lore) =>+  Pattern lore ->+  Exp lore ->+  ImpM lore r op ()+defCompileExp pat (If cond tbranch fbranch _) =+  sIf (toBoolExp cond) (compileBody pat tbranch) (compileBody pat fbranch)+defCompileExp pat (Apply fname args _ _) = do+  dest <- destinationFromPattern pat+  targets <- funcallTargets dest+  args' <- catMaybes <$> mapM compileArg args+  emit $ Imp.Call targets fname args'+  where+    compileArg (se, _) = do+      t <- subExpType se+      case (se, t) of+        (_, Prim pt) -> return $ Just $ Imp.ExpArg $ toExp' pt se+        (Var v, Mem {}) -> return $ Just $ Imp.MemArg v+        _ -> return Nothing+defCompileExp pat (BasicOp op) = defCompileBasicOp pat op+defCompileExp pat (DoLoop ctx val form body) = do+  attrs <- askAttrs+  when ("unroll" `inAttrs` attrs) $+    warn (noLoc :: SrcLoc) [] "#[unroll] on loop with unknown number of iterations." -- FIXME: no location.+  dFParams mergepat+  forM_ merge $ \(p, se) ->+    when ((== 0) $ arrayRank $ paramType p) $+      copyDWIM (paramName p) [] se []++  let doBody = compileLoopBody mergepat body++  case form of+    ForLoop i _ bound loopvars -> do+      let setLoopParam (p, a)+            | Prim _ <- paramType p =+              copyDWIM (paramName p) [] (Var a) [DimFix $ Imp.vi64 i]+            | otherwise =+              return ()++      bound' <- toExp bound++      dLParams $ map fst loopvars+      sFor' i bound' $+        mapM_ setLoopParam loopvars >> doBody+    WhileLoop cond ->+      sWhile (TPrimExp $ Imp.var cond Bool) doBody++  Destination _ pat_dests <- destinationFromPattern pat+  forM_ (zip pat_dests $ map (Var . paramName . fst) merge) $ \(d, r) ->+    copyDWIMDest d [] r []+  where+    merge = ctx ++ val+    mergepat = map fst merge+defCompileExp pat (Op op) = do+  opc <- asks envOpCompiler+  opc pat op++defCompileBasicOp ::+  Mem lore =>+  Pattern lore ->+  BasicOp ->+  ImpM lore r op ()+defCompileBasicOp (Pattern _ [pe]) (SubExp se) =+  copyDWIM (patElemName pe) [] se []+defCompileBasicOp (Pattern _ [pe]) (Opaque se) =+  copyDWIM (patElemName pe) [] se []+defCompileBasicOp (Pattern _ [pe]) (UnOp op e) = do+  e' <- toExp e+  patElemName pe <~~ Imp.UnOpExp op e'+defCompileBasicOp (Pattern _ [pe]) (ConvOp conv e) = do+  e' <- toExp e+  patElemName pe <~~ Imp.ConvOpExp conv e'+defCompileBasicOp (Pattern _ [pe]) (BinOp bop x y) = do+  x' <- toExp x+  y' <- toExp y+  patElemName pe <~~ Imp.BinOpExp bop x' y'+defCompileBasicOp (Pattern _ [pe]) (CmpOp bop x y) = do+  x' <- toExp x+  y' <- toExp y+  patElemName pe <~~ Imp.CmpOpExp bop x' y'+defCompileBasicOp _ (Assert e msg loc) = do+  e' <- toExp e+  msg' <- traverse toExp msg+  emit $ Imp.Assert e' msg' loc++  attrs <- askAttrs+  when (AttrComp "warn" ["safety_checks"] `inAttrs` attrs) $+    uncurry warn loc "Safety check required at run-time."+defCompileBasicOp (Pattern _ [pe]) (Index src slice)+  | Just idxs <- sliceIndices slice =+    copyDWIM (patElemName pe) [] (Var src) $ map (DimFix . toInt64Exp) idxs+defCompileBasicOp _ Index {} =+  return ()+defCompileBasicOp (Pattern _ [pe]) (Update _ slice se) =+  sUpdate (patElemName pe) (map (fmap toInt64Exp) slice) se+defCompileBasicOp (Pattern _ [pe]) (Replicate (Shape ds) se) = do+  ds' <- mapM toExp ds+  is <- replicateM (length ds) (newVName "i")+  copy_elem <- collect $ copyDWIM (patElemName pe) (map (DimFix . Imp.vi64) is) se []+  emit $ foldl (.) id (zipWith Imp.For is ds') copy_elem+defCompileBasicOp _ Scratch {} =+  return ()+defCompileBasicOp (Pattern [] [pe]) (Iota n e s it) = do+  e' <- toExp e+  s' <- toExp s+  sFor "i" (toInt64Exp n) $ \i -> do+    let i' = sExt it $ untyped i+    x <-+      dPrimV "x" $+        TPrimExp $+          BinOpExp (Add it OverflowUndef) e' $+            BinOpExp (Mul it OverflowUndef) i' s'+    copyDWIM (patElemName pe) [DimFix i] (Var (tvVar x)) []+defCompileBasicOp (Pattern _ [pe]) (Copy src) =+  copyDWIM (patElemName pe) [] (Var src) []+defCompileBasicOp (Pattern _ [pe]) (Manifest _ src) =+  copyDWIM (patElemName pe) [] (Var src) []+defCompileBasicOp (Pattern _ [pe]) (Concat i x ys _) = do+  offs_glb <- dPrimV "tmp_offs" 0++  forM_ (x : ys) $ \y -> do+    y_dims <- arrayDims <$> lookupType y+    let rows = case drop i y_dims of+          [] -> error $ "defCompileBasicOp Concat: empty array shape for " ++ pretty y+          r : _ -> toInt64Exp r+        skip_dims = take i y_dims+        sliceAllDim d = DimSlice 0 d 1+        skip_slices = map (sliceAllDim . toInt64Exp) skip_dims+        destslice = skip_slices ++ [DimSlice (tvExp offs_glb) rows 1]+    copyDWIM (patElemName pe) destslice (Var y) []+    offs_glb <-- tvExp offs_glb + rows+defCompileBasicOp (Pattern [] [pe]) (ArrayLit es _)+  | Just vs@(v : _) <- mapM isLiteral es = do+    dest_mem <- entryArrayLocation <$> lookupArray (patElemName pe)+    dest_space <- entryMemSpace <$> lookupMemory (memLocationName dest_mem)+    let t = primValueType v+    static_array <- newVNameForFun "static_array"+    emit $ Imp.DeclareArray static_array dest_space t $ Imp.ArrayValues vs+    let static_src =+          MemLocation static_array [intConst Int64 $ fromIntegral $ length es] $+            IxFun.iota [fromIntegral $ length es]+        entry = MemVar Nothing $ MemEntry dest_space+    addVar static_array entry+    let slice = [DimSlice 0 (genericLength es) 1]+    copy t dest_mem slice static_src slice+  | otherwise =+    forM_ (zip [0 ..] es) $ \(i, e) ->+      copyDWIM (patElemName pe) [DimFix $ fromInteger i] e []+  where+    isLiteral (Constant v) = Just v+    isLiteral _ = Nothing+defCompileBasicOp _ Rearrange {} =+  return ()+defCompileBasicOp _ Rotate {} =+  return ()+defCompileBasicOp _ Reshape {} =+  return ()+defCompileBasicOp pat e =+  error $+    "ImpGen.defCompileBasicOp: Invalid pattern\n  "+      ++ pretty pat+      ++ "\nfor expression\n  "+      ++ pretty e++-- | Note: a hack to be used only for functions.+addArrays :: [ArrayDecl] -> ImpM lore r op ()+addArrays = mapM_ addArray+  where+    addArray (ArrayDecl name bt location) =+      addVar name $+        ArrayVar+          Nothing+          ArrayEntry+            { entryArrayLocation = location,+              entryArrayElemType = bt+            }++-- | Like 'dFParams', but does not create new declarations.+-- Note: a hack to be used only for functions.+addFParams :: Mem lore => [FParam lore] -> ImpM lore r op ()+addFParams = mapM_ addFParam+  where+    addFParam fparam =+      addVar (paramName fparam) $+        memBoundToVarEntry Nothing $ noUniquenessReturns $ paramDec fparam++-- | Another hack.+addLoopVar :: VName -> IntType -> ImpM lore r op ()+addLoopVar i it = addVar i $ ScalarVar Nothing $ ScalarEntry $ IntType it++dVars ::+  Mem lore =>+  Maybe (Exp lore) ->+  [PatElem lore] ->+  ImpM lore r op ()+dVars e = mapM_ dVar+  where+    dVar = dScope e . scopeOfPatElem++dFParams :: Mem lore => [FParam lore] -> ImpM lore r op ()+dFParams = dScope Nothing . scopeOfFParams++dLParams :: Mem lore => [LParam lore] -> ImpM lore r op ()+dLParams = dScope Nothing . scopeOfLParams++dPrimVol :: String -> PrimType -> Imp.TExp t -> ImpM lore r op (TV t)+dPrimVol name t e = do+  name' <- newVName name+  emit $ Imp.DeclareScalar name' Imp.Volatile t+  addVar name' $ ScalarVar Nothing $ ScalarEntry t+  name' <~~ untyped e+  return $ TV name' t++dPrim_ :: VName -> PrimType -> ImpM lore r op ()+dPrim_ name t = do+  emit $ Imp.DeclareScalar name Imp.Nonvolatile t+  addVar name $ ScalarVar Nothing $ ScalarEntry t++-- | The return type is polymorphic, so there is no guarantee it+-- actually matches the 'PrimType', but at least we have to use it+-- consistently.+dPrim :: String -> PrimType -> ImpM lore r op (TV t)+dPrim name t = do+  name' <- newVName name+  dPrim_ name' t+  return $ TV name' t++dPrimV_ :: VName -> Imp.TExp t -> ImpM lore r op ()+dPrimV_ name e = do+  dPrim_ name t+  TV name t <-- e+  where+    t = primExpType $ untyped e++dPrimV :: String -> Imp.TExp t -> ImpM lore r op (TV t)+dPrimV name e = do+  name' <- dPrim name $ primExpType $ untyped e+  name' <-- e+  return name'++dPrimVE :: String -> Imp.TExp t -> ImpM lore r op (Imp.TExp t)+dPrimVE name e = do+  name' <- dPrim name $ primExpType $ untyped e+  name' <-- e+  return $ tvExp name'++memBoundToVarEntry ::+  Maybe (Exp lore) ->+  MemBound NoUniqueness ->+  VarEntry lore+memBoundToVarEntry e (MemPrim bt) =+  ScalarVar e ScalarEntry {entryScalarType = bt}+memBoundToVarEntry e (MemMem space) =+  MemVar e $ MemEntry space+memBoundToVarEntry e (MemArray bt shape _ (ArrayIn mem ixfun)) =+  let location = MemLocation mem (shapeDims shape) $ fmap (fmap Imp.ScalarVar) ixfun+   in ArrayVar+        e+        ArrayEntry+          { entryArrayLocation = location,+            entryArrayElemType = bt+          }++infoDec ::+  Mem lore =>+  NameInfo lore ->+  MemInfo SubExp NoUniqueness MemBind+infoDec (LetName dec) = dec+infoDec (FParamName dec) = noUniquenessReturns dec+infoDec (LParamName dec) = dec+infoDec (IndexName it) = MemPrim $ IntType it++dInfo ::+  Mem lore =>+  Maybe (Exp lore) ->+  VName ->+  NameInfo lore ->+  ImpM lore r op ()+dInfo e name info = do+  let entry = memBoundToVarEntry e $ infoDec info+  case entry of+    MemVar _ entry' ->+      emit $ Imp.DeclareMem name $ entryMemSpace entry'+    ScalarVar _ entry' ->+      emit $ Imp.DeclareScalar name Imp.Nonvolatile $ entryScalarType entry'+    ArrayVar _ _ ->+      return ()+  addVar name entry++dScope ::+  Mem lore =>+  Maybe (Exp lore) ->+  Scope lore ->+  ImpM lore r op ()+dScope e = mapM_ (uncurry $ dInfo e) . M.toList++dArray :: VName -> PrimType -> ShapeBase SubExp -> MemBind -> ImpM lore r op ()+dArray name bt shape membind =+  addVar name $+    memBoundToVarEntry Nothing $ MemArray bt shape NoUniqueness membind++everythingVolatile :: ImpM lore r op a -> ImpM lore r op a+everythingVolatile = local $ \env -> env {envVolatility = Imp.Volatile}++-- | Remove the array targets.+funcallTargets :: Destination -> ImpM lore r op [VName]+funcallTargets (Destination _ dests) =+  concat <$> mapM funcallTarget dests+  where+    funcallTarget (ScalarDestination name) =+      return [name]+    funcallTarget (ArrayDestination _) =+      return []+    funcallTarget (MemoryDestination name) =+      return [name]++-- | A typed variable, which we can turn into a typed expression, or+-- use as the target for an assignment.  This is used to aid in type+-- safety when doing code generation, by keeping the types straight.+-- It is still easy to cheat when you need to.+data TV t = TV VName PrimType++-- | Create a typed variable from a name and a dynamic type.  Note+-- that there is no guarantee that the dynamic type corresponds to the+-- inferred static type, but the latter will at least have to be used+-- consistently.+mkTV :: VName -> PrimType -> TV t+mkTV = TV++-- | Convert a typed variable to a size (a SubExp).+tvSize :: TV t -> Imp.DimSize+tvSize = Var . tvVar++-- | Convert a typed variable to a similarly typed expression.+tvExp :: TV t -> Imp.TExp t+tvExp (TV v t) = Imp.TPrimExp $ Imp.var v t++-- | Extract the underlying variable name from a typed variable.+tvVar :: TV t -> VName+tvVar (TV v _) = v++-- | Compile things to 'Imp.Exp'.+class ToExp a where+  -- | Compile to an 'Imp.Exp', where the type (must must still be a+  -- primitive) is deduced monadically.+  toExp :: a -> ImpM lore r op Imp.Exp++  -- | Compile where we know the type in advance.+  toExp' :: PrimType -> a -> Imp.Exp++  toInt32Exp :: a -> Imp.TExp Int32+  toInt32Exp = TPrimExp . toExp' int32++  toInt64Exp :: a -> Imp.TExp Int64+  toInt64Exp = TPrimExp . toExp' int64++  toBoolExp :: a -> Imp.TExp Bool+  toBoolExp = TPrimExp . toExp' Bool++instance ToExp SubExp where+  toExp (Constant v) =+    return $ Imp.ValueExp v+  toExp (Var v) =+    lookupVar v >>= \case+      ScalarVar _ (ScalarEntry pt) ->+        return $ Imp.var v pt+      _ -> error $ "toExp SubExp: SubExp is not a primitive type: " ++ pretty v++  toExp' _ (Constant v) = Imp.ValueExp v+  toExp' t (Var v) = Imp.var v t++instance ToExp (PrimExp VName) where+  toExp = pure . fmap Imp.ScalarVar+  toExp' _ = fmap Imp.ScalarVar++addVar :: VName -> VarEntry lore -> ImpM lore r op ()+addVar name entry =+  modify $ \s -> s {stateVTable = M.insert name entry $ stateVTable s}++localDefaultSpace :: Imp.Space -> ImpM lore r op a -> ImpM lore r op a+localDefaultSpace space = local (\env -> env {envDefaultSpace = space})++askFunction :: ImpM lore r op (Maybe Name)+askFunction = asks envFunction++-- | Generate a 'VName', prefixed with 'askFunction' if it exists.+newVNameForFun :: String -> ImpM lore r op VName+newVNameForFun s = do+  fname <- fmap nameToString <$> askFunction+  newVName $ maybe "" (++ ".") fname ++ s++-- | Generate a 'Name', prefixed with 'askFunction' if it exists.+nameForFun :: String -> ImpM lore r op Name+nameForFun s = do+  fname <- askFunction+  return $ maybe "" (<> ".") fname <> nameFromString s++askEnv :: ImpM lore r op r+askEnv = asks envEnv++localEnv :: (r -> r) -> ImpM lore r op a -> ImpM lore r op a+localEnv f = local $ \env -> env {envEnv = f $ envEnv env}++-- | The active attributes, including those for the statement+-- currently being compiled.+askAttrs :: ImpM lore r op Attrs+askAttrs = asks envAttrs++-- | Add more attributes to what is returning by 'askAttrs'.+localAttrs :: Attrs -> ImpM lore r op a -> ImpM lore r op a+localAttrs attrs = local $ \env -> env {envAttrs = attrs <> envAttrs env}++localOps :: Operations lore r op -> ImpM lore r op a -> ImpM lore r op a+localOps ops = local $ \env ->+  env+    { envExpCompiler = opsExpCompiler ops,+      envStmsCompiler = opsStmsCompiler ops,+      envCopyCompiler = opsCopyCompiler ops,+      envOpCompiler = opsOpCompiler ops,+      envAllocCompilers = opsAllocCompilers ops+    }++-- | Get the current symbol table.+getVTable :: ImpM lore r op (VTable lore)+getVTable = gets stateVTable++putVTable :: VTable lore -> ImpM lore r op ()+putVTable vtable = modify $ \s -> s {stateVTable = vtable}++-- | Run an action with a modified symbol table.  All changes to the+-- symbol table will be reverted once the action is done!+localVTable :: (VTable lore -> VTable lore) -> ImpM lore r op a -> ImpM lore r op a+localVTable f m = do+  old_vtable <- getVTable+  putVTable $ f old_vtable+  a <- m+  putVTable old_vtable+  return a++lookupVar :: VName -> ImpM lore r op (VarEntry lore)+lookupVar name = do+  res <- gets $ M.lookup name . stateVTable+  case res of+    Just entry -> return entry+    _ -> error $ "Unknown variable: " ++ pretty name++lookupArray :: VName -> ImpM lore r op ArrayEntry+lookupArray name = do+  res <- lookupVar name+  case res of+    ArrayVar _ entry -> return entry+    _ -> error $ "ImpGen.lookupArray: not an array: " ++ pretty name++lookupMemory :: VName -> ImpM lore r op MemEntry+lookupMemory name = do+  res <- lookupVar name+  case res of+    MemVar _ entry -> return entry+    _ -> error $ "Unknown memory block: " ++ pretty name++destinationFromPattern :: Mem lore => Pattern lore -> ImpM lore r op Destination+destinationFromPattern pat =+  fmap (Destination (baseTag <$> maybeHead (patternNames pat))) . mapM inspect $+    patternElements pat+  where+    inspect patElem = do+      let name = patElemName patElem+      entry <- lookupVar name+      case entry of+        ArrayVar _ (ArrayEntry MemLocation {} _) ->+          return $ ArrayDestination Nothing+        MemVar {} ->+          return $ MemoryDestination name+        ScalarVar {} ->+          return $ ScalarDestination name++fullyIndexArray ::+  VName ->+  [Imp.TExp Int64] ->+  ImpM lore r op (VName, Imp.Space, Count Elements (Imp.TExp Int64))+fullyIndexArray name indices = do+  arr <- lookupArray name+  fullyIndexArray' (entryArrayLocation arr) indices++fullyIndexArray' ::+  MemLocation ->+  [Imp.TExp Int64] ->+  ImpM lore r op (VName, Imp.Space, Count Elements (Imp.TExp Int64))+fullyIndexArray' (MemLocation mem _ ixfun) indices = do+  space <- entryMemSpace <$> lookupMemory mem+  let indices' = case space of+        ScalarSpace ds _ ->+          let (zero_is, is) = splitFromEnd (length ds) indices+           in map (const 0) zero_is ++ is+        _ -> indices+  return+    ( mem,+      space,+      elements $ IxFun.index ixfun indices'+    )++-- More complicated read/write operations that use index functions.++copy :: CopyCompiler lore r op+copy bt dest destslice src srcslice = do+  cc <- asks envCopyCompiler+  cc bt dest destslice src srcslice++-- | Is this copy really a mapping with transpose?+isMapTransposeCopy ::+  PrimType ->+  MemLocation ->+  Slice (Imp.TExp Int64) ->+  MemLocation ->+  Slice (Imp.TExp Int64) ->+  Maybe+    ( Imp.TExp Int64,+      Imp.TExp Int64,+      Imp.TExp Int64,+      Imp.TExp Int64,+      Imp.TExp Int64+    )+isMapTransposeCopy+  bt+  (MemLocation _ _ destIxFun)+  destslice+  (MemLocation _ _ srcIxFun)+  srcslice+    | Just (dest_offset, perm_and_destshape) <- IxFun.rearrangeWithOffset destIxFun' bt_size,+      (perm, destshape) <- unzip perm_and_destshape,+      Just src_offset <- IxFun.linearWithOffset srcIxFun' bt_size,+      Just (r1, r2, _) <- isMapTranspose perm =+      isOk destshape swap r1 r2 dest_offset src_offset+    | Just dest_offset <- IxFun.linearWithOffset destIxFun' bt_size,+      Just (src_offset, perm_and_srcshape) <- IxFun.rearrangeWithOffset srcIxFun' bt_size,+      (perm, srcshape) <- unzip perm_and_srcshape,+      Just (r1, r2, _) <- isMapTranspose perm =+      isOk srcshape id r1 r2 dest_offset src_offset+    | otherwise =+      Nothing+    where+      bt_size = primByteSize bt+      swap (x, y) = (y, x)++      destIxFun' = IxFun.slice destIxFun destslice+      srcIxFun' = IxFun.slice srcIxFun srcslice++      isOk shape f r1 r2 dest_offset src_offset = do+        let (num_arrays, size_x, size_y) = getSizes shape f r1 r2+        return+          ( dest_offset,+            src_offset,+            num_arrays,+            size_x,+            size_y+          )++      getSizes shape f r1 r2 =+        let (mapped, notmapped) = splitAt r1 shape+            (pretrans, posttrans) = f $ splitAt r2 notmapped+         in (product mapped, product pretrans, product posttrans)++mapTransposeName :: PrimType -> String+mapTransposeName bt = "map_transpose_" ++ pretty bt++mapTransposeForType :: PrimType -> ImpM lore r op Name+mapTransposeForType bt = do+  let fname = nameFromString $ "builtin#" <> mapTransposeName bt++  exists <- hasFunction fname+  unless exists $ emitFunction fname $ mapTransposeFunction fname bt++  return fname++-- | Use an 'Imp.Copy' if possible, otherwise 'copyElementWise'.+defaultCopy :: CopyCompiler lore r op+defaultCopy pt dest destslice src srcslice+  | Just+      ( destoffset,+        srcoffset,+        num_arrays,+        size_x,+        size_y+        ) <-+      isMapTransposeCopy pt dest destslice src srcslice = do+    fname <- mapTransposeForType pt+    emit $+      Imp.Call+        []+        fname+        $ transposeArgs+          pt+          destmem+          (bytes destoffset)+          srcmem+          (bytes srcoffset)+          num_arrays+          size_x+          size_y+  | Just destoffset <-+      IxFun.linearWithOffset (IxFun.slice dest_ixfun destslice) pt_size,+    Just srcoffset <-+      IxFun.linearWithOffset (IxFun.slice src_ixfun srcslice) pt_size = do+    srcspace <- entryMemSpace <$> lookupMemory srcmem+    destspace <- entryMemSpace <$> lookupMemory destmem+    if isScalarSpace srcspace || isScalarSpace destspace+      then copyElementWise pt dest destslice src srcslice+      else+        emit $+          Imp.Copy+            destmem+            (bytes destoffset)+            destspace+            srcmem+            (bytes srcoffset)+            srcspace+            $ num_elems `withElemType` pt+  | otherwise =+    copyElementWise pt dest destslice src srcslice+  where+    pt_size = primByteSize pt+    num_elems = Imp.elements $ product $ sliceDims srcslice++    MemLocation destmem _ dest_ixfun = dest+    MemLocation srcmem _ src_ixfun = src++    isScalarSpace ScalarSpace {} = True+    isScalarSpace _ = False++copyElementWise :: CopyCompiler lore r op+copyElementWise bt dest destslice src srcslice = do+  let bounds = sliceDims srcslice+  is <- replicateM (length bounds) (newVName "i")+  let ivars = map Imp.vi64 is+  (destmem, destspace, destidx) <-+    fullyIndexArray' dest $ fixSlice destslice ivars+  (srcmem, srcspace, srcidx) <-+    fullyIndexArray' src $ fixSlice srcslice ivars+  vol <- asks envVolatility+  emit $+    foldl (.) id (zipWith Imp.For is $ map untyped bounds) $+      Imp.Write destmem destidx bt destspace vol $+        Imp.index srcmem srcidx bt srcspace vol++-- | Copy from here to there; both destination and source may be+-- indexeded.+copyArrayDWIM ::+  PrimType ->+  MemLocation ->+  [DimIndex (Imp.TExp Int64)] ->+  MemLocation ->+  [DimIndex (Imp.TExp Int64)] ->+  ImpM lore r op (Imp.Code op)+copyArrayDWIM+  bt+  destlocation@(MemLocation _ destshape _)+  destslice+  srclocation@(MemLocation _ srcshape _)+  srcslice+    | Just destis <- mapM dimFix destslice,+      Just srcis <- mapM dimFix srcslice,+      length srcis == length srcshape,+      length destis == length destshape = do+      (targetmem, destspace, targetoffset) <-+        fullyIndexArray' destlocation destis+      (srcmem, srcspace, srcoffset) <-+        fullyIndexArray' srclocation srcis+      vol <- asks envVolatility+      return $+        Imp.Write targetmem targetoffset bt destspace vol $+          Imp.index srcmem srcoffset bt srcspace vol+    | otherwise = do+      let destslice' =+            fullSliceNum (map toInt64Exp destshape) destslice+          srcslice' =+            fullSliceNum (map toInt64Exp srcshape) srcslice+          destrank = length $ sliceDims destslice'+          srcrank = length $ sliceDims srcslice'+      if destrank /= srcrank+        then+          error $+            "copyArrayDWIM: cannot copy to "+              ++ pretty (memLocationName destlocation)+              ++ " from "+              ++ pretty (memLocationName srclocation)+              ++ " because ranks do not match ("+              ++ pretty destrank+              ++ " vs "+              ++ pretty srcrank+              ++ ")"+        else+          if destlocation == srclocation && destslice' == srcslice'+            then return mempty -- Copy would be no-op.+            else collect $ copy bt destlocation destslice' srclocation srcslice'++-- | Like 'copyDWIM', but the target is a 'ValueDestination'+-- instead of a variable name.+copyDWIMDest ::+  ValueDestination ->+  [DimIndex (Imp.TExp Int64)] ->+  SubExp ->+  [DimIndex (Imp.TExp Int64)] ->+  ImpM lore r op ()+copyDWIMDest _ _ (Constant v) (_ : _) =+  error $+    unwords ["copyDWIMDest: constant source", pretty v, "cannot be indexed."]+copyDWIMDest pat dest_slice (Constant v) [] =+  case mapM dimFix dest_slice of+    Nothing ->+      error $+        unwords ["copyDWIMDest: constant source", pretty v, "with slice destination."]+    Just dest_is ->+      case pat of+        ScalarDestination name ->+          emit $ Imp.SetScalar name $ Imp.ValueExp v+        MemoryDestination {} ->+          error $+            unwords ["copyDWIMDest: constant source", pretty v, "cannot be written to memory destination."]+        ArrayDestination (Just dest_loc) -> do+          (dest_mem, dest_space, dest_i) <-+            fullyIndexArray' dest_loc dest_is+          vol <- asks envVolatility+          emit $ Imp.Write dest_mem dest_i bt dest_space vol $ Imp.ValueExp v+        ArrayDestination Nothing ->+          error "copyDWIMDest: ArrayDestination Nothing"+  where+    bt = primValueType v+copyDWIMDest dest dest_slice (Var src) src_slice = do+  src_entry <- lookupVar src+  case (dest, src_entry) of+    (MemoryDestination mem, MemVar _ (MemEntry space)) ->+      emit $ Imp.SetMem mem src space+    (MemoryDestination {}, _) ->+      error $+        unwords ["copyDWIMDest: cannot write", pretty src, "to memory destination."]+    (_, MemVar {}) ->+      error $+        unwords ["copyDWIMDest: source", pretty src, "is a memory block."]+    (_, ScalarVar _ (ScalarEntry _))+      | not $ null src_slice ->+        error $+          unwords ["copyDWIMDest: prim-typed source", pretty src, "with slice", pretty src_slice]+    (ScalarDestination name, _)+      | not $ null dest_slice ->+        error $+          unwords ["copyDWIMDest: prim-typed target", pretty name, "with slice", pretty dest_slice]+    (ScalarDestination name, ScalarVar _ (ScalarEntry pt)) ->+      emit $ Imp.SetScalar name $ Imp.var src pt+    (ScalarDestination name, ArrayVar _ arr)+      | Just src_is <- mapM dimFix src_slice,+        length src_slice == length (entryArrayShape arr) -> do+        let bt = entryArrayElemType arr+        (mem, space, i) <-+          fullyIndexArray' (entryArrayLocation arr) src_is+        vol <- asks envVolatility+        emit $ Imp.SetScalar name $ Imp.index mem i bt space vol+      | otherwise ->+        error $+          unwords+            [ "copyDWIMDest: prim-typed target",+              pretty name,+              "and array-typed source",+              pretty src,+              "with slice",+              pretty src_slice+            ]+    (ArrayDestination (Just dest_loc), ArrayVar _ src_arr) -> do+      let src_loc = entryArrayLocation src_arr+          bt = entryArrayElemType src_arr+      emit =<< copyArrayDWIM bt dest_loc dest_slice src_loc src_slice+    (ArrayDestination (Just dest_loc), ScalarVar _ (ScalarEntry bt))+      | Just dest_is <- mapM dimFix dest_slice -> do+        (dest_mem, dest_space, dest_i) <- fullyIndexArray' dest_loc dest_is+        vol <- asks envVolatility+        emit $ Imp.Write dest_mem dest_i bt dest_space vol (Imp.var src bt)+      | otherwise ->+        error $+          unwords+            [ "copyDWIMDest: array-typed target and prim-typed source",+              pretty src,+              "with slice",+              pretty dest_slice+            ]+    (ArrayDestination Nothing, _) ->+      return () -- Nothing to do; something else set some memory+      -- somewhere.++-- | Copy from here to there; both destination and source be+-- indexeded.  If so, they better be arrays of enough dimensions.+-- This function will generally just Do What I Mean, and Do The Right+-- Thing.  Both destination and source must be in scope.+copyDWIM ::+  VName ->+  [DimIndex (Imp.TExp Int64)] ->+  SubExp ->+  [DimIndex (Imp.TExp Int64)] ->+  ImpM lore r op ()+copyDWIM dest dest_slice src src_slice = do+  dest_entry <- lookupVar dest+  let dest_target =+        case dest_entry of+          ScalarVar _ _ ->+            ScalarDestination dest+          ArrayVar _ (ArrayEntry (MemLocation mem shape ixfun) _) ->+            ArrayDestination $ Just $ MemLocation mem shape ixfun+          MemVar _ _ ->+            MemoryDestination dest+  copyDWIMDest dest_target dest_slice src src_slice++-- | As 'copyDWIM', but implicitly 'DimFix'es the indexes.+copyDWIMFix ::+  VName ->+  [Imp.TExp Int64] ->+  SubExp ->+  [Imp.TExp Int64] ->+  ImpM lore r op ()+copyDWIMFix dest dest_is src src_is =+  copyDWIM dest (map DimFix dest_is) src (map DimFix src_is)++-- | @compileAlloc pat size space@ allocates @n@ bytes of memory in @space@,+-- writing the result to @dest@, which must be a single+-- 'MemoryDestination',+compileAlloc ::+  Mem lore =>+  Pattern lore ->+  SubExp ->+  Space ->+  ImpM lore r op ()+compileAlloc (Pattern [] [mem]) e space = do+  let e' = Imp.bytes $ toInt64Exp e+  allocator <- asks $ M.lookup space . envAllocCompilers+  case allocator of+    Nothing -> emit $ Imp.Allocate (patElemName mem) e' space+    Just allocator' -> allocator' (patElemName mem) e'+compileAlloc pat _ _ =+  error $ "compileAlloc: Invalid pattern: " ++ pretty pat++-- | The number of bytes needed to represent the array in a+-- straightforward contiguous format, as an 'Int64' expression.+typeSize :: Type -> Count Bytes (Imp.TExp Int64)+typeSize t =+  Imp.bytes $+    isInt64 (Imp.LeafExp (Imp.SizeOf $ elemType t) int64)+      * product (map (sExt64 . toInt64Exp) (arrayDims t))++--- Building blocks for constructing code.++sFor' :: VName -> Imp.Exp -> ImpM lore r op () -> ImpM lore r op ()+sFor' i bound body = do+  let it = case primExpType bound of+        IntType bound_t -> bound_t+        t -> error $ "sFor': bound " ++ pretty bound ++ " is of type " ++ pretty t+  addLoopVar i it+  body' <- collect body+  emit $ Imp.For i bound body'++sFor :: String -> Imp.TExp t -> (Imp.TExp t -> ImpM lore r op ()) -> ImpM lore r op ()+sFor i bound body = do+  i' <- newVName i+  sFor' i' (untyped bound) $+    body $ TPrimExp $ Imp.var i' $ primExpType $ untyped bound++sWhile :: Imp.TExp Bool -> ImpM lore r op () -> ImpM lore r op ()+sWhile cond body = do+  body' <- collect body+  emit $ Imp.While cond body'++sComment :: String -> ImpM lore r op () -> ImpM lore r op ()+sComment s code = do+  code' <- collect code+  emit $ Imp.Comment s code'++sIf :: Imp.TExp Bool -> ImpM lore r op () -> ImpM lore r op () -> ImpM lore r op ()+sIf cond tbranch fbranch = do+  tbranch' <- collect tbranch+  fbranch' <- collect fbranch+  emit $ Imp.If cond tbranch' fbranch'++sWhen :: Imp.TExp Bool -> ImpM lore r op () -> ImpM lore r op ()+sWhen cond tbranch = sIf cond tbranch (return ())++sUnless :: Imp.TExp Bool -> ImpM lore r op () -> ImpM lore r op ()+sUnless cond = sIf cond (return ())++sOp :: op -> ImpM lore r op ()+sOp = emit . Imp.Op++sDeclareMem :: String -> Space -> ImpM lore r op VName+sDeclareMem name space = do+  name' <- newVName name+  emit $ Imp.DeclareMem name' space+  addVar name' $ MemVar Nothing $ MemEntry space+  return name'++sAlloc_ :: VName -> Count Bytes (Imp.TExp Int64) -> Space -> ImpM lore r op ()+sAlloc_ name' size' space = do+  allocator <- asks $ M.lookup space . envAllocCompilers+  case allocator of+    Nothing -> emit $ Imp.Allocate name' size' space+    Just allocator' -> allocator' name' size'++sAlloc :: String -> Count Bytes (Imp.TExp Int64) -> Space -> ImpM lore r op VName+sAlloc name size space = do+  name' <- sDeclareMem name space+  sAlloc_ name' size space+  return name'++sArray :: String -> PrimType -> ShapeBase SubExp -> MemBind -> ImpM lore r op VName+sArray name bt shape membind = do+  name' <- newVName name+  dArray name' bt shape membind+  return name'++-- | Declare an array in row-major order in the given memory block.+sArrayInMem :: String -> PrimType -> ShapeBase SubExp -> VName -> ImpM lore r op VName+sArrayInMem name pt shape mem =+  sArray name pt shape $+    ArrayIn mem $+      IxFun.iota $ map (isInt64 . primExpFromSubExp int64) $ shapeDims shape++-- | Like 'sAllocArray', but permute the in-memory representation of the indices as specified.+sAllocArrayPerm :: String -> PrimType -> ShapeBase SubExp -> Space -> [Int] -> ImpM lore r op VName+sAllocArrayPerm name pt shape space perm = do+  let permuted_dims = rearrangeShape perm $ shapeDims shape+  mem <- sAlloc (name ++ "_mem") (typeSize (Array pt shape NoUniqueness)) space+  let iota_ixfun = IxFun.iota $ map (isInt64 . primExpFromSubExp int64) permuted_dims+  sArray name pt shape $+    ArrayIn mem $ IxFun.permute iota_ixfun $ rearrangeInverse perm++-- | Uses linear/iota index function.+sAllocArray :: String -> PrimType -> ShapeBase SubExp -> Space -> ImpM lore r op VName+sAllocArray name pt shape space =+  sAllocArrayPerm name pt shape space [0 .. shapeRank shape -1]++-- | Uses linear/iota index function.+sStaticArray :: String -> Space -> PrimType -> Imp.ArrayContents -> ImpM lore r op VName+sStaticArray name space pt vs = do+  let num_elems = case vs of+        Imp.ArrayValues vs' -> length vs'+        Imp.ArrayZeros n -> fromIntegral n+      shape = Shape [intConst Int64 $ toInteger num_elems]+  mem <- newVNameForFun $ name ++ "_mem"+  emit $ Imp.DeclareArray mem space pt vs+  addVar mem $ MemVar Nothing $ MemEntry space+  sArray name pt shape $ ArrayIn mem $ IxFun.iota [fromIntegral num_elems]++sWrite :: VName -> [Imp.TExp Int64] -> Imp.Exp -> ImpM lore r op ()+sWrite arr is v = do+  (mem, space, offset) <- fullyIndexArray arr is+  vol <- asks envVolatility+  emit $ Imp.Write mem offset (primExpType v) space vol v++sUpdate :: VName -> Slice (Imp.TExp Int64) -> SubExp -> ImpM lore r op ()+sUpdate arr slice v = copyDWIM arr slice v []++sLoopNest ::+  Shape ->+  ([Imp.TExp Int64] -> ImpM lore r op ()) ->+  ImpM lore r op ()+sLoopNest = sLoopNest' [] . shapeDims+  where+    sLoopNest' is [] f = f $ reverse is+    sLoopNest' is (d : ds) f =+      sFor "nest_i" (toInt64Exp d) $ \i -> sLoopNest' (i : is) ds f++-- | Untyped assignment.+(<~~) :: VName -> Imp.Exp -> ImpM lore r op ()+x <~~ e = emit $ Imp.SetScalar x e++infixl 3 <~~++-- | Typed assignment.+(<--) :: TV t -> Imp.TExp t -> ImpM lore r op ()+TV x _ <-- e = emit $ Imp.SetScalar x $ untyped e++infixl 3 <--++-- | Constructing an ad-hoc function that does not+-- correspond to any of the IR functions in the input program.+function ::+  Name ->+  [Imp.Param] ->+  [Imp.Param] ->+  ImpM lore r op () ->+  ImpM lore r op ()+function fname outputs inputs m = local newFunction $ do+  body <- collect $ do+    mapM_ addParam $ outputs ++ inputs+    m+  emitFunction fname $ Imp.Function False outputs inputs body [] []+  where+    addParam (Imp.MemParam name space) =+      addVar name $ MemVar Nothing $ MemEntry space+    addParam (Imp.ScalarParam name bt) =+      addVar name $ ScalarVar Nothing $ ScalarEntry bt+    newFunction env = env {envFunction = Just fname}
src/Futhark/CodeGen/ImpGen/CUDA.hs view
@@ -1,14 +1,14 @@ module Futhark.CodeGen.ImpGen.CUDA-  ( compileProg-  , Warnings-  ) where+  ( compileProg,+    Warnings,+  )+where  import Data.Bifunctor (second)--import Futhark.IR.KernelsMem import Futhark.CodeGen.ImpCode.OpenCL import Futhark.CodeGen.ImpGen.Kernels import Futhark.CodeGen.ImpGen.Kernels.ToOpenCL+import Futhark.IR.KernelsMem import Futhark.MonadFreshNames  compileProg :: MonadFreshNames m => Prog KernelsMem -> m (Warnings, Program)
src/Futhark/CodeGen/ImpGen/Kernels.hs view
@@ -1,137 +1,148 @@+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE TypeFamilies #-}+ -- | Compile a 'KernelsMem' program to imperative code with kernels. -- This is mostly (but not entirely) the same process no matter if we -- are targeting OpenCL or CUDA.  The important distinctions (the host -- level code) are introduced later. module Futhark.CodeGen.ImpGen.Kernels-  ( compileProgOpenCL-  , compileProgCUDA-  , Warnings+  ( compileProgOpenCL,+    compileProgCUDA,+    Warnings,   )-  where+where  import Control.Monad.Except import Data.Bifunctor (second)+import Data.List (foldl') import qualified Data.Map as M import Data.Maybe-import Data.List (foldl')--import Prelude hiding (quot)--import Futhark.Error-import Futhark.MonadFreshNames-import Futhark.IR.KernelsMem-import qualified Futhark.CodeGen.ImpCode.Kernels as Imp import Futhark.CodeGen.ImpCode.Kernels (bytes)+import qualified Futhark.CodeGen.ImpCode.Kernels as Imp import Futhark.CodeGen.ImpGen hiding (compileProg) import qualified Futhark.CodeGen.ImpGen import Futhark.CodeGen.ImpGen.Kernels.Base+import Futhark.CodeGen.ImpGen.Kernels.SegHist import Futhark.CodeGen.ImpGen.Kernels.SegMap import Futhark.CodeGen.ImpGen.Kernels.SegRed import Futhark.CodeGen.ImpGen.Kernels.SegScan-import Futhark.CodeGen.ImpGen.Kernels.SegHist import Futhark.CodeGen.ImpGen.Kernels.Transpose-import qualified Futhark.IR.Mem.IxFun as IxFun import Futhark.CodeGen.SetDefaultSpace-import Futhark.Util.IntegralExp (quot, divUp, IntegralExp)+import Futhark.Error+import Futhark.IR.KernelsMem+import qualified Futhark.IR.Mem.IxFun as IxFun+import Futhark.MonadFreshNames+import Futhark.Util.IntegralExp (IntegralExp, divUp, quot)+import Prelude hiding (quot)  callKernelOperations :: Operations KernelsMem HostEnv Imp.HostOp callKernelOperations =-  Operations { opsExpCompiler = expCompiler-             , opsCopyCompiler = callKernelCopy-             , opsOpCompiler = opCompiler-             , opsStmsCompiler = defCompileStms-             , opsAllocCompilers = mempty-             }+  Operations+    { opsExpCompiler = expCompiler,+      opsCopyCompiler = callKernelCopy,+      opsOpCompiler = opCompiler,+      opsStmsCompiler = defCompileStms,+      opsAllocCompilers = mempty+    }  openclAtomics, cudaAtomics :: AtomicBinOp (openclAtomics, cudaAtomics) = (flip lookup opencl, flip lookup cuda)-  where opencl = [ (Add Int32 OverflowUndef, Imp.AtomicAdd Int32)-                 , (SMax Int32, Imp.AtomicSMax Int32)-                 , (SMin Int32, Imp.AtomicSMin Int32)-                 , (UMax Int32, Imp.AtomicUMax Int32)-                 , (UMin Int32, Imp.AtomicUMin Int32)-                 , (And Int32, Imp.AtomicAnd Int32)-                 , (Or Int32, Imp.AtomicOr Int32)-                 , (Xor Int32, Imp.AtomicXor Int32)-                 ]-        cuda = opencl ++ [(FAdd Float32, Imp.AtomicFAdd Float32)]+  where+    opencl =+      [ (Add Int32 OverflowUndef, Imp.AtomicAdd Int32),+        (SMax Int32, Imp.AtomicSMax Int32),+        (SMin Int32, Imp.AtomicSMin Int32),+        (UMax Int32, Imp.AtomicUMax Int32),+        (UMin Int32, Imp.AtomicUMin Int32),+        (And Int32, Imp.AtomicAnd Int32),+        (Or Int32, Imp.AtomicOr Int32),+        (Xor Int32, Imp.AtomicXor Int32)+      ]+    cuda = opencl ++ [(FAdd Float32, Imp.AtomicFAdd Float32)] -compileProg :: MonadFreshNames m => HostEnv -> Prog KernelsMem-            -> m (Warnings, Imp.Program)+compileProg ::+  MonadFreshNames m =>+  HostEnv ->+  Prog KernelsMem ->+  m (Warnings, Imp.Program) compileProg env prog =-  second (setDefaultSpace (Imp.Space "device")) <$>-  Futhark.CodeGen.ImpGen.compileProg env callKernelOperations (Imp.Space "device") prog+  second (setDefaultSpace (Imp.Space "device"))+    <$> Futhark.CodeGen.ImpGen.compileProg env callKernelOperations (Imp.Space "device") prog  -- | Compile a 'KernelsMem' program to low-level parallel code, with -- either CUDA or OpenCL characteristics.-compileProgOpenCL, compileProgCUDA-  :: MonadFreshNames m => Prog KernelsMem -> m (Warnings, Imp.Program)+compileProgOpenCL,+  compileProgCUDA ::+    MonadFreshNames m => Prog KernelsMem -> m (Warnings, Imp.Program) compileProgOpenCL = compileProg $ HostEnv openclAtomics compileProgCUDA = compileProg $ HostEnv cudaAtomics -opCompiler :: Pattern KernelsMem -> Op KernelsMem-           -> CallKernelGen ()-+opCompiler ::+  Pattern KernelsMem ->+  Op KernelsMem ->+  CallKernelGen () opCompiler dest (Alloc e space) =   compileAlloc dest e space- opCompiler (Pattern _ [pe]) (Inner (SizeOp (GetSize key size_class))) = do   fname <- askFunction-  sOp $ Imp.GetSize (patElemName pe) (keyWithEntryPoint fname key) $-    sizeClassWithEntryPoint fname size_class-+  sOp $+    Imp.GetSize (patElemName pe) (keyWithEntryPoint fname key) $+      sizeClassWithEntryPoint fname size_class opCompiler (Pattern _ [pe]) (Inner (SizeOp (CmpSizeLe key size_class x))) = do   fname <- askFunction   let size_class' = sizeClassWithEntryPoint fname size_class   sOp . Imp.CmpSizeLe (patElemName pe) (keyWithEntryPoint fname key) size_class'     =<< toExp x- opCompiler (Pattern _ [pe]) (Inner (SizeOp (GetSizeMax size_class))) =   sOp $ Imp.GetSizeMax (patElemName pe) size_class- opCompiler (Pattern _ [pe]) (Inner (SizeOp (CalcNumGroups w64 max_num_groups_key group_size))) = do   fname <- askFunction-  max_num_groups <- dPrim "max_num_groups" int32-  sOp $ Imp.GetSize max_num_groups (keyWithEntryPoint fname max_num_groups_key) $-    sizeClassWithEntryPoint fname SizeNumGroups+  max_num_groups :: TV Int32 <- dPrim "max_num_groups" int32+  sOp $+    Imp.GetSize (tvVar max_num_groups) (keyWithEntryPoint fname max_num_groups_key) $+      sizeClassWithEntryPoint fname SizeNumGroups    -- If 'w' is small, we launch fewer groups than we normally would.   -- We don't want any idle groups.   --   -- The calculations are done with 64-bit integers to avoid overflow   -- issues.-  let num_groups_maybe_zero = BinOpExp (SMin Int64)-                              (toExp' int64 w64 `divUp`-                               sExt Int64 (toExp' int32 group_size)) $-                              sExt Int64 (Imp.vi32 max_num_groups)+  let num_groups_maybe_zero =+        sMin64+          ( toInt64Exp w64+              `divUp` sExt64 (toInt32Exp group_size)+          )+          $ sExt64 (tvExp max_num_groups)   -- We also don't want zero groups.-  let num_groups = BinOpExp (SMax Int64) 1 num_groups_maybe_zero-  patElemName pe <-- sExt Int32 num_groups-+  let num_groups = sMax64 1 num_groups_maybe_zero+  mkTV (patElemName pe) int32 <-- sExt32 num_groups opCompiler dest (Inner (SegOp op)) =   segOpCompiler dest op- opCompiler pat e =-  compilerBugS $ "opCompiler: Invalid pattern\n  " ++-  pretty pat ++ "\nfor expression\n  " ++ pretty e+  compilerBugS $+    "opCompiler: Invalid pattern\n  "+      ++ pretty pat+      ++ "\nfor expression\n  "+      ++ pretty e  sizeClassWithEntryPoint :: Maybe Name -> Imp.SizeClass -> Imp.SizeClass sizeClassWithEntryPoint fname (Imp.SizeThreshold path def) =   Imp.SizeThreshold (map f path) def-  where f (name, x) = (keyWithEntryPoint fname name, x)+  where+    f (name, x) = (keyWithEntryPoint fname name, x) sizeClassWithEntryPoint _ size_class = size_class -segOpCompiler :: Pattern KernelsMem -> SegOp SegLevel KernelsMem-              -> CallKernelGen ()+segOpCompiler ::+  Pattern KernelsMem ->+  SegOp SegLevel KernelsMem ->+  CallKernelGen () segOpCompiler pat (SegMap lvl space _ kbody) =   compileSegMap pat lvl space kbody-segOpCompiler pat (SegRed lvl@SegThread{} space reds _ kbody) =+segOpCompiler pat (SegRed lvl@SegThread {} space reds _ kbody) =   compileSegRed pat lvl space reds kbody-segOpCompiler pat (SegScan lvl@SegThread{} space scans _ kbody) =+segOpCompiler pat (SegScan lvl@SegThread {} space scans _ kbody) =   compileSegScan pat lvl space scans kbody segOpCompiler pat (SegHist (SegThread num_groups group_size _) space ops _ kbody) =   compileSegHist pat num_groups group_size space ops kbody@@ -144,7 +155,7 @@ -- otherwise protected by their own multi-versioning branches deeper -- down.  Currently the compiler will not generate multi-versioning -- that makes this a problem, but it might in the future.-checkLocalMemoryReqs :: Imp.Code -> CallKernelGen (Maybe Imp.Exp)+checkLocalMemoryReqs :: Imp.Code -> CallKernelGen (Maybe (Imp.TExp Bool)) checkLocalMemoryReqs code = do   scope <- askScope   let alloc_sizes = map (sum . localAllocSizes . Imp.kernelBody) $ getKernels code@@ -154,40 +165,35 @@   if any (`M.notMember` scope) (namesToList $ freeIn alloc_sizes)     then return Nothing     else do-    local_memory_capacity <- dPrim "local_memory_capacity" int32-    sOp $ Imp.GetSizeMax local_memory_capacity SizeLocalMemory--    let local_memory_capacity_64 =-          sExt Int64 $ Imp.vi32 local_memory_capacity-        fits size =-          unCount size .<=. local_memory_capacity_64-    return $ Just $ foldl' (.&&.) true (map fits alloc_sizes)+      local_memory_capacity :: TV Int32 <- dPrim "local_memory_capacity" int32+      sOp $ Imp.GetSizeMax (tvVar local_memory_capacity) SizeLocalMemory -  where getKernels = foldMap getKernel-        getKernel (Imp.CallKernel k) = [k]-        getKernel _ = []+      let local_memory_capacity_64 =+            sExt64 $ tvExp local_memory_capacity+          fits size =+            unCount size .<=. local_memory_capacity_64+      return $ Just $ foldl' (.&&.) true (map fits alloc_sizes)+  where+    getKernels = foldMap getKernel+    getKernel (Imp.CallKernel k) = [k]+    getKernel _ = [] -        localAllocSizes = foldMap localAllocSize-        localAllocSize (Imp.LocalAlloc _ size) = [size]-        localAllocSize _ = []+    localAllocSizes = foldMap localAllocSize+    localAllocSize (Imp.LocalAlloc _ size) = [size]+    localAllocSize _ = []  expCompiler :: ExpCompiler KernelsMem HostEnv Imp.HostOp- -- We generate a simple kernel for itoa and replicate. expCompiler (Pattern _ [pe]) (BasicOp (Iota n x s et)) = do-  n' <- toExp n   x' <- toExp x   s' <- toExp s -  sIota (patElemName pe) n' x' s' et-+  sIota (patElemName pe) (toInt64Exp n) x' s' et expCompiler (Pattern _ [pe]) (BasicOp (Replicate _ se)) =   sReplicate (patElemName pe) se- -- Allocation in the "local" space is just a placeholder. expCompiler _ (Op (Alloc _ (Space "local"))) =   return ()- -- This is a multi-versioning If created by incremental flattening. -- We need to augment the conditional with a check that any local -- memory requirements in tbranch are compatible with the hardware.@@ -199,40 +205,57 @@   fcode <- collect $ compileBody dest fbranch   check <- checkLocalMemoryReqs tcode   emit $ case check of-           Nothing -> fcode-           Just ok -> Imp.If (ok .&&. toExp' Bool cond) tcode fcode-+    Nothing -> fcode+    Just ok -> Imp.If (ok .&&. toBoolExp cond) tcode fcode expCompiler dest e =   defCompileExp dest e  callKernelCopy :: CopyCompiler KernelsMem HostEnv Imp.HostOp-callKernelCopy bt-  destloc@(MemLocation destmem _ destIxFun) destslice-  srcloc@(MemLocation srcmem srcshape srcIxFun) srcslice-  | Just (destoffset, srcoffset,-          num_arrays, size_x, size_y) <--      isMapTransposeKernel bt destloc destslice srcloc srcslice = do-+callKernelCopy+  bt+  destloc@(MemLocation destmem _ destIxFun)+  destslice+  srcloc@(MemLocation srcmem srcshape srcIxFun)+  srcslice+    | Just+        ( destoffset,+          srcoffset,+          num_arrays,+          size_x,+          size_y+          ) <-+        isMapTransposeCopy bt destloc destslice srcloc srcslice = do       fname <- mapTransposeForType bt-      emit $ Imp.Call [] fname-        [Imp.MemArg destmem, Imp.ExpArg destoffset,-         Imp.MemArg srcmem, Imp.ExpArg srcoffset,-         Imp.ExpArg num_arrays, Imp.ExpArg size_x, Imp.ExpArg size_y]--  | bt_size <- primByteSize bt,-    Just destoffset <--      IxFun.linearWithOffset (IxFun.slice destIxFun destslice) bt_size,-    Just srcoffset  <--      IxFun.linearWithOffset (IxFun.slice srcIxFun srcslice) bt_size = do-        let num_elems = Imp.elements $ product $ map (toExp' int32) srcshape-        srcspace <- entryMemSpace <$> lookupMemory srcmem-        destspace <- entryMemSpace <$> lookupMemory destmem-        emit $ Imp.Copy-          destmem (bytes destoffset) destspace-          srcmem (bytes srcoffset) srcspace $-          num_elems `Imp.withElemType` bt--  | otherwise = sCopy bt destloc destslice srcloc srcslice+      emit $+        Imp.Call+          []+          fname+          [ Imp.MemArg destmem,+            Imp.ExpArg $ untyped destoffset,+            Imp.MemArg srcmem,+            Imp.ExpArg $ untyped srcoffset,+            Imp.ExpArg $ untyped num_arrays,+            Imp.ExpArg $ untyped size_x,+            Imp.ExpArg $ untyped size_y+          ]+    | bt_size <- primByteSize bt,+      Just destoffset <-+        IxFun.linearWithOffset (IxFun.slice destIxFun destslice) bt_size,+      Just srcoffset <-+        IxFun.linearWithOffset (IxFun.slice srcIxFun srcslice) bt_size = do+      let num_elems = Imp.elements $ product $ map toInt64Exp srcshape+      srcspace <- entryMemSpace <$> lookupMemory srcmem+      destspace <- entryMemSpace <$> lookupMemory destmem+      emit $+        Imp.Copy+          destmem+          (bytes $ sExt64 destoffset)+          destspace+          srcmem+          (bytes $ sExt64 srcoffset)+          srcspace+          $ num_elems `Imp.withElemType` bt+    | otherwise = sCopy bt destloc destslice srcloc srcslice  mapTransposeForType :: PrimType -> CallKernelGen Name mapTransposeForType bt = do@@ -244,129 +267,123 @@   return fname  mapTransposeName :: PrimType -> String-mapTransposeName bt = "map_transpose_" ++ pretty bt+mapTransposeName bt = "gpu_map_transpose_" ++ pretty bt  mapTransposeFunction :: PrimType -> Imp.Function mapTransposeFunction bt =   Imp.Function False [] params transpose_code [] []--  where params = [memparam destmem, intparam destoffset,-                  memparam srcmem, intparam srcoffset,-                  intparam num_arrays, intparam x, intparam y]--        space = Space "device"-        memparam v = Imp.MemParam v space-        intparam v = Imp.ScalarParam v $ IntType Int32--        [destmem, destoffset, srcmem, srcoffset,-         num_arrays, x, y,-         mulx, muly, block] =-           zipWith (VName . nameFromString)-           ["destmem",-             "destoffset",-             "srcmem",-             "srcoffset",-             "num_arrays",-             "x_elems",-             "y_elems",-             -- The following is only used for low width/height-             -- transpose kernels-             "mulx",-             "muly",-             "block"-            ]-           [0..]--        v32 v = Imp.var v int32--        block_dim_int = 16--        block_dim :: IntegralExp a => a-        block_dim = 16+  where+    params =+      [ memparam destmem,+        intparam destoffset,+        memparam srcmem,+        intparam srcoffset,+        intparam num_arrays,+        intparam x,+        intparam y+      ] -        -- When an input array has either width==1 or height==1, performing a-        -- transpose will be the same as performing a copy.-        can_use_copy =-          let onearr = CmpOpExp (CmpEq $ IntType Int32) (v32 num_arrays) 1-              height_is_one = CmpOpExp (CmpEq $ IntType Int32) (v32 y) 1-              width_is_one = CmpOpExp (CmpEq $ IntType Int32) (v32 x) 1-          in onearr .&&. (width_is_one .||. height_is_one)+    space = Space "device"+    memparam v = Imp.MemParam v space+    intparam v = Imp.ScalarParam v $ IntType Int32 -        transpose_code =-          Imp.If input_is_empty mempty $ mconcat-          [ Imp.DeclareScalar muly Imp.Nonvolatile (IntType Int32)-          , Imp.SetScalar muly $ block_dim `quot` v32 x-          , Imp.DeclareScalar mulx Imp.Nonvolatile (IntType Int32)-          , Imp.SetScalar mulx $ block_dim `quot` v32 y-          , Imp.If can_use_copy copy_code $-            Imp.If should_use_lowwidth (callTransposeKernel TransposeLowWidth) $-            Imp.If should_use_lowheight (callTransposeKernel TransposeLowHeight) $-            Imp.If should_use_small (callTransposeKernel TransposeSmall) $-            callTransposeKernel TransposeNormal]+    [ destmem,+      destoffset,+      srcmem,+      srcoffset,+      num_arrays,+      x,+      y,+      mulx,+      muly,+      block+      ] =+        zipWith+          (VName . nameFromString)+          [ "destmem",+            "destoffset",+            "srcmem",+            "srcoffset",+            "num_arrays",+            "x_elems",+            "y_elems",+            -- The following is only used for low width/height+            -- transpose kernels+            "mulx",+            "muly",+            "block"+          ]+          [0 ..] -        input_is_empty =-          v32 num_arrays .==. 0 .||. v32 x .==. 0 .||. v32 y .==. 0+    block_dim_int = 16 -        should_use_small = BinOpExp LogAnd-          (CmpOpExp (CmpSle Int32) (v32 x) (block_dim `quot` 2))-          (CmpOpExp (CmpSle Int32) (v32 y) (block_dim `quot` 2))+    block_dim :: IntegralExp a => a+    block_dim = 16 -        should_use_lowwidth = BinOpExp LogAnd-          (CmpOpExp (CmpSle Int32) (v32 x) (block_dim `quot` 2))-          (CmpOpExp (CmpSlt Int32) block_dim (v32 y))+    -- When an input array has either width==1 or height==1, performing a+    -- transpose will be the same as performing a copy.+    can_use_copy =+      let onearr = Imp.vi32 num_arrays .==. 1+          height_is_one = Imp.vi32 y .==. 1+          width_is_one = Imp.vi32 x .==. 1+       in onearr .&&. (width_is_one .||. height_is_one) -        should_use_lowheight = BinOpExp LogAnd-          (CmpOpExp (CmpSle Int32) (v32 y) (block_dim `quot` 2))-          (CmpOpExp (CmpSlt Int32) block_dim (v32 x))+    transpose_code =+      Imp.If input_is_empty mempty $+        mconcat+          [ Imp.DeclareScalar muly Imp.Nonvolatile (IntType Int32),+            Imp.SetScalar muly $ untyped $ block_dim `quot` Imp.vi32 x,+            Imp.DeclareScalar mulx Imp.Nonvolatile (IntType Int32),+            Imp.SetScalar mulx $ untyped $ block_dim `quot` Imp.vi32 y,+            Imp.If can_use_copy copy_code $+              Imp.If should_use_lowwidth (callTransposeKernel TransposeLowWidth) $+                Imp.If should_use_lowheight (callTransposeKernel TransposeLowHeight) $+                  Imp.If should_use_small (callTransposeKernel TransposeSmall) $+                    callTransposeKernel TransposeNormal+          ] -        copy_code =-          let num_bytes =-                v32 x * v32 y * Imp.LeafExp (Imp.SizeOf bt) (IntType Int32)-          in Imp.Copy-               destmem (Imp.Count $ v32 destoffset) space-               srcmem (Imp.Count $ v32 srcoffset) space-               (Imp.Count num_bytes)+    input_is_empty =+      Imp.vi32 num_arrays .==. 0 .||. Imp.vi32 x .==. 0 .||. Imp.vi32 y .==. 0 -        callTransposeKernel =-          Imp.Op . Imp.CallKernel .-          mapTransposeKernel (mapTransposeName bt) block_dim_int-          (destmem, v32 destoffset, srcmem, v32 srcoffset,-            v32 x, v32 y,-            v32 mulx, v32 muly, v32 num_arrays,-            block) bt+    should_use_small =+      Imp.vi32 x .<=. (block_dim `quot` 2)+        .&&. Imp.vi32 y .<=. (block_dim `quot` 2) -isMapTransposeKernel :: PrimType-                     -> MemLocation -> Slice Imp.Exp-                     -> MemLocation -> Slice Imp.Exp-                     -> Maybe (Imp.Exp, Imp.Exp,-                               Imp.Exp, Imp.Exp, Imp.Exp)-isMapTransposeKernel bt-  (MemLocation _ _ destIxFun) destslice-  (MemLocation _ _ srcIxFun) srcslice-  | Just (dest_offset, perm_and_destshape) <- IxFun.rearrangeWithOffset destIxFun' bt_size,-    (perm, destshape) <- unzip perm_and_destshape,-    Just src_offset <- IxFun.linearWithOffset srcIxFun' bt_size,-    Just (r1, r2, _) <- isMapTranspose perm =-      isOk destshape swap r1 r2 dest_offset src_offset-  | Just dest_offset <- IxFun.linearWithOffset destIxFun' bt_size,-    Just (src_offset, perm_and_srcshape) <- IxFun.rearrangeWithOffset srcIxFun' bt_size,-    (perm, srcshape) <- unzip perm_and_srcshape,-    Just (r1, r2, _) <- isMapTranspose perm =-      isOk srcshape id r1 r2 dest_offset src_offset-  | otherwise =-      Nothing-  where bt_size = primByteSize bt-        swap (x,y) = (y,x)+    should_use_lowwidth =+      Imp.vi32 x .<=. (block_dim `quot` 2)+        .&&. block_dim .<. Imp.vi32 y -        destIxFun' = IxFun.slice destIxFun destslice-        srcIxFun' = IxFun.slice srcIxFun srcslice+    should_use_lowheight =+      Imp.vi32 y .<=. (block_dim `quot` 2)+        .&&. block_dim .<. Imp.vi32 x -        isOk shape f r1 r2 dest_offset src_offset = do-          let (num_arrays, size_x, size_y) = getSizes shape f r1 r2-          return (dest_offset, src_offset,-                  num_arrays, size_x, size_y)+    copy_code =+      let num_bytes =+            sExt64 $+              Imp.vi32 x * Imp.vi32 y * isInt32 (Imp.LeafExp (Imp.SizeOf bt) (IntType Int32))+       in Imp.Copy+            destmem+            (Imp.Count $ sExt64 $ Imp.vi32 destoffset)+            space+            srcmem+            (Imp.Count $ sExt64 $ Imp.vi32 srcoffset)+            space+            (Imp.Count num_bytes) -        getSizes shape f r1 r2 =-          let (mapped, notmapped) = splitAt r1 shape-              (pretrans, posttrans) = f $ splitAt r2 notmapped-          in (product mapped, product pretrans, product posttrans)+    callTransposeKernel =+      Imp.Op . Imp.CallKernel+        . mapTransposeKernel+          (mapTransposeName bt)+          block_dim_int+          ( destmem,+            Imp.vi32 destoffset,+            srcmem,+            Imp.vi32 srcoffset,+            Imp.vi32 x,+            Imp.vi32 y,+            Imp.vi32 mulx,+            Imp.vi32 muly,+            Imp.vi32 num_arrays,+            block+          )+          bt
src/Futhark/CodeGen/ImpGen/Kernels/Base.hs view
@@ -1,1514 +1,1699 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeFamilies #-}-module Futhark.CodeGen.ImpGen.Kernels.Base-  ( KernelConstants (..)-  , keyWithEntryPoint-  , CallKernelGen-  , InKernelGen-  , HostEnv (..)-  , KernelEnv (..)-  , computeThreadChunkSize-  , groupReduce-  , groupScan-  , isActive-  , sKernelThread-  , sKernelGroup-  , sReplicate-  , sIota-  , sCopy-  , compileThreadResult-  , compileGroupResult-  , virtualiseGroups-  , groupLoop-  , kernelLoop-  , groupCoverSpace-  , precomputeSegOpIDs--  , atomicUpdateLocking-  , AtomicBinOp-  , Locking(..)-  , AtomicUpdate(..)-  , DoAtomicUpdate-  )-  where--import Control.Monad.Except-import Data.Maybe-import qualified Data.Map.Strict as M-import qualified Data.Set as S-import Data.List (elemIndex, find, nub, zip4)--import Prelude hiding (quot, rem)--import Futhark.Error-import Futhark.MonadFreshNames-import Futhark.Transform.Rename-import Futhark.IR.KernelsMem-import qualified Futhark.IR.Mem.IxFun as IxFun-import qualified Futhark.CodeGen.ImpCode.Kernels as Imp-import Futhark.CodeGen.ImpGen-import Futhark.Util.IntegralExp (divUp, quot, rem)-import Futhark.Util (chunks, maybeNth, mapAccumLM, takeLast, dropLast)--newtype HostEnv = HostEnv-  { hostAtomics :: AtomicBinOp }--data KernelEnv = KernelEnv-  { kernelAtomics :: AtomicBinOp-  , kernelConstants :: KernelConstants-  }--type CallKernelGen = ImpM KernelsMem HostEnv Imp.HostOp-type InKernelGen = ImpM KernelsMem KernelEnv Imp.KernelOp--data KernelConstants =-  KernelConstants-  { kernelGlobalThreadId :: Imp.Exp-  , kernelLocalThreadId :: Imp.Exp-  , kernelGroupId :: Imp.Exp-  , kernelGlobalThreadIdVar :: VName-  , kernelLocalThreadIdVar :: VName-  , kernelGroupIdVar :: VName-  , kernelNumGroups :: Imp.Exp-  , kernelGroupSize :: Imp.Exp-  , kernelNumThreads :: Imp.Exp-  , kernelWaveSize :: Imp.Exp-  , kernelThreadActive :: Imp.Exp-  , kernelLocalIdMap :: M.Map [SubExp] [Imp.Exp]-    -- ^ A mapping from dimensions of nested SegOps to already-    -- computed local thread IDs.-  }--segOpSizes :: Stms KernelsMem -> S.Set [SubExp]-segOpSizes = onStms-  where onStms = foldMap (onExp . stmExp)-        onExp (Op (Inner (SegOp op))) =-          S.singleton $ map snd $ unSegSpace $ segSpace op-        onExp (If _ tbranch fbranch _) =-          onStms (bodyStms tbranch) <> onStms (bodyStms fbranch)-        onExp (DoLoop _ _ _ body) =-          onStms (bodyStms body)-        onExp _ = mempty--precomputeSegOpIDs :: Stms KernelsMem -> InKernelGen a -> InKernelGen a-precomputeSegOpIDs stms m = do-  ltid <- kernelLocalThreadId . kernelConstants <$> askEnv-  new_ids <- M.fromList <$> mapM (mkMap ltid) (S.toList (segOpSizes stms))-  let f env = env { kernelConstants =-                      (kernelConstants env) { kernelLocalIdMap = new_ids }-                  }-  localEnv f m-  where mkMap ltid dims = do-          dims' <- mapM toExp dims-          ids' <- mapM (dPrimVE "ltid_pre") $ unflattenIndex dims' ltid-          return (dims, ids')--keyWithEntryPoint :: Maybe Name -> Name -> Name-keyWithEntryPoint fname key =-  nameFromString $ maybe "" ((++".") . nameToString) fname ++ nameToString key--allocLocal :: AllocCompiler KernelsMem r Imp.KernelOp-allocLocal mem size =-  sOp $ Imp.LocalAlloc mem size--kernelAlloc :: Pattern KernelsMem-            -> SubExp -> Space-            -> InKernelGen ()-kernelAlloc (Pattern _ [_]) _ ScalarSpace{} =-  -- Handled by the declaration of the memory block, which is then-  -- translated to an actual scalar variable during C code generation.-  return ()-kernelAlloc (Pattern _ [mem]) size (Space "local") = do-  size' <- toExp size-  allocLocal (patElemName mem) $ Imp.bytes size'-kernelAlloc (Pattern _ [mem]) _ _ =-  compilerLimitationS $ "Cannot allocate memory block " ++ pretty mem ++ " in kernel."-kernelAlloc dest _ _ =-  error $ "Invalid target for in-kernel allocation: " ++ show dest--splitSpace :: (ToExp w, ToExp i, ToExp elems_per_thread) =>-              Pattern KernelsMem -> SplitOrdering -> w -> i -> elems_per_thread-           -> ImpM lore r op ()-splitSpace (Pattern [] [size]) o w i elems_per_thread = do-  num_elements <- Imp.elements <$> toExp w-  i' <- toExp i-  elems_per_thread' <- Imp.elements <$> toExp elems_per_thread-  computeThreadChunkSize o i' elems_per_thread' num_elements (patElemName size)-splitSpace pat _ _ _ _ =-  error $ "Invalid target for splitSpace: " ++ pretty pat--compileThreadExp :: ExpCompiler KernelsMem KernelEnv Imp.KernelOp-compileThreadExp (Pattern _ [dest]) (BasicOp (ArrayLit es _)) =-  forM_ (zip [0..] es) $ \(i,e) ->-  copyDWIMFix (patElemName dest) [fromIntegral (i::Int32)] e []-compileThreadExp dest e =-  defCompileExp dest e----- | Assign iterations of a for-loop to all threads in the kernel.--- The passed-in function is invoked with the (symbolic) iteration.--- 'threadOperations' will be in effect in the body.  For--- multidimensional loops, use 'groupCoverSpace'.-kernelLoop :: Imp.Exp -> Imp.Exp -> Imp.Exp-           -> (Imp.Exp -> InKernelGen ()) -> InKernelGen ()-kernelLoop tid num_threads n f =-  localOps threadOperations $-  if n == num_threads then-    f tid-  else do-    -- Compute how many elements this thread is responsible for.-    -- Formula: (n - tid) / num_threads (rounded up).-    let elems_for_this = (n - tid) `divUp` num_threads--    sFor "i" elems_for_this $ \i -> f $-      i * num_threads + tid---- | Assign iterations of a for-loop to threads in the workgroup.  The--- passed-in function is invoked with the (symbolic) iteration.  For--- multidimensional loops, use 'groupCoverSpace'.-groupLoop :: Imp.Exp-          -> (Imp.Exp -> InKernelGen ()) -> InKernelGen ()-groupLoop n f = do-  constants <- kernelConstants <$> askEnv-  kernelLoop (kernelLocalThreadId constants) (kernelGroupSize constants) n f---- | Iterate collectively though a multidimensional space, such that--- all threads in the group participate.  The passed-in function is--- invoked with a (symbolic) point in the index space.-groupCoverSpace :: [Imp.Exp]-                -> ([Imp.Exp] -> InKernelGen ()) -> InKernelGen ()-groupCoverSpace ds f =-  groupLoop (product ds) $ f . unflattenIndex ds--compileGroupExp :: ExpCompiler KernelsMem KernelEnv Imp.KernelOp--- The static arrays stuff does not work inside kernels.-compileGroupExp (Pattern _ [dest]) (BasicOp (ArrayLit es _)) =-  forM_ (zip [0..] es) $ \(i,e) ->-  copyDWIMFix (patElemName dest) [fromIntegral (i::Int32)] e []-compileGroupExp (Pattern _ [dest]) (BasicOp (Replicate ds se)) = do-  ds' <- mapM toExp $ shapeDims ds-  groupCoverSpace ds' $ \is ->-    copyDWIMFix (patElemName dest) is se (drop (shapeRank ds) is)-  sOp $ Imp.Barrier Imp.FenceLocal-compileGroupExp (Pattern _ [dest]) (BasicOp (Iota n e s _)) = do-  n' <- toExp n-  e' <- toExp e-  s' <- toExp s-  groupLoop n' $ \i' -> do-    x <- dPrimV "x" $ e' + i' * s'-    copyDWIMFix (patElemName dest) [i'] (Var x) []-  sOp $ Imp.Barrier Imp.FenceLocal-compileGroupExp dest e =-  defCompileExp dest e--sanityCheckLevel :: SegLevel -> InKernelGen ()-sanityCheckLevel SegThread{} = return ()-sanityCheckLevel SegGroup{} =-  error "compileGroupOp: unexpected group-level SegOp."--localThreadIDs :: [SubExp] -> InKernelGen [Imp.Exp]-localThreadIDs dims = do-  ltid <- kernelLocalThreadId . kernelConstants <$> askEnv-  dims' <- mapM toExp dims-  fromMaybe (unflattenIndex dims' ltid) .-    M.lookup dims . kernelLocalIdMap . kernelConstants <$> askEnv--compileGroupSpace :: SegLevel -> SegSpace -> InKernelGen ()-compileGroupSpace lvl space = do-  sanityCheckLevel lvl-  let (ltids, dims) = unzip $ unSegSpace space-  zipWithM_ dPrimV_ ltids =<< localThreadIDs dims-  ltid <- kernelLocalThreadId . kernelConstants <$> askEnv-  dPrimV_ (segFlat space) ltid---- Construct the necessary lock arrays for an intra-group histogram.-prepareIntraGroupSegHist :: Count GroupSize SubExp-                         -> [HistOp KernelsMem]-                         -> InKernelGen [[Imp.Exp] -> InKernelGen ()]-prepareIntraGroupSegHist group_size =-  fmap snd . mapAccumLM onOp Nothing-  where-    onOp l op = do--      constants <- kernelConstants <$> askEnv-      atomicBinOp <- kernelAtomics <$> askEnv--      let local_subhistos = histDest op--      case (l, atomicUpdateLocking atomicBinOp $ histOp op) of-        (_, AtomicPrim f) -> return (l, f (Space "local") local_subhistos)-        (_, AtomicCAS f) -> return (l, f (Space "local") local_subhistos)-        (Just l', AtomicLocking f) -> return (l, f l' (Space "local") local_subhistos)-        (Nothing, AtomicLocking f) -> do-          locks <- newVName "locks"-          num_locks <- toExp $ unCount group_size--          let dims = map (toExp' int32) $-                     shapeDims (histShape op) ++-                     [histWidth op]-              l' = Locking locks 0 1 0 (pure . (`rem` num_locks) . flattenIndex dims)-              locks_t = Array int32 (Shape [unCount group_size]) NoUniqueness--          locks_mem <- sAlloc "locks_mem" (typeSize locks_t) $ Space "local"-          dArray locks int32 (arrayShape locks_t) $-            ArrayIn locks_mem $ IxFun.iota $-            map (primExpFromSubExp int32) $ arrayDims locks_t--          sComment "All locks start out unlocked" $-            groupCoverSpace [kernelGroupSize constants] $ \is ->-            copyDWIMFix locks is (intConst Int32 0) []--          return (Just l', f l' (Space "local") local_subhistos)--whenActive :: SegLevel -> SegSpace -> InKernelGen () -> InKernelGen ()-whenActive lvl space m-  | SegNoVirtFull <- segVirt lvl = m-  | otherwise = sWhen (isActive $ unSegSpace space) m--compileGroupOp :: OpCompiler KernelsMem KernelEnv Imp.KernelOp--compileGroupOp pat (Alloc size space) =-  kernelAlloc pat size space--compileGroupOp pat (Inner (SizeOp (SplitSpace o w i elems_per_thread))) =-  splitSpace pat o w i elems_per_thread--compileGroupOp pat (Inner (SegOp (SegMap lvl space _ body))) = do-  void $ compileGroupSpace lvl space--  whenActive lvl space $ localOps threadOperations $-    compileStms mempty (kernelBodyStms body) $-    zipWithM_ (compileThreadResult space) (patternElements pat) $-    kernelBodyResult body--  sOp $ Imp.ErrorSync Imp.FenceLocal--compileGroupOp pat (Inner (SegOp (SegScan lvl space scans _ body))) = do-  compileGroupSpace lvl space-  let (ltids, dims) = unzip $ unSegSpace space-  dims' <- mapM toExp dims--  whenActive lvl space $-    compileStms mempty (kernelBodyStms body) $-    forM_ (zip (patternNames pat) $ kernelBodyResult body) $ \(dest, res) ->-    copyDWIMFix dest-    (map (`Imp.var` int32) ltids)-    (kernelResultSubExp res) []--  sOp $ Imp.ErrorSync Imp.FenceLocal--  let segment_size = last dims'-      crossesSegment from to = (to-from) .>. (to `rem` segment_size)--  -- groupScan needs to treat the scan output as a one-dimensional-  -- array of scan elements, so we invent some new flattened arrays-  -- here.  XXX: this assumes that the original index function is just-  -- row-major, but does not actually verify it.-  dims_flat <- dPrimV "dims_flat" $ product dims'-  let flattened pe = do-        MemLocation mem _ _ <--          entryArrayLocation <$> lookupArray (patElemName pe)-        let pe_t = typeOf pe-            arr_dims = Var dims_flat : drop (length dims') (arrayDims pe_t)-        sArray (baseString (patElemName pe) ++ "_flat")-          (elemType pe_t) (Shape arr_dims) $-          ArrayIn mem $ IxFun.iota $ map (primExpFromSubExp int32) arr_dims--      num_scan_results = sum $ map (length . segBinOpNeutral) scans--  arrs_flat <- mapM flattened $ take num_scan_results $ patternElements pat--  forM_ scans $ \scan -> do-    let scan_op = segBinOpLambda scan-    groupScan (Just crossesSegment) (product dims') (product dims') scan_op arrs_flat--compileGroupOp pat (Inner (SegOp (SegRed lvl space ops _ body))) = do-  compileGroupSpace lvl space--  let (ltids, dims) = unzip $ unSegSpace space-      (red_pes, map_pes) =-        splitAt (segBinOpResults ops) $ patternElements pat--  dims' <- mapM toExp dims--  let mkTempArr t =-        sAllocArray "red_arr" (elemType t) (Shape dims <> arrayShape t) $ Space "local"-  tmp_arrs <- mapM mkTempArr $ concatMap (lambdaReturnType . segBinOpLambda) ops-  let tmps_for_ops = chunks (map (length . segBinOpNeutral) ops) tmp_arrs--  whenActive lvl space $-    compileStms mempty (kernelBodyStms body) $ do-    let (red_res, map_res) =-          splitAt (segBinOpResults ops) $ kernelBodyResult body-    forM_ (zip tmp_arrs red_res) $ \(dest, res) ->-      copyDWIMFix dest (map (`Imp.var` int32) ltids) (kernelResultSubExp res) []-    zipWithM_ (compileThreadResult space) map_pes map_res--  sOp $ Imp.ErrorSync Imp.FenceLocal--  case dims' of-    -- Nonsegmented case (or rather, a single segment) - this we can-    -- handle directly with a group-level reduction.-    [dim'] -> do-      forM_ (zip ops tmps_for_ops) $ \(op, tmps) ->-        groupReduce dim' (segBinOpLambda op) tmps--      sOp $ Imp.ErrorSync Imp.FenceLocal--      forM_ (zip red_pes tmp_arrs) $ \(pe, arr) ->-        copyDWIMFix (patElemName pe) [] (Var arr) [0]--    _ -> do-      -- Segmented intra-group reductions are turned into (regular)-      -- segmented scans.  It is possible that this can be done-      -- better, but at least this approach is simple.--      -- groupScan operates on flattened arrays.  This does not-      -- involve copying anything; merely playing with the index-      -- function.-      dims_flat <- dPrimV "dims_flat" $ product dims'-      let flatten arr = do-            ArrayEntry arr_loc pt <- lookupArray arr-            let flat_shape = Shape $ Var dims_flat :-                             drop (length ltids) (memLocationShape arr_loc)-            sArray "red_arr_flat" pt flat_shape $-              ArrayIn (memLocationName arr_loc) $-              IxFun.iota $ map (primExpFromSubExp int32) $ shapeDims flat_shape--      let segment_size = last dims'-          crossesSegment from to = (to-from) .>. (to `rem` segment_size)--      forM_ (zip ops tmps_for_ops) $ \(op, tmps) -> do-        tmps_flat <- mapM flatten tmps-        groupScan (Just crossesSegment) (product dims') (product dims')-          (segBinOpLambda op) tmps_flat--      sOp $ Imp.ErrorSync Imp.FenceLocal--      forM_ (zip red_pes tmp_arrs) $ \(pe, arr) ->-        copyDWIM (patElemName pe) [] (Var arr)-        (map (unitSlice 0) (init dims') ++ [DimFix $ last dims'-1])--      sOp $ Imp.Barrier Imp.FenceLocal--compileGroupOp pat (Inner (SegOp (SegHist lvl space ops _ kbody))) = do-  compileGroupSpace lvl space-  let ltids = map fst $ unSegSpace space--  -- We don't need the red_pes, because it is guaranteed by our type-  -- rules that they occupy the same memory as the destinations for-  -- the ops.-  let num_red_res = length ops + sum (map (length . histNeutral) ops)-      (_red_pes, map_pes) =-        splitAt num_red_res $ patternElements pat--  ops' <- prepareIntraGroupSegHist (segGroupSize lvl) ops--  -- Ensure that all locks have been initialised.-  sOp $ Imp.Barrier Imp.FenceLocal--  whenActive lvl space $-    compileStms mempty (kernelBodyStms kbody) $ do-    let (red_res, map_res) = splitAt num_red_res $ kernelBodyResult kbody-        (red_is, red_vs) = splitAt (length ops) $ map kernelResultSubExp red_res-    zipWithM_ (compileThreadResult space) map_pes map_res--    let vs_per_op = chunks (map (length . histDest) ops) red_vs--    forM_ (zip4 red_is vs_per_op ops' ops) $-      \(bin, op_vs, do_op, HistOp dest_w _ _ _ shape lam) -> do-        let bin' = toExp' int32 bin-            dest_w' = toExp' int32 dest_w-            bin_in_bounds = 0 .<=. bin' .&&. bin' .<. dest_w'-            bin_is = map (`Imp.var` int32) (init ltids) ++ [bin']-            vs_params = takeLast (length op_vs) $ lambdaParams lam--        sComment "perform atomic updates" $-          sWhen bin_in_bounds $ do-          dLParams $ lambdaParams lam-          sLoopNest shape $ \is -> do-            forM_ (zip vs_params op_vs) $ \(p, v) ->-              copyDWIMFix (paramName p) [] v is-            do_op (bin_is ++ is)--  sOp $ Imp.ErrorSync Imp.FenceLocal--compileGroupOp pat _ =-  compilerBugS $ "compileGroupOp: cannot compile rhs of binding " ++ pretty pat--compileThreadOp :: OpCompiler KernelsMem KernelEnv Imp.KernelOp-compileThreadOp pat (Alloc size space) =-  kernelAlloc pat size space-compileThreadOp pat (Inner (SizeOp (SplitSpace o w i elems_per_thread))) =-  splitSpace pat o w i elems_per_thread-compileThreadOp pat _ =-  compilerBugS $ "compileThreadOp: cannot compile rhs of binding " ++ pretty pat---- | Locking strategy used for an atomic update.-data Locking =-  Locking { lockingArray :: VName-            -- ^ Array containing the lock.-          , lockingIsUnlocked :: Imp.Exp-            -- ^ Value for us to consider the lock free.-          , lockingToLock :: Imp.Exp-            -- ^ What to write when we lock it.-          , lockingToUnlock :: Imp.Exp-            -- ^ What to write when we unlock it.-          , lockingMapping :: [Imp.Exp] -> [Imp.Exp]-            -- ^ A transformation from the logical lock index to the-            -- physical position in the array.  This can also be used-            -- to make the lock array smaller.-          }---- | A function for generating code for an atomic update.  Assumes--- that the bucket is in-bounds.-type DoAtomicUpdate lore r =-  Space -> [VName] -> [Imp.Exp] -> ImpM lore r Imp.KernelOp ()---- | The mechanism that will be used for performing the atomic update.--- Approximates how efficient it will be.  Ordered from most to least--- efficient.-data AtomicUpdate lore r-  = AtomicPrim (DoAtomicUpdate lore r)-    -- ^ Supported directly by primitive.-  | AtomicCAS (DoAtomicUpdate lore r)-    -- ^ Can be done by efficient swaps.-  | AtomicLocking (Locking -> DoAtomicUpdate lore r)-    -- ^ Requires explicit locking.---- | Is there an atomic t'BinOp' corresponding to this t'BinOp'?-type AtomicBinOp =-  BinOp ->-  Maybe (VName -> VName -> Count Imp.Elements Imp.Exp -> Imp.Exp -> Imp.AtomicOp)---- | Do an atomic update corresponding to a binary operator lambda.-atomicUpdateLocking :: AtomicBinOp -> Lambda KernelsMem-                    -> AtomicUpdate KernelsMem KernelEnv--atomicUpdateLocking atomicBinOp lam-  | Just ops_and_ts <- splitOp lam,-    all (\(_, t, _, _) -> primBitSize t == 32) ops_and_ts =-    primOrCas ops_and_ts $ \space arrs bucket ->-  -- If the operator is a vectorised binary operator on 32-bit values,-  -- we can use a particularly efficient implementation. If the-  -- operator has an atomic implementation we use that, otherwise it-  -- is still a binary operator which can be implemented by atomic-  -- compare-and-swap if 32 bits.-  forM_ (zip arrs ops_and_ts) $ \(a, (op, t, x, y)) -> do--  -- Common variables.-  old <- dPrim "old" t--  (arr', _a_space, bucket_offset) <- fullyIndexArray a bucket--  case opHasAtomicSupport space old arr' bucket_offset op of-    Just f -> sOp $ f $ Imp.var y t-    Nothing -> atomicUpdateCAS space t a old bucket x $-      x <-- Imp.BinOpExp op (Imp.var x t) (Imp.var y t)--  where opHasAtomicSupport space old arr' bucket' bop = do-          let atomic f = Imp.Atomic space . f old arr' bucket'-          atomic <$> atomicBinOp bop--        primOrCas ops-          | all isPrim ops = AtomicPrim-          | otherwise      = AtomicCAS--        isPrim (op, _, _, _) = isJust $ atomicBinOp op---- If the operator functions purely on single 32-bit values, we can--- use an implementation based on CAS, no matter what the operator--- does.-atomicUpdateLocking _ op-  | [Prim t] <- lambdaReturnType op,-    [xp, _] <- lambdaParams op,-    primBitSize t == 32 = AtomicCAS $ \space [arr] bucket -> do-      old <- dPrim "old" t-      atomicUpdateCAS space t arr old bucket (paramName xp) $-        compileBody' [xp] $ lambdaBody op--atomicUpdateLocking _ op = AtomicLocking $ \locking space arrs bucket -> do-  old <- dPrim "old" int32-  continue <- newVName "continue"-  dPrimVol_ continue Bool-  continue <-- true--  -- Correctly index into locks.-  (locks', _locks_space, locks_offset) <--    fullyIndexArray (lockingArray locking) $ lockingMapping locking bucket--  -- Critical section-  let try_acquire_lock =-        sOp $ Imp.Atomic space $-        Imp.AtomicCmpXchg int32 old locks' locks_offset-        (lockingIsUnlocked locking) (lockingToLock locking)-      lock_acquired = Imp.var old int32 .==. lockingIsUnlocked locking-      -- Even the releasing is done with an atomic rather than a-      -- simple write, for memory coherency reasons.-      release_lock =-        sOp $ Imp.Atomic space $-        Imp.AtomicCmpXchg int32 old locks' locks_offset-        (lockingToLock locking) (lockingToUnlock locking)-      break_loop = continue <-- false--  -- Preparing parameters. It is assumed that the caller has already-  -- filled the arr_params. We copy the current value to the-  -- accumulator parameters.-  ---  -- Note the use of 'everythingVolatile' when reading and writing the-  -- buckets.  This was necessary to ensure correct execution on a-  -- newer NVIDIA GPU (RTX 2080).  The 'volatile' modifiers likely-  -- make the writes pass through the (SM-local) L1 cache, which is-  -- necessary here, because we are really doing device-wide-  -- synchronisation without atomics (naughty!).-  let (acc_params, _arr_params) = splitAt (length arrs) $ lambdaParams op-      bind_acc_params =-        everythingVolatile $-        sComment "bind lhs" $-        forM_ (zip acc_params arrs) $ \(acc_p, arr) ->-        copyDWIMFix (paramName acc_p) [] (Var arr) bucket--  let op_body = sComment "execute operation" $-                compileBody' acc_params $ lambdaBody op--      do_hist =-        everythingVolatile $-        sComment "update global result" $-        zipWithM_ (writeArray bucket) arrs $ map (Var . paramName) acc_params--      fence = case space of Space "local" -> sOp $ Imp.MemFence Imp.FenceLocal-                            _             -> sOp $ Imp.MemFence Imp.FenceGlobal---  -- While-loop: Try to insert your value-  sWhile (Imp.var continue Bool) $ do-    try_acquire_lock-    sWhen lock_acquired $ do-      dLParams acc_params-      bind_acc_params-      op_body-      do_hist-      fence-      release_lock-      break_loop-    fence-  where writeArray bucket arr val = copyDWIMFix arr bucket val []--atomicUpdateCAS :: Space -> PrimType-                -> VName -> VName-                -> [Imp.Exp] -> VName-                -> InKernelGen ()-                -> InKernelGen ()-atomicUpdateCAS space t arr old bucket x do_op = do-  -- Code generation target:-  ---  -- old = d_his[idx];-  -- do {-  --   assumed = old;-  --   x = do_op(assumed, y);-  --   old = atomicCAS(&d_his[idx], assumed, tmp);-  -- } while(assumed != old);-  assumed <- dPrim "assumed" t-  run_loop <- dPrimV "run_loop" 1--  -- XXX: CUDA may generate really bad code if this is not a volatile-  -- read.  Unclear why.  The later reads are volatile, so maybe-  -- that's it.-  everythingVolatile $ copyDWIMFix old [] (Var arr) bucket--  (arr', _a_space, bucket_offset) <- fullyIndexArray arr bucket--  -- While-loop: Try to insert your value-  let (toBits, fromBits) =-        case t of FloatType Float32 -> (\v -> Imp.FunExp "to_bits32" [v] int32,-                                        \v -> Imp.FunExp "from_bits32" [v] t)-                  _                 -> (id, id)-  sWhile (Imp.var run_loop int32) $ do-    assumed <-- Imp.var old t-    x <-- Imp.var assumed t-    do_op-    old_bits <- dPrim "old_bits" int32-    sOp $ Imp.Atomic space $-      Imp.AtomicCmpXchg int32 old_bits arr' bucket_offset-      (toBits (Imp.var assumed t)) (toBits (Imp.var x t))-    old <-- fromBits (Imp.var old_bits int32)-    sWhen (toBits (Imp.var assumed t) .==. Imp.var old_bits int32)-      (run_loop <-- 0)---- | Horizontally fission a lambda that models a binary operator.-splitOp :: ASTLore lore => Lambda lore -> Maybe [(BinOp, PrimType, VName, VName)]-splitOp lam = mapM splitStm $ bodyResult $ lambdaBody lam-  where n = length $ lambdaReturnType lam-        splitStm (Var res) = do-          Let (Pattern [] [pe]) _ (BasicOp (BinOp op (Var x) (Var y))) <--            find (([res]==) . patternNames . stmPattern) $-            stmsToList $ bodyStms $ lambdaBody lam-          i <- Var res `elemIndex` bodyResult (lambdaBody lam)-          xp <- maybeNth i $ lambdaParams lam-          yp <- maybeNth (n+i) $ lambdaParams lam-          guard $ paramName xp == x-          guard $ paramName yp == y-          Prim t <- Just $ patElemType pe-          return (op, t, paramName xp, paramName yp)-        splitStm _ = Nothing--computeKernelUses :: FreeIn a =>-                     a -> [VName]-                  -> CallKernelGen [Imp.KernelUse]-computeKernelUses kernel_body bound_in_kernel = do-  let actually_free = freeIn kernel_body `namesSubtract` namesFromList bound_in_kernel-  -- Compute the variables that we need to pass to the kernel.-  nub <$> readsFromSet actually_free--readsFromSet :: Names -> CallKernelGen [Imp.KernelUse]-readsFromSet free =-  fmap catMaybes $-  forM (namesToList free) $ \var -> do-    t <- lookupType var-    vtable <- getVTable-    case t of-      Array {} -> return Nothing-      Mem (Space "local") -> return Nothing-      Mem {} -> return $ Just $ Imp.MemoryUse var-      Prim bt ->-        isConstExp vtable (Imp.var var bt) >>= \case-          Just ce -> return $ Just $ Imp.ConstUse var ce-          Nothing | bt == Cert -> return Nothing-                  | otherwise  -> return $ Just $ Imp.ScalarUse var bt--isConstExp :: VTable KernelsMem -> Imp.Exp-           -> ImpM lore r op (Maybe Imp.KernelConstExp)-isConstExp vtable size = do-  fname <- askFunction-  let onLeaf (Imp.ScalarVar name) _ = lookupConstExp name-      onLeaf (Imp.SizeOf pt) _ = Just $ primByteSize pt-      onLeaf Imp.Index{} _ = Nothing-      lookupConstExp name =-        constExp =<< hasExp =<< M.lookup name vtable-      constExp (Op (Inner (SizeOp (GetSize key _)))) =-        Just $ LeafExp (Imp.SizeConst $ keyWithEntryPoint fname key) int32-      constExp e = primExpFromExp lookupConstExp e-  return $ replaceInPrimExpM onLeaf size-  where hasExp (ArrayVar e _) = e-        hasExp (ScalarVar e _) = e-        hasExp (MemVar e _) = e--computeThreadChunkSize :: SplitOrdering-                       -> Imp.Exp-                       -> Imp.Count Imp.Elements Imp.Exp-                       -> Imp.Count Imp.Elements Imp.Exp-                       -> VName-                       -> ImpM lore r op ()-computeThreadChunkSize (SplitStrided stride) thread_index elements_per_thread num_elements chunk_var = do-  stride' <- toExp stride-  chunk_var <---    Imp.BinOpExp (SMin Int32)-    (Imp.unCount elements_per_thread)-    ((Imp.unCount num_elements - thread_index) `divUp` stride')--computeThreadChunkSize SplitContiguous thread_index elements_per_thread num_elements chunk_var = do-  starting_point <- dPrimV "starting_point" $-    thread_index * Imp.unCount elements_per_thread-  remaining_elements <- dPrimV "remaining_elements" $-    Imp.unCount num_elements - Imp.var starting_point int32--  let no_remaining_elements = Imp.var remaining_elements int32 .<=. 0-      beyond_bounds = Imp.unCount num_elements .<=. Imp.var starting_point int32--  sIf (no_remaining_elements .||. beyond_bounds)-    (chunk_var <-- 0)-    (sIf is_last_thread-       (chunk_var <-- Imp.unCount last_thread_elements)-       (chunk_var <-- Imp.unCount elements_per_thread))-  where last_thread_elements =-          num_elements - Imp.elements thread_index * elements_per_thread-        is_last_thread =-          Imp.unCount num_elements .<.-          (thread_index + 1) * Imp.unCount elements_per_thread--kernelInitialisationSimple :: Count NumGroups Imp.Exp -> Count GroupSize Imp.Exp-                           -> CallKernelGen (KernelConstants, InKernelGen ())-kernelInitialisationSimple (Count num_groups) (Count group_size) = do-  global_tid <- newVName "global_tid"-  local_tid <- newVName "local_tid"-  group_id <- newVName "group_tid"-  wave_size <- newVName "wave_size"-  inner_group_size <- newVName "group_size"-  let constants =-        KernelConstants-        (Imp.var global_tid int32)-        (Imp.var local_tid int32)-        (Imp.var group_id int32)-        global_tid local_tid group_id-        num_groups group_size (group_size*num_groups)-        (Imp.var wave_size int32)-        true-        mempty--  let set_constants = do-        dPrim_ global_tid int32-        dPrim_ local_tid int32-        dPrim_ inner_group_size int32-        dPrim_ wave_size int32-        dPrim_ group_id int32--        sOp (Imp.GetGlobalId global_tid 0)-        sOp (Imp.GetLocalId local_tid 0)-        sOp (Imp.GetLocalSize inner_group_size 0)-        sOp (Imp.GetLockstepWidth wave_size)-        sOp (Imp.GetGroupId group_id 0)--  return (constants, set_constants)--isActive :: [(VName, SubExp)] -> Imp.Exp-isActive limit = case actives of-                    [] -> Imp.ValueExp $ BoolValue True-                    x:xs -> foldl (.&&.) x xs-  where (is, ws) = unzip limit-        actives = zipWith active is $ map (toExp' Bool) ws-        active i = (Imp.var i int32 .<.)---- | Change every memory block to be in the global address space,--- except those who are in the local memory space.  This only affects--- generated code - we still need to make sure that the memory is--- actually present on the device (and dared as variables in the--- kernel).-makeAllMemoryGlobal :: CallKernelGen a -> CallKernelGen a-makeAllMemoryGlobal =-  localDefaultSpace (Imp.Space "global") . localVTable (M.map globalMemory)-  where globalMemory (MemVar _ entry)-          | entryMemSpace entry /= Space "local" =-              MemVar Nothing entry { entryMemSpace = Imp.Space "global" }-        globalMemory entry =-          entry--groupReduce :: Imp.Exp-            -> Lambda KernelsMem-            -> [VName]-            -> InKernelGen ()-groupReduce w lam arrs = do-  offset <- dPrim "offset" int32-  groupReduceWithOffset offset w lam arrs--groupReduceWithOffset :: VName-                      -> Imp.Exp-                      -> Lambda KernelsMem-                      -> [VName]-                      -> InKernelGen ()-groupReduceWithOffset offset w lam arrs = do-  constants <- kernelConstants <$> askEnv--  let local_tid = kernelLocalThreadId constants-      global_tid = kernelGlobalThreadId constants--      barrier-        | all primType $ lambdaReturnType lam = sOp $ Imp.Barrier Imp.FenceLocal-        | otherwise                           = sOp $ Imp.Barrier Imp.FenceGlobal--      readReduceArgument param arr-        | Prim _ <- paramType param = do-            let i = local_tid + Imp.vi32 offset-            copyDWIMFix (paramName param) [] (Var arr) [i]-        | otherwise = do-            let i = global_tid + Imp.vi32 offset-            copyDWIMFix (paramName param) [] (Var arr) [i]--      writeReduceOpResult param arr-        | Prim _ <- paramType param =-            copyDWIMFix arr [local_tid] (Var $ paramName param) []-        | otherwise =-            return ()--  let (reduce_acc_params, reduce_arr_params) = splitAt (length arrs) $ lambdaParams lam--  skip_waves <- dPrim "skip_waves" int32-  dLParams $ lambdaParams lam--  offset <-- 0--  comment "participating threads read initial accumulator" $-    sWhen (local_tid .<. w) $-    zipWithM_ readReduceArgument reduce_acc_params arrs--  let do_reduce = do comment "read array element" $-                       zipWithM_ readReduceArgument reduce_arr_params arrs-                     comment "apply reduction operation" $-                       compileBody' reduce_acc_params $ lambdaBody lam-                     comment "write result of operation" $-                       zipWithM_ writeReduceOpResult reduce_acc_params arrs-      in_wave_reduce = everythingVolatile do_reduce--      wave_size = kernelWaveSize constants-      group_size = kernelGroupSize constants-      wave_id = local_tid `quot` wave_size-      in_wave_id = local_tid - wave_id * wave_size-      num_waves = (group_size + wave_size - 1) `quot` wave_size-      arg_in_bounds = local_tid + Imp.var offset int32 .<. w--      doing_in_wave_reductions =-        Imp.var offset int32 .<. wave_size-      apply_in_in_wave_iteration =-        (in_wave_id .&. (2 * Imp.var offset int32 - 1)) .==. 0-      in_wave_reductions = do-        offset <-- 1-        sWhile doing_in_wave_reductions $ do-          sWhen (arg_in_bounds .&&. apply_in_in_wave_iteration)-            in_wave_reduce-          offset <-- Imp.var offset int32 * 2--      doing_cross_wave_reductions =-        Imp.var skip_waves int32 .<. num_waves-      is_first_thread_in_wave =-        in_wave_id .==. 0-      wave_not_skipped =-        (wave_id .&. (2 * Imp.var skip_waves int32 - 1)) .==. 0-      apply_in_cross_wave_iteration =-        arg_in_bounds .&&. is_first_thread_in_wave .&&. wave_not_skipped-      cross_wave_reductions = do-        skip_waves <-- 1-        sWhile doing_cross_wave_reductions $ do-          barrier-          offset <-- Imp.var skip_waves int32 * wave_size-          sWhen apply_in_cross_wave_iteration-            do_reduce-          skip_waves <-- Imp.var skip_waves int32 * 2--  in_wave_reductions-  cross_wave_reductions--groupScan :: Maybe (Imp.Exp -> Imp.Exp -> Imp.Exp)-          -> Imp.Exp-          -> Imp.Exp-          -> Lambda KernelsMem-          -> [VName]-          -> InKernelGen ()-groupScan seg_flag arrs_full_size w lam arrs = do-  constants <- kernelConstants <$> askEnv-  renamed_lam <- renameLambda lam--  let ltid = kernelLocalThreadId constants-      (x_params, y_params) = splitAt (length arrs) $ lambdaParams lam--  dLParams (lambdaParams lam++lambdaParams renamed_lam)--  -- The scan works by splitting the group into blocks, which are-  -- scanned separately.  Typically, these blocks are smaller than-  -- the lockstep width, which enables barrier-free execution inside-  -- them.-  ---  -- We hardcode the block size here.  The only requirement is that-  -- it should not be less than the square root of the group size.-  -- With 32, we will work on groups of size 1024 or smaller, which-  -- fits every device Troels has seen.  Still, it would be nicer if-  -- it were a runtime parameter.  Some day.-  let block_size = Imp.ValueExp $ IntValue $ Int32Value 32-      simd_width = kernelWaveSize constants-      block_id = ltid `quot` block_size-      in_block_id = ltid - block_id * block_size-      doInBlockScan seg_flag' active =-        inBlockScan constants seg_flag' arrs_full_size-        simd_width block_size active arrs barrier-      ltid_in_bounds = ltid .<. w-      array_scan = not $ all primType $ lambdaReturnType lam-      barrier | array_scan =-                  sOp $ Imp.Barrier Imp.FenceGlobal-              | otherwise =-                  sOp $ Imp.Barrier Imp.FenceLocal--      group_offset = kernelGroupId constants * kernelGroupSize constants--      writeBlockResult p arr-        | primType $ paramType p =-            copyDWIM arr [DimFix block_id] (Var $ paramName p) []-        | otherwise =-            copyDWIM arr [DimFix $ group_offset + block_id] (Var $ paramName p) []--      readPrevBlockResult p arr-        | primType $ paramType p =-            copyDWIM (paramName p) [] (Var arr) [DimFix $ block_id - 1]-        | otherwise =-            copyDWIM (paramName p) [] (Var arr) [DimFix $ group_offset + block_id - 1]--  doInBlockScan seg_flag ltid_in_bounds lam-  barrier--  let is_first_block = block_id .==. 0-  when array_scan $ do-    sComment "save correct values for first block" $-      sWhen is_first_block $ forM_ (zip x_params arrs) $ \(x, arr) ->-      unless (primType $ paramType x) $-      copyDWIM arr [DimFix $ arrs_full_size + group_offset + block_size + ltid] (Var $ paramName x) []--    barrier--  let last_in_block = in_block_id .==. block_size - 1-  sComment "last thread of block 'i' writes its result to offset 'i'" $-    sWhen (last_in_block .&&. ltid_in_bounds) $ everythingVolatile $-    zipWithM_ writeBlockResult x_params arrs--  barrier--  let first_block_seg_flag = do-        flag_true <- seg_flag-        Just $ \from to ->-          flag_true (from*block_size+block_size-1) (to*block_size+block_size-1)-  comment-    "scan the first block, after which offset 'i' contains carry-in for block 'i+1'" $-    doInBlockScan first_block_seg_flag (is_first_block .&&. ltid_in_bounds) renamed_lam--  barrier--  when array_scan $ do-    sComment "move correct values for first block back a block" $-      sWhen is_first_block $ forM_ (zip x_params arrs) $ \(x, arr) ->-      unless (primType $ paramType x) $-      copyDWIM-      arr [DimFix $ arrs_full_size + group_offset + ltid]-      (Var arr) [DimFix $ arrs_full_size + group_offset + block_size + ltid]--    barrier--  let read_carry_in = do-        forM_ (zip x_params y_params) $ \(x,y) ->-          copyDWIM (paramName y) [] (Var (paramName x)) []-        zipWithM_ readPrevBlockResult x_params arrs--      y_to_x = forM_ (zip x_params y_params) $ \(x,y) ->-        when (primType (paramType x)) $-        copyDWIM (paramName x) [] (Var (paramName y)) []--      op_to_x-        | Nothing <- seg_flag =-            compileBody' x_params $ lambdaBody lam-        | Just flag_true <- seg_flag = do-            inactive <--              dPrimVE "inactive" $ flag_true (block_id*block_size-1) ltid-            sWhen inactive y_to_x-            when array_scan barrier-            sUnless inactive $ compileBody' x_params $ lambdaBody lam--      write_final_result =-        forM_ (zip x_params arrs) $ \(p, arr) ->-        when (primType $ paramType p) $-        copyDWIM arr [DimFix ltid] (Var $ paramName p) []--  sComment "carry-in for every block except the first" $-    sUnless (is_first_block .||. Imp.UnOpExp Not ltid_in_bounds) $ do-    sComment "read operands" read_carry_in-    sComment "perform operation" op_to_x-    sComment "write final result" write_final_result--  barrier--  sComment "restore correct values for first block" $-    sWhen is_first_block $ forM_ (zip3 x_params y_params arrs) $ \(x, y, arr) ->-      if primType (paramType y)-      then copyDWIM arr [DimFix ltid] (Var $ paramName y) []-      else copyDWIM (paramName x) [] (Var arr) [DimFix $ arrs_full_size + group_offset + ltid]--  barrier--inBlockScan :: KernelConstants-            -> Maybe (Imp.Exp -> Imp.Exp -> Imp.Exp)-            -> Imp.Exp-            -> Imp.Exp-            -> Imp.Exp-            -> Imp.Exp-            -> [VName]-            -> InKernelGen ()-            -> Lambda KernelsMem-            -> InKernelGen ()-inBlockScan constants seg_flag arrs_full_size lockstep_width block_size active arrs barrier scan_lam = everythingVolatile $ do-  skip_threads <- dPrim "skip_threads" int32-  let in_block_thread_active =-        Imp.var skip_threads int32 .<=. in_block_id-      actual_params = lambdaParams scan_lam-      (x_params, y_params) =-        splitAt (length actual_params `div` 2) actual_params-      y_to_x =-        forM_ (zip x_params y_params) $ \(x,y) ->-        when (primType (paramType x)) $-        copyDWIM (paramName x) [] (Var (paramName y)) []--  -- Set initial y values-  sComment "read input for in-block scan" $-    sWhen active $ do-    zipWithM_ readInitial y_params arrs-    -- Since the final result is expected to be in x_params, we may-    -- need to copy it there for the first thread in the block.-    sWhen (in_block_id .==. 0) y_to_x--  when array_scan barrier--  let op_to_x-        | Nothing <- seg_flag =-            compileBody' x_params $ lambdaBody scan_lam-        | Just flag_true <- seg_flag = do-            inactive <- dPrimVE "inactive" $-                        flag_true (ltid-Imp.var skip_threads int32) ltid-            sWhen inactive y_to_x-            when array_scan barrier-            sUnless inactive $ compileBody' x_params $ lambdaBody scan_lam--      maybeBarrier = sWhen (lockstep_width .<=. Imp.var skip_threads int32)-                     barrier--  sComment "in-block scan (hopefully no barriers needed)" $ do-    skip_threads <-- 1-    sWhile (Imp.var skip_threads int32 .<. block_size) $ do-      sWhen (in_block_thread_active .&&. active) $ do-        sComment "read operands" $-          zipWithM_ (readParam (Imp.vi32 skip_threads)) x_params arrs-        sComment "perform operation" op_to_x--      maybeBarrier--      sWhen (in_block_thread_active .&&. active) $-        sComment "write result" $-        sequence_ $ zipWith3 writeResult x_params y_params arrs--      maybeBarrier--      skip_threads <-- Imp.var skip_threads int32 * 2--  where block_id = ltid `quot` block_size-        in_block_id = ltid - block_id * block_size-        ltid = kernelLocalThreadId constants-        gtid = kernelGlobalThreadId constants-        array_scan = not $ all primType $ lambdaReturnType scan_lam--        readInitial p arr-          | primType $ paramType p =-              copyDWIM (paramName p) [] (Var arr) [DimFix ltid]-          | otherwise =-              copyDWIM (paramName p) [] (Var arr) [DimFix gtid]--        readParam behind p arr-          | primType $ paramType p =-              copyDWIM (paramName p) [] (Var arr) [DimFix $ ltid - behind]-          | otherwise =-              copyDWIM (paramName p) [] (Var arr) [DimFix $ gtid - behind + arrs_full_size]--        writeResult x y arr-          | primType $ paramType x = do-              copyDWIM arr [DimFix ltid] (Var $ paramName x) []-              copyDWIM (paramName y) [] (Var $ paramName x) []-          | otherwise =-              copyDWIM (paramName y) [] (Var $ paramName x) []--computeMapKernelGroups :: Imp.Exp -> CallKernelGen (Imp.Exp, Imp.Exp)-computeMapKernelGroups kernel_size = do-  group_size <- dPrim "group_size" int32-  fname <- askFunction-  let group_size_var = Imp.var group_size int32-      group_size_key = keyWithEntryPoint fname $ nameFromString $ pretty group_size-  sOp $ Imp.GetSize group_size group_size_key Imp.SizeGroup-  num_groups <- dPrimV "num_groups" $ kernel_size `divUp` group_size_var-  return (Imp.var num_groups int32, Imp.var group_size int32)--simpleKernelConstants :: Imp.Exp -> String-                      -> CallKernelGen (KernelConstants, InKernelGen ())-simpleKernelConstants kernel_size desc = do-  thread_gtid <- newVName $ desc ++ "_gtid"-  thread_ltid <- newVName $ desc ++ "_ltid"-  group_id <- newVName $ desc ++ "_gid"-  (num_groups, group_size) <- computeMapKernelGroups kernel_size-  let set_constants = do-        dPrim_ thread_gtid int32-        dPrim_ thread_ltid int32-        dPrim_ group_id int32-        sOp (Imp.GetGlobalId thread_gtid 0)-        sOp (Imp.GetLocalId thread_ltid 0)-        sOp (Imp.GetGroupId group_id 0)---  return (KernelConstants-          (Imp.var thread_gtid int32) (Imp.var thread_ltid int32) (Imp.var group_id int32)-          thread_gtid thread_ltid group_id-          num_groups group_size (group_size*num_groups) 0-          (Imp.var thread_gtid int32 .<. kernel_size)-          mempty,--          set_constants)---- | For many kernels, we may not have enough physical groups to cover--- the logical iteration space.  Some groups thus have to perform--- double duty; we put an outer loop to accomplish this.  The--- advantage over just launching a bazillion threads is that the cost--- of memory expansion should be proportional to the number of--- *physical* threads (hardware parallelism), not the amount of--- application parallelism.-virtualiseGroups :: SegVirt-                 -> Imp.Exp-                 -> (VName -> InKernelGen ())-                 -> InKernelGen ()-virtualiseGroups SegVirt required_groups m = do-  constants <- kernelConstants <$> askEnv-  phys_group_id <- dPrim "phys_group_id" int32-  sOp $ Imp.GetGroupId phys_group_id 0-  let iterations = (required_groups - Imp.vi32 phys_group_id) `divUp`-                   kernelNumGroups constants--  sFor "i" iterations $ \i -> do-    m =<< dPrimV "virt_group_id" (Imp.vi32 phys_group_id + i * kernelNumGroups constants)-    -- Make sure the virtual group is actually done before we let-    -- another virtual group have its way with it.-    sOp $ Imp.Barrier Imp.FenceGlobal-virtualiseGroups _ _ m = do-  gid <- kernelGroupIdVar . kernelConstants <$> askEnv-  m gid--sKernelThread :: String-              -> Count NumGroups Imp.Exp -> Count GroupSize Imp.Exp-              -> VName-              -> InKernelGen ()-              -> CallKernelGen ()-sKernelThread = sKernel threadOperations kernelGlobalThreadId--sKernelGroup :: String-             -> Count NumGroups Imp.Exp -> Count GroupSize Imp.Exp-             -> VName-             -> InKernelGen ()-             -> CallKernelGen ()-sKernelGroup = sKernel groupOperations kernelGroupId--sKernelFailureTolerant :: Bool-                       -> Operations KernelsMem KernelEnv Imp.KernelOp-                       -> KernelConstants-                       -> Name-                       -> InKernelGen ()-                       -> CallKernelGen ()-sKernelFailureTolerant tol ops constants name m = do-  HostEnv atomics <- askEnv-  body <- makeAllMemoryGlobal $ subImpM_ (KernelEnv atomics constants) ops m-  uses <- computeKernelUses body mempty-  emit $ Imp.Op $ Imp.CallKernel Imp.Kernel-    { Imp.kernelBody = body-    , Imp.kernelUses = uses-    , Imp.kernelNumGroups = [kernelNumGroups constants]-    , Imp.kernelGroupSize = [kernelGroupSize constants]-    , Imp.kernelName = name-    , Imp.kernelFailureTolerant = tol-    }--sKernel :: Operations KernelsMem KernelEnv Imp.KernelOp-        -> (KernelConstants -> Imp.Exp)-        -> String-        -> Count NumGroups Imp.Exp-        -> Count GroupSize Imp.Exp-        -> VName-        -> InKernelGen ()-        -> CallKernelGen ()-sKernel ops flatf name num_groups group_size v f = do-  (constants, set_constants) <- kernelInitialisationSimple num_groups group_size-  name' <- nameForFun $ name ++ "_" ++ show (baseTag v)-  sKernelFailureTolerant False ops constants name' $ do-    set_constants-    dPrimV_ v $ flatf constants-    f--copyInGroup :: CopyCompiler KernelsMem KernelEnv Imp.KernelOp-copyInGroup pt destloc destslice srcloc srcslice = do-  dest_space <- entryMemSpace <$> lookupMemory (memLocationName destloc)-  src_space <- entryMemSpace <$> lookupMemory (memLocationName srcloc)--  case (dest_space, src_space) of-    (ScalarSpace destds _, ScalarSpace srcds _) -> do-      let destslice' =-            replicate (length destslice - length destds) (DimFix 0) ++-            takeLast (length destds) destslice-          srcslice' =-            replicate (length srcslice - length srcds) (DimFix 0) ++-            takeLast (length srcds) srcslice-      copyElementWise pt destloc destslice' srcloc srcslice'--    _ -> do-      groupCoverSpace (sliceDims destslice) $ \is ->-        copyElementWise pt-        destloc (map DimFix $ fixSlice destslice is)-        srcloc (map DimFix $ fixSlice srcslice is)-      sOp $ Imp.Barrier Imp.FenceLocal--threadOperations, groupOperations :: Operations KernelsMem KernelEnv Imp.KernelOp-threadOperations =-  (defaultOperations compileThreadOp)-  { opsCopyCompiler = copyElementWise-  , opsExpCompiler = compileThreadExp-  , opsStmsCompiler = \_ -> defCompileStms mempty-  , opsAllocCompilers =-      M.fromList [ (Space "local", allocLocal) ]-  }-groupOperations =-  (defaultOperations compileGroupOp)-  { opsCopyCompiler = copyInGroup-  , opsExpCompiler = compileGroupExp-  , opsStmsCompiler = \_ -> defCompileStms mempty-  , opsAllocCompilers =-      M.fromList [ (Space "local", allocLocal) ]-  }---- | Perform a Replicate with a kernel.-sReplicateKernel :: VName -> SubExp -> CallKernelGen ()-sReplicateKernel arr se = do-  t <- subExpType se-  ds <- dropLast (arrayRank t) . arrayDims <$> lookupType arr--  dims <- mapM toExp $ ds ++ arrayDims t-  (constants, set_constants) <--    simpleKernelConstants (product dims) "replicate"--  fname <- askFunction-  let name = keyWithEntryPoint fname $ nameFromString $-             "replicate_" ++ show (baseTag $ kernelGlobalThreadIdVar constants)-      is' = unflattenIndex dims $ kernelGlobalThreadId constants--  sKernelFailureTolerant True threadOperations constants name $ do-    set_constants-    sWhen (kernelThreadActive constants) $-      copyDWIMFix arr is' se $ drop (length ds) is'--replicateName :: PrimType -> String-replicateName bt = "replicate_" ++ pretty bt--replicateForType :: PrimType -> CallKernelGen Name-replicateForType bt = do-  let fname = nameFromString $ "builtin#" <> replicateName bt--  exists <- hasFunction fname-  unless exists $ do-    mem <- newVName "mem"-    num_elems <- newVName "num_elems"-    val <- newVName "val"--    let params = [Imp.MemParam mem (Space "device"),-                  Imp.ScalarParam num_elems int32,-                  Imp.ScalarParam val bt]-        shape = Shape [Var num_elems]-    function fname [] params $ do-      arr <- sArray "arr" bt shape $ ArrayIn mem $ IxFun.iota $-             map (primExpFromSubExp int32) $ shapeDims shape-      sReplicateKernel arr $ Var val--  return fname--replicateIsFill :: VName -> SubExp -> CallKernelGen (Maybe (CallKernelGen ()))-replicateIsFill arr v = do-  ArrayEntry (MemLocation arr_mem arr_shape arr_ixfun) _ <- lookupArray arr-  v_t <- subExpType v-  case v_t of-    Prim v_t'-      | IxFun.isLinear arr_ixfun -> return $ Just $ do-          fname <- replicateForType v_t'-          emit $ Imp.Call [] fname-            [Imp.MemArg arr_mem,-             Imp.ExpArg $ product $ map (toExp' int32) arr_shape,-             Imp.ExpArg $ toExp' v_t' v]-    _ -> return Nothing---- | Perform a Replicate with a kernel.-sReplicate :: VName -> SubExp -> CallKernelGen ()-sReplicate arr se = do-  -- If the replicate is of a particularly common and simple form-  -- (morally a memset()/fill), then we use a common function.-  is_fill <- replicateIsFill arr se--  case is_fill of-    Just m -> m-    Nothing -> sReplicateKernel arr se---- | Perform an Iota with a kernel.-sIotaKernel :: VName -> Imp.Exp -> Imp.Exp -> Imp.Exp -> IntType-            -> CallKernelGen ()-sIotaKernel arr n x s et = do-  destloc <- entryArrayLocation <$> lookupArray arr-  (constants, set_constants) <- simpleKernelConstants n "iota"--  fname <- askFunction-  let name = keyWithEntryPoint fname $ nameFromString $-             "iota_" ++ pretty et ++ "_" ++-             show (baseTag $ kernelGlobalThreadIdVar constants)--  sKernelFailureTolerant True threadOperations constants name $ do-    set_constants-    let gtid = kernelGlobalThreadId constants-    sWhen (kernelThreadActive constants) $ do-      (destmem, destspace, destidx) <- fullyIndexArray' destloc [gtid]--      emit $-        Imp.Write destmem destidx (IntType et) destspace Imp.Nonvolatile $-        Imp.sExt et gtid * s + x--iotaName :: IntType -> String-iotaName bt = "iota_" ++ pretty bt--iotaForType :: IntType -> CallKernelGen Name-iotaForType bt = do-  let fname = nameFromString $ "builtin#" <> iotaName bt--  exists <- hasFunction fname-  unless exists $ do-    mem <- newVName "mem"-    n <- newVName "n"-    x <- newVName "x"-    s <- newVName "s"--    let params = [Imp.MemParam mem (Space "device"),-                  Imp.ScalarParam n int32,-                  Imp.ScalarParam x $ IntType bt,-                  Imp.ScalarParam s $ IntType bt]-        shape = Shape [Var n]-        n' = Imp.vi32 n-        x' = Imp.var x $ IntType bt-        s' = Imp.var s $ IntType bt--    function fname [] params $ do-      arr <- sArray "arr" (IntType bt) shape $ ArrayIn mem $ IxFun.iota $-             map (primExpFromSubExp int32) $ shapeDims shape-      sIotaKernel arr n' x' s' bt--  return fname---- | Perform an Iota with a kernel.-sIota :: VName -> Imp.Exp -> Imp.Exp -> Imp.Exp -> IntType-      -> CallKernelGen ()-sIota arr n x s et = do-  ArrayEntry (MemLocation arr_mem _ arr_ixfun) _ <- lookupArray arr-  if IxFun.isLinear arr_ixfun then do-    fname <- iotaForType et-    emit $ Imp.Call [] fname-      [Imp.MemArg arr_mem, Imp.ExpArg n, Imp.ExpArg x, Imp.ExpArg s]-    else sIotaKernel arr n x s et--sCopy :: CopyCompiler KernelsMem HostEnv Imp.HostOp-sCopy bt-  destloc@(MemLocation destmem _ _) destslice-  srcloc@(MemLocation srcmem _ _) srcslice-  = do-  -- Note that the shape of the destination and the source are-  -- necessarily the same.-  let shape = sliceDims srcslice-      kernel_size = product shape--  (constants, set_constants) <- simpleKernelConstants kernel_size "copy"--  fname <- askFunction-  let name = keyWithEntryPoint fname $ nameFromString $-             "copy_" ++ show (baseTag $ kernelGlobalThreadIdVar constants)--  sKernelFailureTolerant True threadOperations constants name $ do-    set_constants--    let gtid = kernelGlobalThreadId constants-        dest_is = unflattenIndex shape gtid-        src_is = dest_is--    (_, destspace, destidx) <--      fullyIndexArray' destloc $ fixSlice destslice dest_is-    (_, srcspace, srcidx) <--      fullyIndexArray' srcloc $ fixSlice srcslice src_is--    sWhen (gtid .<. kernel_size) $ emit $-      Imp.Write destmem destidx bt destspace Imp.Nonvolatile $-      Imp.index srcmem srcidx bt srcspace Imp.Nonvolatile--compileGroupResult :: SegSpace-                   -> PatElem KernelsMem -> KernelResult-                   -> InKernelGen ()--compileGroupResult _ pe (TileReturns [(w,per_group_elems)] what) = do-  n <- toExp . arraySize 0 =<< lookupType what--  constants <- kernelConstants <$> askEnv-  let ltid = kernelLocalThreadId constants-      offset = toExp' int32 per_group_elems * kernelGroupId constants--  -- Avoid loop for the common case where each thread is statically-  -- known to write at most one element.-  localOps threadOperations $-    if toExp' int32 per_group_elems == kernelGroupSize constants-    then sWhen (offset + ltid .<. toExp' int32 w) $-         copyDWIMFix (patElemName pe) [ltid + offset] (Var what) [ltid]-    else-    sFor "i" (n `divUp` kernelGroupSize constants) $ \i -> do-      j <- fmap Imp.vi32 $ dPrimV "j" $-           kernelGroupSize constants * i + ltid-      sWhen (j .<. n) $ copyDWIMFix (patElemName pe) [j + offset] (Var what) [j]--compileGroupResult space pe (TileReturns dims what) = do-  let gids = map fst $ unSegSpace space-      out_tile_sizes = map (toExp' int32 . snd) dims-      group_is = zipWith (*) (map Imp.vi32 gids) out_tile_sizes-  local_is <- localThreadIDs $ map snd dims-  is_for_thread <- mapM (dPrimV "thread_out_index") $ zipWith (+) group_is local_is--  localOps threadOperations $-    sWhen (isActive $ zip is_for_thread $ map fst dims) $-    copyDWIMFix (patElemName pe) (map Imp.vi32 is_for_thread) (Var what) local_is--compileGroupResult space pe (Returns _ what) = do-  constants <- kernelConstants <$> askEnv-  in_local_memory <- arrayInLocalMemory what-  let gids = map (Imp.vi32 . fst) $ unSegSpace space--  if not in_local_memory then-    localOps threadOperations $-    sWhen (kernelLocalThreadId constants .==. 0) $-    copyDWIMFix (patElemName pe) gids what []-    else-      -- If the result of the group is an array in local memory, we-      -- store it by collective copying among all the threads of the-      -- group.  TODO: also do this if the array is in global memory-      -- (but this is a bit more tricky, synchronisation-wise).-      copyDWIMFix (patElemName pe) gids what []--compileGroupResult _ _ WriteReturns{} =-  compilerLimitationS "compileGroupResult: WriteReturns not handled yet."--compileGroupResult _ _ ConcatReturns{} =-  compilerLimitationS "compileGroupResult: ConcatReturns not handled yet."--compileThreadResult :: SegSpace-                    -> PatElem KernelsMem -> KernelResult-                    -> InKernelGen ()--compileThreadResult space pe (Returns _ what) = do-  let is = map (Imp.vi32 . fst) $ unSegSpace space-  copyDWIMFix (patElemName pe) is what []--compileThreadResult _ pe (ConcatReturns SplitContiguous _ per_thread_elems what) = do-  constants <- kernelConstants <$> askEnv-  let offset = toExp' int32 per_thread_elems * kernelGlobalThreadId constants-  n <- toExp' int32 . arraySize 0 <$> lookupType what-  copyDWIM (patElemName pe) [DimSlice offset n 1] (Var what) []--compileThreadResult _ pe (ConcatReturns (SplitStrided stride) _ _ what) = do-  offset <- kernelGlobalThreadId . kernelConstants <$> askEnv-  n <- toExp' int32 . arraySize 0 <$> lookupType what-  copyDWIM (patElemName pe) [DimSlice offset n $ toExp' int32 stride] (Var what) []--compileThreadResult _ pe (WriteReturns rws _arr dests) = do-  constants <- kernelConstants <$> askEnv-  rws' <- mapM toExp rws-  forM_ dests $ \(slice, e) -> do-    slice' <- mapM (traverse toExp) slice-    let condInBounds (DimFix i) rw =-          0 .<=. i .&&. i .<. rw-        condInBounds (DimSlice i n s) rw =-          0 .<=. i .&&. i+n*s .<. rw-        write = foldl (.&&.) (kernelThreadActive constants) $-                zipWith condInBounds slice' rws'-    sWhen write $ copyDWIM (patElemName pe) slice' e []--compileThreadResult _ _ TileReturns{} =-  compilerBugS "compileThreadResult: TileReturns unhandled."--arrayInLocalMemory :: SubExp -> InKernelGen Bool-arrayInLocalMemory (Var name) = do-  res <- lookupVar name-  case res of-    ArrayVar _ entry ->-      (Space "local"==) . entryMemSpace <$>-      lookupMemory (memLocationName (entryArrayLocation entry))-    _ -> return False-arrayInLocalMemory Constant{} = return False++module Futhark.CodeGen.ImpGen.Kernels.Base+  ( KernelConstants (..),+    keyWithEntryPoint,+    CallKernelGen,+    InKernelGen,+    HostEnv (..),+    KernelEnv (..),+    computeThreadChunkSize,+    groupReduce,+    groupScan,+    isActive,+    sKernelThread,+    sKernelGroup,+    sReplicate,+    sIota,+    sCopy,+    compileThreadResult,+    compileGroupResult,+    virtualiseGroups,+    groupLoop,+    kernelLoop,+    groupCoverSpace,+    precomputeSegOpIDs,+    atomicUpdateLocking,+    AtomicBinOp,+    Locking (..),+    AtomicUpdate (..),+    DoAtomicUpdate,+  )+where++import Control.Monad.Except+import Data.List (elemIndex, find, nub, zip4)+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Data.Set as S+import qualified Futhark.CodeGen.ImpCode.Kernels as Imp+import Futhark.CodeGen.ImpGen+import Futhark.Error+import Futhark.IR.KernelsMem+import qualified Futhark.IR.Mem.IxFun as IxFun+import Futhark.MonadFreshNames+import Futhark.Transform.Rename+import Futhark.Util (chunks, dropLast, mapAccumLM, maybeNth, takeLast)+import Futhark.Util.IntegralExp (divUp, quot, rem)+import Prelude hiding (quot, rem)++newtype HostEnv = HostEnv+  {hostAtomics :: AtomicBinOp}++data KernelEnv = KernelEnv+  { kernelAtomics :: AtomicBinOp,+    kernelConstants :: KernelConstants+  }++type CallKernelGen = ImpM KernelsMem HostEnv Imp.HostOp++type InKernelGen = ImpM KernelsMem KernelEnv Imp.KernelOp++data KernelConstants = KernelConstants+  { kernelGlobalThreadId :: Imp.TExp Int32,+    kernelLocalThreadId :: Imp.TExp Int32,+    kernelGroupId :: Imp.TExp Int32,+    kernelGlobalThreadIdVar :: VName,+    kernelLocalThreadIdVar :: VName,+    kernelGroupIdVar :: VName,+    kernelNumGroups :: Imp.TExp Int64,+    kernelGroupSize :: Imp.TExp Int64,+    kernelNumThreads :: Imp.TExp Int32,+    kernelWaveSize :: Imp.TExp Int32,+    kernelThreadActive :: Imp.TExp Bool,+    -- | A mapping from dimensions of nested SegOps to already+    -- computed local thread IDs.+    kernelLocalIdMap :: M.Map [SubExp] [Imp.TExp Int32]+  }++segOpSizes :: Stms KernelsMem -> S.Set [SubExp]+segOpSizes = onStms+  where+    onStms = foldMap (onExp . stmExp)+    onExp (Op (Inner (SegOp op))) =+      S.singleton $ map snd $ unSegSpace $ segSpace op+    onExp (If _ tbranch fbranch _) =+      onStms (bodyStms tbranch) <> onStms (bodyStms fbranch)+    onExp (DoLoop _ _ _ body) =+      onStms (bodyStms body)+    onExp _ = mempty++precomputeSegOpIDs :: Stms KernelsMem -> InKernelGen a -> InKernelGen a+precomputeSegOpIDs stms m = do+  ltid <- kernelLocalThreadId . kernelConstants <$> askEnv+  new_ids <- M.fromList <$> mapM (mkMap ltid) (S.toList (segOpSizes stms))+  let f env =+        env+          { kernelConstants =+              (kernelConstants env) {kernelLocalIdMap = new_ids}+          }+  localEnv f m+  where+    mkMap ltid dims = do+      let dims' = map (sExt32 . toInt64Exp) dims+      ids' <- mapM (dPrimVE "ltid_pre") $ unflattenIndex dims' ltid+      return (dims, ids')++keyWithEntryPoint :: Maybe Name -> Name -> Name+keyWithEntryPoint fname key =+  nameFromString $ maybe "" ((++ ".") . nameToString) fname ++ nameToString key++allocLocal :: AllocCompiler KernelsMem r Imp.KernelOp+allocLocal mem size =+  sOp $ Imp.LocalAlloc mem size++kernelAlloc ::+  Pattern KernelsMem ->+  SubExp ->+  Space ->+  InKernelGen ()+kernelAlloc (Pattern _ [_]) _ ScalarSpace {} =+  -- Handled by the declaration of the memory block, which is then+  -- translated to an actual scalar variable during C code generation.+  return ()+kernelAlloc (Pattern _ [mem]) size (Space "local") =+  allocLocal (patElemName mem) $ Imp.bytes $ toInt64Exp size+kernelAlloc (Pattern _ [mem]) _ _ =+  compilerLimitationS $ "Cannot allocate memory block " ++ pretty mem ++ " in kernel."+kernelAlloc dest _ _ =+  error $ "Invalid target for in-kernel allocation: " ++ show dest++splitSpace ::+  (ToExp w, ToExp i, ToExp elems_per_thread) =>+  Pattern KernelsMem ->+  SplitOrdering ->+  w ->+  i ->+  elems_per_thread ->+  ImpM lore r op ()+splitSpace (Pattern [] [size]) o w i elems_per_thread = do+  num_elements <- Imp.elements . TPrimExp <$> toExp w+  let i' = toInt64Exp i+  elems_per_thread' <- Imp.elements . TPrimExp <$> toExp elems_per_thread+  computeThreadChunkSize o i' elems_per_thread' num_elements (mkTV (patElemName size) int64)+splitSpace pat _ _ _ _ =+  error $ "Invalid target for splitSpace: " ++ pretty pat++compileThreadExp :: ExpCompiler KernelsMem KernelEnv Imp.KernelOp+compileThreadExp (Pattern _ [dest]) (BasicOp (ArrayLit es _)) =+  forM_ (zip [0 ..] es) $ \(i, e) ->+    copyDWIMFix (patElemName dest) [fromIntegral (i :: Int64)] e []+compileThreadExp dest e =+  defCompileExp dest e++-- | Assign iterations of a for-loop to all threads in the kernel.+-- The passed-in function is invoked with the (symbolic) iteration.+-- 'threadOperations' will be in effect in the body.  For+-- multidimensional loops, use 'groupCoverSpace'.+kernelLoop ::+  IntExp t =>+  Imp.TExp t ->+  Imp.TExp t ->+  Imp.TExp t ->+  (Imp.TExp t -> InKernelGen ()) ->+  InKernelGen ()+kernelLoop tid num_threads n f =+  localOps threadOperations $+    if n == num_threads+      then f tid+      else do+        -- Compute how many elements this thread is responsible for.+        -- Formula: (n - tid) / num_threads (rounded up).+        let elems_for_this = (n - tid) `divUp` num_threads++        sFor "i" elems_for_this $ \i -> f $ i * num_threads + tid++-- | Assign iterations of a for-loop to threads in the workgroup.  The+-- passed-in function is invoked with the (symbolic) iteration.  For+-- multidimensional loops, use 'groupCoverSpace'.+groupLoop ::+  Imp.TExp Int64 ->+  (Imp.TExp Int64 -> InKernelGen ()) ->+  InKernelGen ()+groupLoop n f = do+  constants <- kernelConstants <$> askEnv+  kernelLoop+    (sExt64 $ kernelLocalThreadId constants)+    (kernelGroupSize constants)+    n+    f++-- | Iterate collectively though a multidimensional space, such that+-- all threads in the group participate.  The passed-in function is+-- invoked with a (symbolic) point in the index space.+groupCoverSpace ::+  [Imp.TExp Int64] ->+  ([Imp.TExp Int64] -> InKernelGen ()) ->+  InKernelGen ()+groupCoverSpace ds f =+  groupLoop (product ds) $ f . unflattenIndex ds++compileGroupExp :: ExpCompiler KernelsMem KernelEnv Imp.KernelOp+-- The static arrays stuff does not work inside kernels.+compileGroupExp (Pattern _ [dest]) (BasicOp (ArrayLit es _)) =+  forM_ (zip [0 ..] es) $ \(i, e) ->+    copyDWIMFix (patElemName dest) [fromIntegral (i :: Int64)] e []+compileGroupExp (Pattern _ [dest]) (BasicOp (Replicate ds se)) = do+  let ds' = map toInt64Exp $ shapeDims ds+  groupCoverSpace ds' $ \is ->+    copyDWIMFix (patElemName dest) is se (drop (shapeRank ds) is)+  sOp $ Imp.Barrier Imp.FenceLocal+compileGroupExp (Pattern _ [dest]) (BasicOp (Iota n e s it)) = do+  n' <- toExp n+  e' <- toExp e+  s' <- toExp s+  groupLoop (TPrimExp n') $ \i' -> do+    x <-+      dPrimV "x" $+        TPrimExp $+          BinOpExp (Add it OverflowUndef) e' $+            BinOpExp (Mul it OverflowUndef) (untyped i') s'+    copyDWIMFix (patElemName dest) [i'] (Var (tvVar x)) []+  sOp $ Imp.Barrier Imp.FenceLocal++-- When generating code for a scalar in-place update, we must make+-- sure that only one thread performs the write.  When writing an+-- array, the group-level copy code will take care of doing the right+-- thing.+compileGroupExp (Pattern _ [pe]) (BasicOp (Update _ slice se))+  | null $ sliceDims slice = do+    sOp $ Imp.Barrier Imp.FenceLocal+    ltid <- kernelLocalThreadId . kernelConstants <$> askEnv+    sWhen (ltid .==. 0) $+      copyDWIM (patElemName pe) (map (fmap toInt64Exp) slice) se []+    sOp $ Imp.Barrier Imp.FenceLocal+compileGroupExp dest e =+  defCompileExp dest e++sanityCheckLevel :: SegLevel -> InKernelGen ()+sanityCheckLevel SegThread {} = return ()+sanityCheckLevel SegGroup {} =+  error "compileGroupOp: unexpected group-level SegOp."++localThreadIDs :: [SubExp] -> InKernelGen [Imp.TExp Int64]+localThreadIDs dims = do+  ltid <- sExt64 . kernelLocalThreadId . kernelConstants <$> askEnv+  let dims' = map toInt64Exp dims+  maybe (unflattenIndex dims' ltid) (map sExt64)+    . M.lookup dims+    . kernelLocalIdMap+    . kernelConstants+    <$> askEnv++compileGroupSpace :: SegLevel -> SegSpace -> InKernelGen ()+compileGroupSpace lvl space = do+  sanityCheckLevel lvl+  let (ltids, dims) = unzip $ unSegSpace space+  zipWithM_ dPrimV_ ltids =<< localThreadIDs dims+  ltid <- kernelLocalThreadId . kernelConstants <$> askEnv+  dPrimV_ (segFlat space) ltid++-- Construct the necessary lock arrays for an intra-group histogram.+prepareIntraGroupSegHist ::+  Count GroupSize SubExp ->+  [HistOp KernelsMem] ->+  InKernelGen [[Imp.TExp Int64] -> InKernelGen ()]+prepareIntraGroupSegHist group_size =+  fmap snd . mapAccumLM onOp Nothing+  where+    onOp l op = do+      constants <- kernelConstants <$> askEnv+      atomicBinOp <- kernelAtomics <$> askEnv++      let local_subhistos = histDest op++      case (l, atomicUpdateLocking atomicBinOp $ histOp op) of+        (_, AtomicPrim f) -> return (l, f (Space "local") local_subhistos)+        (_, AtomicCAS f) -> return (l, f (Space "local") local_subhistos)+        (Just l', AtomicLocking f) -> return (l, f l' (Space "local") local_subhistos)+        (Nothing, AtomicLocking f) -> do+          locks <- newVName "locks"++          let num_locks = toInt64Exp $ unCount group_size+              dims = map toInt64Exp $ shapeDims (histShape op) ++ [histWidth op]+              l' = Locking locks 0 1 0 (pure . (`rem` num_locks) . flattenIndex dims)+              locks_t = Array int32 (Shape [unCount group_size]) NoUniqueness++          locks_mem <- sAlloc "locks_mem" (typeSize locks_t) $ Space "local"+          dArray locks int32 (arrayShape locks_t) $+            ArrayIn locks_mem $+              IxFun.iota $+                map pe64 $ arrayDims locks_t++          sComment "All locks start out unlocked" $+            groupCoverSpace [kernelGroupSize constants] $ \is ->+              copyDWIMFix locks is (intConst Int32 0) []++          return (Just l', f l' (Space "local") local_subhistos)++whenActive :: SegLevel -> SegSpace -> InKernelGen () -> InKernelGen ()+whenActive lvl space m+  | SegNoVirtFull <- segVirt lvl = m+  | otherwise = sWhen (isActive $ unSegSpace space) m++compileGroupOp :: OpCompiler KernelsMem KernelEnv Imp.KernelOp+compileGroupOp pat (Alloc size space) =+  kernelAlloc pat size space+compileGroupOp pat (Inner (SizeOp (SplitSpace o w i elems_per_thread))) =+  splitSpace pat o w i elems_per_thread+compileGroupOp pat (Inner (SegOp (SegMap lvl space _ body))) = do+  void $ compileGroupSpace lvl space++  whenActive lvl space $+    localOps threadOperations $+      compileStms mempty (kernelBodyStms body) $+        zipWithM_ (compileThreadResult space) (patternElements pat) $+          kernelBodyResult body++  sOp $ Imp.ErrorSync Imp.FenceLocal+compileGroupOp pat (Inner (SegOp (SegScan lvl space scans _ body))) = do+  compileGroupSpace lvl space+  let (ltids, dims) = unzip $ unSegSpace space+      dims' = map toInt64Exp dims++  whenActive lvl space $+    compileStms mempty (kernelBodyStms body) $+      forM_ (zip (patternNames pat) $ kernelBodyResult body) $ \(dest, res) ->+        copyDWIMFix+          dest+          (map Imp.vi64 ltids)+          (kernelResultSubExp res)+          []++  sOp $ Imp.ErrorSync Imp.FenceLocal++  let segment_size = last dims'+      crossesSegment from to =+        (sExt64 to - sExt64 from) .>. (sExt64 to `rem` segment_size)++  -- groupScan needs to treat the scan output as a one-dimensional+  -- array of scan elements, so we invent some new flattened arrays+  -- here.  XXX: this assumes that the original index function is just+  -- row-major, but does not actually verify it.+  dims_flat <- dPrimV "dims_flat" $ product dims'+  let flattened pe = do+        MemLocation mem _ _ <-+          entryArrayLocation <$> lookupArray (patElemName pe)+        let pe_t = typeOf pe+            arr_dims = Var (tvVar dims_flat) : drop (length dims') (arrayDims pe_t)+        sArray+          (baseString (patElemName pe) ++ "_flat")+          (elemType pe_t)+          (Shape arr_dims)+          $ ArrayIn mem $ IxFun.iota $ map pe64 arr_dims++      num_scan_results = sum $ map (length . segBinOpNeutral) scans++  arrs_flat <- mapM flattened $ take num_scan_results $ patternElements pat++  forM_ scans $ \scan -> do+    let scan_op = segBinOpLambda scan+    groupScan (Just crossesSegment) (product dims') (product dims') scan_op arrs_flat+compileGroupOp pat (Inner (SegOp (SegRed lvl space ops _ body))) = do+  compileGroupSpace lvl space++  let (ltids, dims) = unzip $ unSegSpace space+      (red_pes, map_pes) =+        splitAt (segBinOpResults ops) $ patternElements pat++      dims' = map toInt64Exp dims++      mkTempArr t =+        sAllocArray "red_arr" (elemType t) (Shape dims <> arrayShape t) $ Space "local"++  tmp_arrs <- mapM mkTempArr $ concatMap (lambdaReturnType . segBinOpLambda) ops+  let tmps_for_ops = chunks (map (length . segBinOpNeutral) ops) tmp_arrs++  whenActive lvl space $+    compileStms mempty (kernelBodyStms body) $ do+      let (red_res, map_res) =+            splitAt (segBinOpResults ops) $ kernelBodyResult body+      forM_ (zip tmp_arrs red_res) $ \(dest, res) ->+        copyDWIMFix dest (map Imp.vi64 ltids) (kernelResultSubExp res) []+      zipWithM_ (compileThreadResult space) map_pes map_res++  sOp $ Imp.ErrorSync Imp.FenceLocal++  case dims' of+    -- Nonsegmented case (or rather, a single segment) - this we can+    -- handle directly with a group-level reduction.+    [dim'] -> do+      forM_ (zip ops tmps_for_ops) $ \(op, tmps) ->+        groupReduce (sExt32 dim') (segBinOpLambda op) tmps++      sOp $ Imp.ErrorSync Imp.FenceLocal++      forM_ (zip red_pes tmp_arrs) $ \(pe, arr) ->+        copyDWIMFix (patElemName pe) [] (Var arr) [0]+    _ -> do+      -- Segmented intra-group reductions are turned into (regular)+      -- segmented scans.  It is possible that this can be done+      -- better, but at least this approach is simple.++      -- groupScan operates on flattened arrays.  This does not+      -- involve copying anything; merely playing with the index+      -- function.+      dims_flat <- dPrimV "dims_flat" $ product dims'+      let flatten arr = do+            ArrayEntry arr_loc pt <- lookupArray arr+            let flat_shape =+                  Shape $+                    Var (tvVar dims_flat) :+                    drop (length ltids) (memLocationShape arr_loc)+            sArray "red_arr_flat" pt flat_shape $+              ArrayIn (memLocationName arr_loc) $+                IxFun.iota $ map pe64 $ shapeDims flat_shape++      let segment_size = last dims'+          crossesSegment from to =+            (sExt64 to - sExt64 from) .>. (sExt64 to `rem` sExt64 segment_size)++      forM_ (zip ops tmps_for_ops) $ \(op, tmps) -> do+        tmps_flat <- mapM flatten tmps+        groupScan+          (Just crossesSegment)+          (product dims')+          (product dims')+          (segBinOpLambda op)+          tmps_flat++      sOp $ Imp.ErrorSync Imp.FenceLocal++      forM_ (zip red_pes tmp_arrs) $ \(pe, arr) ->+        copyDWIM+          (patElemName pe)+          []+          (Var arr)+          (map (unitSlice 0) (init dims') ++ [DimFix $ last dims' -1])++      sOp $ Imp.Barrier Imp.FenceLocal+compileGroupOp pat (Inner (SegOp (SegHist lvl space ops _ kbody))) = do+  compileGroupSpace lvl space+  let ltids = map fst $ unSegSpace space++  -- We don't need the red_pes, because it is guaranteed by our type+  -- rules that they occupy the same memory as the destinations for+  -- the ops.+  let num_red_res = length ops + sum (map (length . histNeutral) ops)+      (_red_pes, map_pes) =+        splitAt num_red_res $ patternElements pat++  ops' <- prepareIntraGroupSegHist (segGroupSize lvl) ops++  -- Ensure that all locks have been initialised.+  sOp $ Imp.Barrier Imp.FenceLocal++  whenActive lvl space $+    compileStms mempty (kernelBodyStms kbody) $ do+      let (red_res, map_res) = splitAt num_red_res $ kernelBodyResult kbody+          (red_is, red_vs) = splitAt (length ops) $ map kernelResultSubExp red_res+      zipWithM_ (compileThreadResult space) map_pes map_res++      let vs_per_op = chunks (map (length . histDest) ops) red_vs++      forM_ (zip4 red_is vs_per_op ops' ops) $+        \(bin, op_vs, do_op, HistOp dest_w _ _ _ shape lam) -> do+          let bin' = toInt64Exp bin+              dest_w' = toInt64Exp dest_w+              bin_in_bounds = 0 .<=. bin' .&&. bin' .<. dest_w'+              bin_is = map Imp.vi64 (init ltids) ++ [bin']+              vs_params = takeLast (length op_vs) $ lambdaParams lam++          sComment "perform atomic updates" $+            sWhen bin_in_bounds $ do+              dLParams $ lambdaParams lam+              sLoopNest shape $ \is -> do+                forM_ (zip vs_params op_vs) $ \(p, v) ->+                  copyDWIMFix (paramName p) [] v is+                do_op (bin_is ++ is)++  sOp $ Imp.ErrorSync Imp.FenceLocal+compileGroupOp pat _ =+  compilerBugS $ "compileGroupOp: cannot compile rhs of binding " ++ pretty pat++compileThreadOp :: OpCompiler KernelsMem KernelEnv Imp.KernelOp+compileThreadOp pat (Alloc size space) =+  kernelAlloc pat size space+compileThreadOp pat (Inner (SizeOp (SplitSpace o w i elems_per_thread))) =+  splitSpace pat o w i elems_per_thread+compileThreadOp pat _ =+  compilerBugS $ "compileThreadOp: cannot compile rhs of binding " ++ pretty pat++-- | Locking strategy used for an atomic update.+data Locking = Locking+  { -- | Array containing the lock.+    lockingArray :: VName,+    -- | Value for us to consider the lock free.+    lockingIsUnlocked :: Imp.TExp Int32,+    -- | What to write when we lock it.+    lockingToLock :: Imp.TExp Int32,+    -- | What to write when we unlock it.+    lockingToUnlock :: Imp.TExp Int32,+    -- | A transformation from the logical lock index to the+    -- physical position in the array.  This can also be used+    -- to make the lock array smaller.+    lockingMapping :: [Imp.TExp Int64] -> [Imp.TExp Int64]+  }++-- | A function for generating code for an atomic update.  Assumes+-- that the bucket is in-bounds.+type DoAtomicUpdate lore r =+  Space -> [VName] -> [Imp.TExp Int64] -> ImpM lore r Imp.KernelOp ()++-- | The mechanism that will be used for performing the atomic update.+-- Approximates how efficient it will be.  Ordered from most to least+-- efficient.+data AtomicUpdate lore r+  = -- | Supported directly by primitive.+    AtomicPrim (DoAtomicUpdate lore r)+  | -- | Can be done by efficient swaps.+    AtomicCAS (DoAtomicUpdate lore r)+  | -- | Requires explicit locking.+    AtomicLocking (Locking -> DoAtomicUpdate lore r)++-- | Is there an atomic t'BinOp' corresponding to this t'BinOp'?+type AtomicBinOp =+  BinOp ->+  Maybe (VName -> VName -> Count Imp.Elements (Imp.TExp Int64) -> Imp.Exp -> Imp.AtomicOp)++-- | Do an atomic update corresponding to a binary operator lambda.+atomicUpdateLocking ::+  AtomicBinOp ->+  Lambda KernelsMem ->+  AtomicUpdate KernelsMem KernelEnv+atomicUpdateLocking atomicBinOp lam+  | Just ops_and_ts <- splitOp lam,+    all (\(_, t, _, _) -> primBitSize t == 32) ops_and_ts =+    primOrCas ops_and_ts $ \space arrs bucket ->+      -- If the operator is a vectorised binary operator on 32-bit values,+      -- we can use a particularly efficient implementation. If the+      -- operator has an atomic implementation we use that, otherwise it+      -- is still a binary operator which can be implemented by atomic+      -- compare-and-swap if 32 bits.+      forM_ (zip arrs ops_and_ts) $ \(a, (op, t, x, y)) -> do+        -- Common variables.+        old <- dPrim "old" t++        (arr', _a_space, bucket_offset) <- fullyIndexArray a bucket++        case opHasAtomicSupport space (tvVar old) arr' bucket_offset op of+          Just f -> sOp $ f $ Imp.var y t+          Nothing ->+            atomicUpdateCAS space t a (tvVar old) bucket x $+              x <~~ Imp.BinOpExp op (Imp.var x t) (Imp.var y t)+  where+    opHasAtomicSupport space old arr' bucket' bop = do+      let atomic f = Imp.Atomic space . f old arr' bucket'+      atomic <$> atomicBinOp bop++    primOrCas ops+      | all isPrim ops = AtomicPrim+      | otherwise = AtomicCAS++    isPrim (op, _, _, _) = isJust $ atomicBinOp op++-- If the operator functions purely on single 32-bit values, we can+-- use an implementation based on CAS, no matter what the operator+-- does.+atomicUpdateLocking _ op+  | [Prim t] <- lambdaReturnType op,+    [xp, _] <- lambdaParams op,+    primBitSize t == 32 = AtomicCAS $ \space [arr] bucket -> do+    old <- dPrim "old" t+    atomicUpdateCAS space t arr (tvVar old) bucket (paramName xp) $+      compileBody' [xp] $ lambdaBody op+atomicUpdateLocking _ op = AtomicLocking $ \locking space arrs bucket -> do+  old <- dPrim "old" int32+  continue <- dPrimVol "continue" Bool true++  -- Correctly index into locks.+  (locks', _locks_space, locks_offset) <-+    fullyIndexArray (lockingArray locking) $ lockingMapping locking bucket++  -- Critical section+  let try_acquire_lock =+        sOp $+          Imp.Atomic space $+            Imp.AtomicCmpXchg+              int32+              (tvVar old)+              locks'+              locks_offset+              (untyped $ lockingIsUnlocked locking)+              (untyped $ lockingToLock locking)+      lock_acquired = tvExp old .==. lockingIsUnlocked locking+      -- Even the releasing is done with an atomic rather than a+      -- simple write, for memory coherency reasons.+      release_lock =+        sOp $+          Imp.Atomic space $+            Imp.AtomicCmpXchg+              int32+              (tvVar old)+              locks'+              locks_offset+              (untyped $ lockingToLock locking)+              (untyped $ lockingToUnlock locking)+      break_loop = continue <-- false++  -- Preparing parameters. It is assumed that the caller has already+  -- filled the arr_params. We copy the current value to the+  -- accumulator parameters.+  --+  -- Note the use of 'everythingVolatile' when reading and writing the+  -- buckets.  This was necessary to ensure correct execution on a+  -- newer NVIDIA GPU (RTX 2080).  The 'volatile' modifiers likely+  -- make the writes pass through the (SM-local) L1 cache, which is+  -- necessary here, because we are really doing device-wide+  -- synchronisation without atomics (naughty!).+  let (acc_params, _arr_params) = splitAt (length arrs) $ lambdaParams op+      bind_acc_params =+        everythingVolatile $+          sComment "bind lhs" $+            forM_ (zip acc_params arrs) $ \(acc_p, arr) ->+              copyDWIMFix (paramName acc_p) [] (Var arr) bucket++  let op_body =+        sComment "execute operation" $+          compileBody' acc_params $ lambdaBody op++      do_hist =+        everythingVolatile $+          sComment "update global result" $+            zipWithM_ (writeArray bucket) arrs $ map (Var . paramName) acc_params++      fence = case space of+        Space "local" -> sOp $ Imp.MemFence Imp.FenceLocal+        _ -> sOp $ Imp.MemFence Imp.FenceGlobal++  -- While-loop: Try to insert your value+  sWhile (tvExp continue) $ do+    try_acquire_lock+    sWhen lock_acquired $ do+      dLParams acc_params+      bind_acc_params+      op_body+      do_hist+      fence+      release_lock+      break_loop+    fence+  where+    writeArray bucket arr val = copyDWIMFix arr bucket val []++atomicUpdateCAS ::+  Space ->+  PrimType ->+  VName ->+  VName ->+  [Imp.TExp Int64] ->+  VName ->+  InKernelGen () ->+  InKernelGen ()+atomicUpdateCAS space t arr old bucket x do_op = do+  -- Code generation target:+  --+  -- old = d_his[idx];+  -- do {+  --   assumed = old;+  --   x = do_op(assumed, y);+  --   old = atomicCAS(&d_his[idx], assumed, tmp);+  -- } while(assumed != old);+  assumed <- tvVar <$> dPrim "assumed" t+  run_loop <- dPrimV "run_loop" true++  -- XXX: CUDA may generate really bad code if this is not a volatile+  -- read.  Unclear why.  The later reads are volatile, so maybe+  -- that's it.+  everythingVolatile $ copyDWIMFix old [] (Var arr) bucket++  (arr', _a_space, bucket_offset) <- fullyIndexArray arr bucket++  -- While-loop: Try to insert your value+  let (toBits, fromBits) =+        case t of+          FloatType Float32 ->+            ( \v -> Imp.FunExp "to_bits32" [v] int32,+              \v -> Imp.FunExp "from_bits32" [v] t+            )+          _ -> (id, id)+  sWhile (tvExp run_loop) $ do+    assumed <~~ Imp.var old t+    x <~~ Imp.var assumed t+    do_op+    old_bits <- dPrim "old_bits" int32+    sOp $+      Imp.Atomic space $+        Imp.AtomicCmpXchg+          int32+          (tvVar old_bits)+          arr'+          bucket_offset+          (toBits (Imp.var assumed t))+          (toBits (Imp.var x t))+    old <~~ fromBits (untyped $ tvExp old_bits)+    sWhen+      (isInt32 (toBits (Imp.var assumed t)) .==. tvExp old_bits)+      (run_loop <-- false)++-- | Horizontally fission a lambda that models a binary operator.+splitOp :: ASTLore lore => Lambda lore -> Maybe [(BinOp, PrimType, VName, VName)]+splitOp lam = mapM splitStm $ bodyResult $ lambdaBody lam+  where+    n = length $ lambdaReturnType lam+    splitStm (Var res) = do+      Let (Pattern [] [pe]) _ (BasicOp (BinOp op (Var x) (Var y))) <-+        find (([res] ==) . patternNames . stmPattern) $+          stmsToList $ bodyStms $ lambdaBody lam+      i <- Var res `elemIndex` bodyResult (lambdaBody lam)+      xp <- maybeNth i $ lambdaParams lam+      yp <- maybeNth (n + i) $ lambdaParams lam+      guard $ paramName xp == x+      guard $ paramName yp == y+      Prim t <- Just $ patElemType pe+      return (op, t, paramName xp, paramName yp)+    splitStm _ = Nothing++computeKernelUses ::+  FreeIn a =>+  a ->+  [VName] ->+  CallKernelGen [Imp.KernelUse]+computeKernelUses kernel_body bound_in_kernel = do+  let actually_free = freeIn kernel_body `namesSubtract` namesFromList bound_in_kernel+  -- Compute the variables that we need to pass to the kernel.+  nub <$> readsFromSet actually_free++readsFromSet :: Names -> CallKernelGen [Imp.KernelUse]+readsFromSet free =+  fmap catMaybes $+    forM (namesToList free) $ \var -> do+      t <- lookupType var+      vtable <- getVTable+      case t of+        Array {} -> return Nothing+        Mem (Space "local") -> return Nothing+        Mem {} -> return $ Just $ Imp.MemoryUse var+        Prim bt ->+          isConstExp vtable (Imp.var var bt) >>= \case+            Just ce -> return $ Just $ Imp.ConstUse var ce+            Nothing+              | bt == Cert -> return Nothing+              | otherwise -> return $ Just $ Imp.ScalarUse var bt++isConstExp ::+  VTable KernelsMem ->+  Imp.Exp ->+  ImpM lore r op (Maybe Imp.KernelConstExp)+isConstExp vtable size = do+  fname <- askFunction+  let onLeaf (Imp.ScalarVar name) _ = lookupConstExp name+      onLeaf (Imp.SizeOf pt) _ = Just $ ValueExp $ IntValue $ Int32Value $ primByteSize pt+      onLeaf Imp.Index {} _ = Nothing+      lookupConstExp name =+        constExp =<< hasExp =<< M.lookup name vtable+      constExp (Op (Inner (SizeOp (GetSize key _)))) =+        Just $ LeafExp (Imp.SizeConst $ keyWithEntryPoint fname key) int32+      constExp e = primExpFromExp lookupConstExp e+  return $ replaceInPrimExpM onLeaf size+  where+    hasExp (ArrayVar e _) = e+    hasExp (ScalarVar e _) = e+    hasExp (MemVar e _) = e++computeThreadChunkSize ::+  SplitOrdering ->+  Imp.TExp Int64 ->+  Imp.Count Imp.Elements (Imp.TExp Int64) ->+  Imp.Count Imp.Elements (Imp.TExp Int64) ->+  TV Int64 ->+  ImpM lore r op ()+computeThreadChunkSize (SplitStrided stride) thread_index elements_per_thread num_elements chunk_var =+  chunk_var+    <-- sMin64+      (Imp.unCount elements_per_thread)+      ((Imp.unCount num_elements - thread_index) `divUp` toInt64Exp stride)+computeThreadChunkSize SplitContiguous thread_index elements_per_thread num_elements chunk_var = do+  starting_point <-+    dPrimV "starting_point" $+      thread_index * Imp.unCount elements_per_thread+  remaining_elements <-+    dPrimV "remaining_elements" $+      Imp.unCount num_elements - tvExp starting_point++  let no_remaining_elements = tvExp remaining_elements .<=. 0+      beyond_bounds = Imp.unCount num_elements .<=. tvExp starting_point++  sIf+    (no_remaining_elements .||. beyond_bounds)+    (chunk_var <-- 0)+    ( sIf+        is_last_thread+        (chunk_var <-- Imp.unCount last_thread_elements)+        (chunk_var <-- Imp.unCount elements_per_thread)+    )+  where+    last_thread_elements =+      num_elements - Imp.elements thread_index * elements_per_thread+    is_last_thread =+      Imp.unCount num_elements+        .<. (thread_index + 1) * Imp.unCount elements_per_thread++kernelInitialisationSimple ::+  Count NumGroups (Imp.TExp Int64) ->+  Count GroupSize (Imp.TExp Int64) ->+  CallKernelGen (KernelConstants, InKernelGen ())+kernelInitialisationSimple (Count num_groups) (Count group_size) = do+  global_tid <- newVName "global_tid"+  local_tid <- newVName "local_tid"+  group_id <- newVName "group_tid"+  wave_size <- newVName "wave_size"+  inner_group_size <- newVName "group_size"+  let constants =+        KernelConstants+          (Imp.vi32 global_tid)+          (Imp.vi32 local_tid)+          (Imp.vi32 group_id)+          global_tid+          local_tid+          group_id+          num_groups+          group_size+          (sExt32 (group_size * num_groups))+          (Imp.vi32 wave_size)+          true+          mempty++  let set_constants = do+        dPrim_ global_tid int32+        dPrim_ local_tid int32+        dPrim_ inner_group_size int64+        dPrim_ wave_size int32+        dPrim_ group_id int32++        sOp (Imp.GetGlobalId global_tid 0)+        sOp (Imp.GetLocalId local_tid 0)+        sOp (Imp.GetLocalSize inner_group_size 0)+        sOp (Imp.GetLockstepWidth wave_size)+        sOp (Imp.GetGroupId group_id 0)++  return (constants, set_constants)++isActive :: [(VName, SubExp)] -> Imp.TExp Bool+isActive limit = case actives of+  [] -> true+  x : xs -> foldl (.&&.) x xs+  where+    (is, ws) = unzip limit+    actives = zipWith active is $ map toInt64Exp ws+    active i = (Imp.vi64 i .<.)++-- | Change every memory block to be in the global address space,+-- except those who are in the local memory space.  This only affects+-- generated code - we still need to make sure that the memory is+-- actually present on the device (and dared as variables in the+-- kernel).+makeAllMemoryGlobal :: CallKernelGen a -> CallKernelGen a+makeAllMemoryGlobal =+  localDefaultSpace (Imp.Space "global") . localVTable (M.map globalMemory)+  where+    globalMemory (MemVar _ entry)+      | entryMemSpace entry /= Space "local" =+        MemVar Nothing entry {entryMemSpace = Imp.Space "global"}+    globalMemory entry =+      entry++groupReduce ::+  Imp.TExp Int32 ->+  Lambda KernelsMem ->+  [VName] ->+  InKernelGen ()+groupReduce w lam arrs = do+  offset <- dPrim "offset" int32+  groupReduceWithOffset offset w lam arrs++groupReduceWithOffset ::+  TV Int32 ->+  Imp.TExp Int32 ->+  Lambda KernelsMem ->+  [VName] ->+  InKernelGen ()+groupReduceWithOffset offset w lam arrs = do+  constants <- kernelConstants <$> askEnv++  let local_tid = kernelLocalThreadId constants+      global_tid = kernelGlobalThreadId constants++      barrier+        | all primType $ lambdaReturnType lam = sOp $ Imp.Barrier Imp.FenceLocal+        | otherwise = sOp $ Imp.Barrier Imp.FenceGlobal++      readReduceArgument param arr+        | Prim _ <- paramType param = do+          let i = local_tid + tvExp offset+          copyDWIMFix (paramName param) [] (Var arr) [sExt64 i]+        | otherwise = do+          let i = global_tid + tvExp offset+          copyDWIMFix (paramName param) [] (Var arr) [sExt64 i]++      writeReduceOpResult param arr+        | Prim _ <- paramType param =+          copyDWIMFix arr [sExt64 local_tid] (Var $ paramName param) []+        | otherwise =+          return ()++  let (reduce_acc_params, reduce_arr_params) = splitAt (length arrs) $ lambdaParams lam++  skip_waves <- dPrimV "skip_waves" (1 :: Imp.TExp Int32)+  dLParams $ lambdaParams lam++  offset <-- (0 :: Imp.TExp Int32)++  comment "participating threads read initial accumulator" $+    sWhen (local_tid .<. w) $+      zipWithM_ readReduceArgument reduce_acc_params arrs++  let do_reduce = do+        comment "read array element" $+          zipWithM_ readReduceArgument reduce_arr_params arrs+        comment "apply reduction operation" $+          compileBody' reduce_acc_params $ lambdaBody lam+        comment "write result of operation" $+          zipWithM_ writeReduceOpResult reduce_acc_params arrs+      in_wave_reduce = everythingVolatile do_reduce++      wave_size = kernelWaveSize constants+      group_size = kernelGroupSize constants+      wave_id = local_tid `quot` wave_size+      in_wave_id = local_tid - wave_id * wave_size+      num_waves = (sExt32 group_size + wave_size - 1) `quot` wave_size+      arg_in_bounds = local_tid + tvExp offset .<. w++      doing_in_wave_reductions =+        tvExp offset .<. wave_size+      apply_in_in_wave_iteration =+        (in_wave_id .&. (2 * tvExp offset - 1)) .==. 0+      in_wave_reductions = do+        offset <-- (1 :: Imp.TExp Int32)+        sWhile doing_in_wave_reductions $ do+          sWhen+            (arg_in_bounds .&&. apply_in_in_wave_iteration)+            in_wave_reduce+          offset <-- tvExp offset * 2++      doing_cross_wave_reductions =+        tvExp skip_waves .<. num_waves+      is_first_thread_in_wave =+        in_wave_id .==. 0+      wave_not_skipped =+        (wave_id .&. (2 * tvExp skip_waves - 1)) .==. 0+      apply_in_cross_wave_iteration =+        arg_in_bounds .&&. is_first_thread_in_wave .&&. wave_not_skipped+      cross_wave_reductions =+        sWhile doing_cross_wave_reductions $ do+          barrier+          offset <-- tvExp skip_waves * wave_size+          sWhen+            apply_in_cross_wave_iteration+            do_reduce+          skip_waves <-- tvExp skip_waves * 2++  in_wave_reductions+  cross_wave_reductions++groupScan ::+  Maybe (Imp.TExp Int32 -> Imp.TExp Int32 -> Imp.TExp Bool) ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  Lambda KernelsMem ->+  [VName] ->+  InKernelGen ()+groupScan seg_flag arrs_full_size w lam arrs = do+  constants <- kernelConstants <$> askEnv+  renamed_lam <- renameLambda lam++  let ltid32 = kernelLocalThreadId constants+      ltid = sExt64 ltid32+      (x_params, y_params) = splitAt (length arrs) $ lambdaParams lam++  dLParams (lambdaParams lam ++ lambdaParams renamed_lam)++  ltid_in_bounds <- dPrimVE "ltid_in_bounds" $ ltid .<. w++  -- The scan works by splitting the group into blocks, which are+  -- scanned separately.  Typically, these blocks are smaller than+  -- the lockstep width, which enables barrier-free execution inside+  -- them.+  --+  -- We hardcode the block size here.  The only requirement is that+  -- it should not be less than the square root of the group size.+  -- With 32, we will work on groups of size 1024 or smaller, which+  -- fits every device Troels has seen.  Still, it would be nicer if+  -- it were a runtime parameter.  Some day.+  let block_size = 32+      simd_width = kernelWaveSize constants+      block_id = ltid32 `quot` block_size+      in_block_id = ltid32 - block_id * block_size+      doInBlockScan seg_flag' active =+        inBlockScan+          constants+          seg_flag'+          arrs_full_size+          simd_width+          block_size+          active+          arrs+          barrier+      array_scan = not $ all primType $ lambdaReturnType lam+      barrier+        | array_scan =+          sOp $ Imp.Barrier Imp.FenceGlobal+        | otherwise =+          sOp $ Imp.Barrier Imp.FenceLocal++      group_offset = sExt64 (kernelGroupId constants) * kernelGroupSize constants++      writeBlockResult p arr+        | primType $ paramType p =+          copyDWIM arr [DimFix $ sExt64 block_id] (Var $ paramName p) []+        | otherwise =+          copyDWIM arr [DimFix $ group_offset + sExt64 block_id] (Var $ paramName p) []++      readPrevBlockResult p arr+        | primType $ paramType p =+          copyDWIM (paramName p) [] (Var arr) [DimFix $ sExt64 block_id - 1]+        | otherwise =+          copyDWIM (paramName p) [] (Var arr) [DimFix $ group_offset + sExt64 block_id - 1]++  doInBlockScan seg_flag ltid_in_bounds lam+  barrier++  let is_first_block = block_id .==. 0+  when array_scan $ do+    sComment "save correct values for first block" $+      sWhen is_first_block $+        forM_ (zip x_params arrs) $ \(x, arr) ->+          unless (primType $ paramType x) $+            copyDWIM arr [DimFix $ arrs_full_size + group_offset + sExt64 block_size + ltid] (Var $ paramName x) []++    barrier++  let last_in_block = in_block_id .==. block_size - 1+  sComment "last thread of block 'i' writes its result to offset 'i'" $+    sWhen (last_in_block .&&. ltid_in_bounds) $+      everythingVolatile $+        zipWithM_ writeBlockResult x_params arrs++  barrier++  let first_block_seg_flag = do+        flag_true <- seg_flag+        Just $ \from to ->+          flag_true (from * block_size + block_size -1) (to * block_size + block_size -1)+  comment+    "scan the first block, after which offset 'i' contains carry-in for block 'i+1'"+    $ doInBlockScan first_block_seg_flag (is_first_block .&&. ltid_in_bounds) renamed_lam++  barrier++  when array_scan $ do+    sComment "move correct values for first block back a block" $+      sWhen is_first_block $+        forM_ (zip x_params arrs) $ \(x, arr) ->+          unless (primType $ paramType x) $+            copyDWIM+              arr+              [DimFix $ arrs_full_size + group_offset + ltid]+              (Var arr)+              [DimFix $ arrs_full_size + group_offset + sExt64 block_size + ltid]++    barrier++  let read_carry_in = do+        forM_ (zip x_params y_params) $ \(x, y) ->+          copyDWIM (paramName y) [] (Var (paramName x)) []+        zipWithM_ readPrevBlockResult x_params arrs++      y_to_x = forM_ (zip x_params y_params) $ \(x, y) ->+        when (primType (paramType x)) $+          copyDWIM (paramName x) [] (Var (paramName y)) []++      op_to_x+        | Nothing <- seg_flag =+          compileBody' x_params $ lambdaBody lam+        | Just flag_true <- seg_flag = do+          inactive <-+            dPrimVE "inactive" $ flag_true (block_id * block_size -1) ltid32+          sWhen inactive y_to_x+          when array_scan barrier+          sUnless inactive $ compileBody' x_params $ lambdaBody lam++      write_final_result =+        forM_ (zip x_params arrs) $ \(p, arr) ->+          when (primType $ paramType p) $+            copyDWIM arr [DimFix ltid] (Var $ paramName p) []++  sComment "carry-in for every block except the first" $+    sUnless (is_first_block .||. bNot ltid_in_bounds) $ do+      sComment "read operands" read_carry_in+      sComment "perform operation" op_to_x+      sComment "write final result" write_final_result++  barrier++  sComment "restore correct values for first block" $+    sWhen is_first_block $+      forM_ (zip3 x_params y_params arrs) $ \(x, y, arr) ->+        if primType (paramType y)+          then copyDWIM arr [DimFix ltid] (Var $ paramName y) []+          else copyDWIM (paramName x) [] (Var arr) [DimFix $ arrs_full_size + group_offset + ltid]++  barrier++inBlockScan ::+  KernelConstants ->+  Maybe (Imp.TExp Int32 -> Imp.TExp Int32 -> Imp.TExp Bool) ->+  Imp.TExp Int64 ->+  Imp.TExp Int32 ->+  Imp.TExp Int32 ->+  Imp.TExp Bool ->+  [VName] ->+  InKernelGen () ->+  Lambda KernelsMem ->+  InKernelGen ()+inBlockScan constants seg_flag arrs_full_size lockstep_width block_size active arrs barrier scan_lam = everythingVolatile $ do+  skip_threads <- dPrim "skip_threads" int32+  let in_block_thread_active =+        tvExp skip_threads .<=. in_block_id+      actual_params = lambdaParams scan_lam+      (x_params, y_params) =+        splitAt (length actual_params `div` 2) actual_params+      y_to_x =+        forM_ (zip x_params y_params) $ \(x, y) ->+          when (primType (paramType x)) $+            copyDWIM (paramName x) [] (Var (paramName y)) []++  -- Set initial y values+  sComment "read input for in-block scan" $+    sWhen active $ do+      zipWithM_ readInitial y_params arrs+      -- Since the final result is expected to be in x_params, we may+      -- need to copy it there for the first thread in the block.+      sWhen (in_block_id .==. 0) y_to_x++  when array_scan barrier++  let op_to_x+        | Nothing <- seg_flag =+          compileBody' x_params $ lambdaBody scan_lam+        | Just flag_true <- seg_flag = do+          inactive <-+            dPrimVE "inactive" $+              flag_true (ltid32 - tvExp skip_threads) ltid32+          sWhen inactive y_to_x+          when array_scan barrier+          sUnless inactive $ compileBody' x_params $ lambdaBody scan_lam++      maybeBarrier =+        sWhen+          (lockstep_width .<=. tvExp skip_threads)+          barrier++  sComment "in-block scan (hopefully no barriers needed)" $ do+    skip_threads <-- 1+    sWhile (tvExp skip_threads .<. block_size) $ do+      sWhen (in_block_thread_active .&&. active) $ do+        sComment "read operands" $+          zipWithM_ (readParam (sExt64 $ tvExp skip_threads)) x_params arrs+        sComment "perform operation" op_to_x++      maybeBarrier++      sWhen (in_block_thread_active .&&. active) $+        sComment "write result" $+          sequence_ $ zipWith3 writeResult x_params y_params arrs++      maybeBarrier++      skip_threads <-- tvExp skip_threads * 2+  where+    block_id = ltid32 `quot` block_size+    in_block_id = ltid32 - block_id * block_size+    ltid32 = kernelLocalThreadId constants+    ltid = sExt64 ltid32+    gtid = sExt64 $ kernelGlobalThreadId constants+    array_scan = not $ all primType $ lambdaReturnType scan_lam++    readInitial p arr+      | primType $ paramType p =+        copyDWIM (paramName p) [] (Var arr) [DimFix ltid]+      | otherwise =+        copyDWIM (paramName p) [] (Var arr) [DimFix gtid]++    readParam behind p arr+      | primType $ paramType p =+        copyDWIM (paramName p) [] (Var arr) [DimFix $ ltid - behind]+      | otherwise =+        copyDWIM (paramName p) [] (Var arr) [DimFix $ gtid - behind + arrs_full_size]++    writeResult x y arr+      | primType $ paramType x = do+        copyDWIM arr [DimFix ltid] (Var $ paramName x) []+        copyDWIM (paramName y) [] (Var $ paramName x) []+      | otherwise =+        copyDWIM (paramName y) [] (Var $ paramName x) []++computeMapKernelGroups :: Imp.TExp Int64 -> CallKernelGen (Imp.TExp Int64, Imp.TExp Int64)+computeMapKernelGroups kernel_size = do+  group_size <- dPrim "group_size" int64+  fname <- askFunction+  let group_size_key = keyWithEntryPoint fname $ nameFromString $ pretty $ tvVar group_size+  sOp $ Imp.GetSize (tvVar group_size) group_size_key Imp.SizeGroup+  num_groups <- dPrimV "num_groups" $ kernel_size `divUp` tvExp group_size+  return (tvExp num_groups, tvExp group_size)++simpleKernelConstants ::+  Imp.TExp Int64 ->+  String ->+  CallKernelGen (KernelConstants, InKernelGen ())+simpleKernelConstants kernel_size desc = do+  thread_gtid <- newVName $ desc ++ "_gtid"+  thread_ltid <- newVName $ desc ++ "_ltid"+  group_id <- newVName $ desc ++ "_gid"+  (num_groups, group_size) <- computeMapKernelGroups kernel_size+  let set_constants = do+        dPrim_ thread_gtid int32+        dPrim_ thread_ltid int32+        dPrim_ group_id int32+        sOp (Imp.GetGlobalId thread_gtid 0)+        sOp (Imp.GetLocalId thread_ltid 0)+        sOp (Imp.GetGroupId group_id 0)++  return+    ( KernelConstants+        (Imp.vi32 thread_gtid)+        (Imp.vi32 thread_ltid)+        (Imp.vi32 group_id)+        thread_gtid+        thread_ltid+        group_id+        num_groups+        group_size+        (sExt32 (group_size * num_groups))+        0+        (Imp.vi64 thread_gtid .<. kernel_size)+        mempty,+      set_constants+    )++-- | For many kernels, we may not have enough physical groups to cover+-- the logical iteration space.  Some groups thus have to perform+-- double duty; we put an outer loop to accomplish this.  The+-- advantage over just launching a bazillion threads is that the cost+-- of memory expansion should be proportional to the number of+-- *physical* threads (hardware parallelism), not the amount of+-- application parallelism.+virtualiseGroups ::+  SegVirt ->+  Imp.TExp Int32 ->+  (Imp.TExp Int32 -> InKernelGen ()) ->+  InKernelGen ()+virtualiseGroups SegVirt required_groups m = do+  constants <- kernelConstants <$> askEnv+  phys_group_id <- dPrim "phys_group_id" int32+  sOp $ Imp.GetGroupId (tvVar phys_group_id) 0+  let iterations =+        (required_groups - tvExp phys_group_id)+          `divUp` sExt32 (kernelNumGroups constants)++  sFor "i" iterations $ \i -> do+    m . tvExp+      =<< dPrimV+        "virt_group_id"+        (tvExp phys_group_id + i * sExt32 (kernelNumGroups constants))+    -- Make sure the virtual group is actually done before we let+    -- another virtual group have its way with it.+    sOp $ Imp.Barrier Imp.FenceGlobal+virtualiseGroups _ _ m = do+  gid <- kernelGroupIdVar . kernelConstants <$> askEnv+  m $ Imp.vi32 gid++sKernelThread ::+  String ->+  Count NumGroups (Imp.TExp Int64) ->+  Count GroupSize (Imp.TExp Int64) ->+  VName ->+  InKernelGen () ->+  CallKernelGen ()+sKernelThread = sKernel threadOperations kernelGlobalThreadId++sKernelGroup ::+  String ->+  Count NumGroups (Imp.TExp Int64) ->+  Count GroupSize (Imp.TExp Int64) ->+  VName ->+  InKernelGen () ->+  CallKernelGen ()+sKernelGroup = sKernel groupOperations kernelGroupId++sKernelFailureTolerant ::+  Bool ->+  Operations KernelsMem KernelEnv Imp.KernelOp ->+  KernelConstants ->+  Name ->+  InKernelGen () ->+  CallKernelGen ()+sKernelFailureTolerant tol ops constants name m = do+  HostEnv atomics <- askEnv+  body <- makeAllMemoryGlobal $ subImpM_ (KernelEnv atomics constants) ops m+  uses <- computeKernelUses body mempty+  emit $+    Imp.Op $+      Imp.CallKernel+        Imp.Kernel+          { Imp.kernelBody = body,+            Imp.kernelUses = uses,+            Imp.kernelNumGroups = [untyped $ kernelNumGroups constants],+            Imp.kernelGroupSize = [untyped $ kernelGroupSize constants],+            Imp.kernelName = name,+            Imp.kernelFailureTolerant = tol+          }++sKernel ::+  Operations KernelsMem KernelEnv Imp.KernelOp ->+  (KernelConstants -> Imp.TExp Int32) ->+  String ->+  Count NumGroups (Imp.TExp Int64) ->+  Count GroupSize (Imp.TExp Int64) ->+  VName ->+  InKernelGen () ->+  CallKernelGen ()+sKernel ops flatf name num_groups group_size v f = do+  (constants, set_constants) <- kernelInitialisationSimple num_groups group_size+  name' <- nameForFun $ name ++ "_" ++ show (baseTag v)+  sKernelFailureTolerant False ops constants name' $ do+    set_constants+    dPrimV_ v $ flatf constants+    f++copyInGroup :: CopyCompiler KernelsMem KernelEnv Imp.KernelOp+copyInGroup pt destloc destslice srcloc srcslice = do+  dest_space <- entryMemSpace <$> lookupMemory (memLocationName destloc)+  src_space <- entryMemSpace <$> lookupMemory (memLocationName srcloc)++  case (dest_space, src_space) of+    (ScalarSpace destds _, ScalarSpace srcds _) -> do+      let destslice' =+            replicate (length destslice - length destds) (DimFix 0)+              ++ takeLast (length destds) destslice+          srcslice' =+            replicate (length srcslice - length srcds) (DimFix 0)+              ++ takeLast (length srcds) srcslice+      copyElementWise pt destloc destslice' srcloc srcslice'+    _ -> do+      groupCoverSpace (sliceDims destslice) $ \is ->+        copyElementWise+          pt+          destloc+          (map DimFix $ fixSlice destslice is)+          srcloc+          (map DimFix $ fixSlice srcslice is)+      sOp $ Imp.Barrier Imp.FenceLocal++threadOperations, groupOperations :: Operations KernelsMem KernelEnv Imp.KernelOp+threadOperations =+  (defaultOperations compileThreadOp)+    { opsCopyCompiler = copyElementWise,+      opsExpCompiler = compileThreadExp,+      opsStmsCompiler = \_ -> defCompileStms mempty,+      opsAllocCompilers =+        M.fromList [(Space "local", allocLocal)]+    }+groupOperations =+  (defaultOperations compileGroupOp)+    { opsCopyCompiler = copyInGroup,+      opsExpCompiler = compileGroupExp,+      opsStmsCompiler = \_ -> defCompileStms mempty,+      opsAllocCompilers =+        M.fromList [(Space "local", allocLocal)]+    }++-- | Perform a Replicate with a kernel.+sReplicateKernel :: VName -> SubExp -> CallKernelGen ()+sReplicateKernel arr se = do+  t <- subExpType se+  ds <- dropLast (arrayRank t) . arrayDims <$> lookupType arr++  let dims = map toInt64Exp $ ds ++ arrayDims t+  (constants, set_constants) <-+    simpleKernelConstants (product $ map sExt64 dims) "replicate"++  fname <- askFunction+  let name =+        keyWithEntryPoint fname $+          nameFromString $+            "replicate_" ++ show (baseTag $ kernelGlobalThreadIdVar constants)+      is' = unflattenIndex dims $ sExt64 $ kernelGlobalThreadId constants++  sKernelFailureTolerant True threadOperations constants name $ do+    set_constants+    sWhen (kernelThreadActive constants) $+      copyDWIMFix arr is' se $ drop (length ds) is'++replicateName :: PrimType -> String+replicateName bt = "replicate_" ++ pretty bt++replicateForType :: PrimType -> CallKernelGen Name+replicateForType bt = do+  let fname = nameFromString $ "builtin#" <> replicateName bt++  exists <- hasFunction fname+  unless exists $ do+    mem <- newVName "mem"+    num_elems <- newVName "num_elems"+    val <- newVName "val"++    let params =+          [ Imp.MemParam mem (Space "device"),+            Imp.ScalarParam num_elems int32,+            Imp.ScalarParam val bt+          ]+        shape = Shape [Var num_elems]+    function fname [] params $ do+      arr <-+        sArray "arr" bt shape $+          ArrayIn mem $+            IxFun.iota $+              map pe64 $ shapeDims shape+      sReplicateKernel arr $ Var val++  return fname++replicateIsFill :: VName -> SubExp -> CallKernelGen (Maybe (CallKernelGen ()))+replicateIsFill arr v = do+  ArrayEntry (MemLocation arr_mem arr_shape arr_ixfun) _ <- lookupArray arr+  v_t <- subExpType v+  case v_t of+    Prim v_t'+      | IxFun.isLinear arr_ixfun -> return $+        Just $ do+          fname <- replicateForType v_t'+          emit $+            Imp.Call+              []+              fname+              [ Imp.MemArg arr_mem,+                Imp.ExpArg $ untyped $ product $ map toInt64Exp arr_shape,+                Imp.ExpArg $ toExp' v_t' v+              ]+    _ -> return Nothing++-- | Perform a Replicate with a kernel.+sReplicate :: VName -> SubExp -> CallKernelGen ()+sReplicate arr se = do+  -- If the replicate is of a particularly common and simple form+  -- (morally a memset()/fill), then we use a common function.+  is_fill <- replicateIsFill arr se++  case is_fill of+    Just m -> m+    Nothing -> sReplicateKernel arr se++-- | Perform an Iota with a kernel.+sIotaKernel ::+  VName ->+  Imp.TExp Int64 ->+  Imp.Exp ->+  Imp.Exp ->+  IntType ->+  CallKernelGen ()+sIotaKernel arr n x s et = do+  destloc <- entryArrayLocation <$> lookupArray arr+  (constants, set_constants) <- simpleKernelConstants n "iota"++  fname <- askFunction+  let name =+        keyWithEntryPoint fname $+          nameFromString $+            "iota_" ++ pretty et ++ "_"+              ++ show (baseTag $ kernelGlobalThreadIdVar constants)++  sKernelFailureTolerant True threadOperations constants name $ do+    set_constants+    let gtid = sExt64 $ kernelGlobalThreadId constants+    sWhen (kernelThreadActive constants) $ do+      (destmem, destspace, destidx) <- fullyIndexArray' destloc [gtid]++      emit $+        Imp.Write destmem destidx (IntType et) destspace Imp.Nonvolatile $+          BinOpExp+            (Add et OverflowWrap)+            (BinOpExp (Mul et OverflowWrap) (Imp.sExt et $ untyped gtid) s)+            x++iotaName :: IntType -> String+iotaName bt = "iota_" ++ pretty bt++iotaForType :: IntType -> CallKernelGen Name+iotaForType bt = do+  let fname = nameFromString $ "builtin#" <> iotaName bt++  exists <- hasFunction fname+  unless exists $ do+    mem <- newVName "mem"+    n <- newVName "n"+    x <- newVName "x"+    s <- newVName "s"++    let params =+          [ Imp.MemParam mem (Space "device"),+            Imp.ScalarParam n int32,+            Imp.ScalarParam x $ IntType bt,+            Imp.ScalarParam s $ IntType bt+          ]+        shape = Shape [Var n]+        n' = Imp.vi64 n+        x' = Imp.var x $ IntType bt+        s' = Imp.var s $ IntType bt++    function fname [] params $ do+      arr <-+        sArray "arr" (IntType bt) shape $+          ArrayIn mem $+            IxFun.iota $+              map pe64 $ shapeDims shape+      sIotaKernel arr (sExt64 n') x' s' bt++  return fname++-- | Perform an Iota with a kernel.+sIota ::+  VName ->+  Imp.TExp Int64 ->+  Imp.Exp ->+  Imp.Exp ->+  IntType ->+  CallKernelGen ()+sIota arr n x s et = do+  ArrayEntry (MemLocation arr_mem _ arr_ixfun) _ <- lookupArray arr+  if IxFun.isLinear arr_ixfun+    then do+      fname <- iotaForType et+      emit $+        Imp.Call+          []+          fname+          [Imp.MemArg arr_mem, Imp.ExpArg $ untyped n, Imp.ExpArg x, Imp.ExpArg s]+    else sIotaKernel arr n x s et++sCopy :: CopyCompiler KernelsMem HostEnv Imp.HostOp+sCopy+  bt+  destloc@(MemLocation destmem _ _)+  destslice+  srcloc@(MemLocation srcmem _ _)+  srcslice =+    do+      -- Note that the shape of the destination and the source are+      -- necessarily the same.+      let shape = sliceDims srcslice+          kernel_size = product shape++      (constants, set_constants) <- simpleKernelConstants kernel_size "copy"++      fname <- askFunction+      let name =+            keyWithEntryPoint fname $+              nameFromString $+                "copy_" ++ show (baseTag $ kernelGlobalThreadIdVar constants)++      sKernelFailureTolerant True threadOperations constants name $ do+        set_constants++        let gtid = sExt64 $ kernelGlobalThreadId constants+            dest_is = unflattenIndex shape gtid+            src_is = dest_is++        (_, destspace, destidx) <-+          fullyIndexArray' destloc $ fixSlice destslice dest_is+        (_, srcspace, srcidx) <-+          fullyIndexArray' srcloc $ fixSlice srcslice src_is++        sWhen (gtid .<. kernel_size) $+          emit $+            Imp.Write destmem destidx bt destspace Imp.Nonvolatile $+              Imp.index srcmem srcidx bt srcspace Imp.Nonvolatile++compileGroupResult ::+  SegSpace ->+  PatElem KernelsMem ->+  KernelResult ->+  InKernelGen ()+compileGroupResult _ pe (TileReturns [(w, per_group_elems)] what) = do+  n <- toInt64Exp . arraySize 0 <$> lookupType what++  constants <- kernelConstants <$> askEnv+  let ltid = sExt64 $ kernelLocalThreadId constants+      offset =+        toInt64Exp per_group_elems+          * sExt64 (kernelGroupId constants)++  -- Avoid loop for the common case where each thread is statically+  -- known to write at most one element.+  localOps threadOperations $+    if toInt64Exp per_group_elems == kernelGroupSize constants+      then+        sWhen (ltid + offset .<. toInt64Exp w) $+          copyDWIMFix (patElemName pe) [ltid + offset] (Var what) [ltid]+      else sFor "i" (n `divUp` kernelGroupSize constants) $ \i -> do+        j <- dPrimVE "j" $ kernelGroupSize constants * i + ltid+        sWhen (j + offset .<. toInt64Exp w) $+          copyDWIMFix (patElemName pe) [j + offset] (Var what) [j]+compileGroupResult space pe (TileReturns dims what) = do+  let gids = map fst $ unSegSpace space+      out_tile_sizes = map (toInt64Exp . snd) dims+      group_is = zipWith (*) (map Imp.vi64 gids) out_tile_sizes+  local_is <- localThreadIDs $ map snd dims+  is_for_thread <-+    mapM (dPrimV "thread_out_index") $+      zipWith (+) group_is local_is++  localOps threadOperations $+    sWhen (isActive $ zip (map tvVar is_for_thread) $ map fst dims) $+      copyDWIMFix (patElemName pe) (map tvExp is_for_thread) (Var what) local_is+compileGroupResult space pe (Returns _ what) = do+  constants <- kernelConstants <$> askEnv+  in_local_memory <- arrayInLocalMemory what+  let gids = map (Imp.vi64 . fst) $ unSegSpace space++  if not in_local_memory+    then+      localOps threadOperations $+        sWhen (kernelLocalThreadId constants .==. 0) $+          copyDWIMFix (patElemName pe) gids what []+    else -- If the result of the group is an array in local memory, we+    -- store it by collective copying among all the threads of the+    -- group.  TODO: also do this if the array is in global memory+    -- (but this is a bit more tricky, synchronisation-wise).+      copyDWIMFix (patElemName pe) gids what []+compileGroupResult _ _ WriteReturns {} =+  compilerLimitationS "compileGroupResult: WriteReturns not handled yet."+compileGroupResult _ _ ConcatReturns {} =+  compilerLimitationS "compileGroupResult: ConcatReturns not handled yet."++compileThreadResult ::+  SegSpace ->+  PatElem KernelsMem ->+  KernelResult ->+  InKernelGen ()+compileThreadResult space pe (Returns _ what) = do+  let is = map (Imp.vi64 . fst) $ unSegSpace space+  copyDWIMFix (patElemName pe) is what []+compileThreadResult _ pe (ConcatReturns SplitContiguous _ per_thread_elems what) = do+  constants <- kernelConstants <$> askEnv+  let offset =+        toInt64Exp per_thread_elems+          * sExt64 (kernelGlobalThreadId constants)+  n <- toInt64Exp . arraySize 0 <$> lookupType what+  copyDWIM (patElemName pe) [DimSlice offset n 1] (Var what) []+compileThreadResult _ pe (ConcatReturns (SplitStrided stride) _ _ what) = do+  offset <- sExt64 . kernelGlobalThreadId . kernelConstants <$> askEnv+  n <- toInt64Exp . arraySize 0 <$> lookupType what+  copyDWIM (patElemName pe) [DimSlice offset n $ toInt64Exp stride] (Var what) []+compileThreadResult _ pe (WriteReturns rws _arr dests) = do+  constants <- kernelConstants <$> askEnv+  let rws' = map toInt64Exp rws+  forM_ dests $ \(slice, e) -> do+    let slice' = map (fmap toInt64Exp) slice+        condInBounds (DimFix i) rw =+          0 .<=. i .&&. i .<. rw+        condInBounds (DimSlice i n s) rw =+          0 .<=. i .&&. i + n * s .<. rw+        write =+          foldl (.&&.) (kernelThreadActive constants) $+            zipWith condInBounds slice' rws'+    sWhen write $ copyDWIM (patElemName pe) slice' e []+compileThreadResult _ _ TileReturns {} =+  compilerBugS "compileThreadResult: TileReturns unhandled."++arrayInLocalMemory :: SubExp -> InKernelGen Bool+arrayInLocalMemory (Var name) = do+  res <- lookupVar name+  case res of+    ArrayVar _ entry ->+      (Space "local" ==) . entryMemSpace+        <$> lookupMemory (memLocationName (entryArrayLocation entry))+    _ -> return False+arrayInLocalMemory Constant {} = return False
src/Futhark/CodeGen/ImpGen/Kernels/SegHist.hs view
@@ -1,979 +1,1143 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}--- | Our compilation strategy for 'SegHist' is based around avoiding--- bin conflicts.  We do this by splitting the input into chunks, and--- for each chunk computing a single subhistogram.  Then we combine--- the subhistograms using an ordinary segmented reduction ('SegRed').------ There are some branches around to efficiently handle the case where--- we use only a single subhistogram (because it's large), so that we--- respect the asymptotics, and do not copy the destination array.------ We also use a heuristic strategy for computing subhistograms in--- local memory when possible.  Given:------ H: total size of histograms in bytes, including any lock arrays.------ G: group size------ T: number of bytes of local memory each thread can be given without--- impacting occupancy (determined experimentally, e.g. 32).------ LMAX: maximum amount of local memory per workgroup (hard limit).------ We wish to compute:------ COOP: cooperation level (number of threads per subhistogram)------ LH: number of local memory subhistograms------ We do this as:------ COOP = ceil(H / T)--- LH = ceil((G*T)/H)--- if COOP <= G && H <= LMAX then---   use local memory--- else---   use global memory--module Futhark.CodeGen.ImpGen.Kernels.SegHist-  ( compileSegHist )-  where--import Control.Monad.Except-import Data.Maybe-import Data.List (foldl', genericLength, zip4, zip6)--import Prelude hiding (quot, rem)--import Futhark.MonadFreshNames-import Futhark.IR.KernelsMem-import qualified Futhark.IR.Mem.IxFun as IxFun-import Futhark.Pass.ExplicitAllocations()-import qualified Futhark.CodeGen.ImpCode.Kernels as Imp-import Futhark.CodeGen.ImpGen-import Futhark.CodeGen.ImpGen.Kernels.SegRed (compileSegRed')-import Futhark.CodeGen.ImpGen.Kernels.Base-import Futhark.Util.IntegralExp (divUp, quot, rem)-import Futhark.Util (chunks, mapAccumLM, maxinum, splitFromEnd, takeLast)-import Futhark.Construct (fullSliceNum)--data SubhistosInfo = SubhistosInfo { subhistosArray :: VName-                                   , subhistosAlloc :: CallKernelGen ()-                                   }--data SegHistSlug = SegHistSlug-                   { slugOp :: HistOp KernelsMem-                   , slugNumSubhistos :: VName-                   , slugSubhistos :: [SubhistosInfo]-                   , slugAtomicUpdate :: AtomicUpdate KernelsMem KernelEnv-                   }--histoSpaceUsage :: HistOp KernelsMem-                -> Imp.Count Imp.Bytes Imp.Exp-histoSpaceUsage op =-  fmap (sExt Int32) $ sum $-  map (typeSize .-       (`arrayOfRow` histWidth op) .-       (`arrayOfShape` histShape op)) $-  lambdaReturnType $ histOp op---- | Figure out how much memory is needed per histogram, both--- segmented and unsegmented,, and compute some other auxiliary--- information.-computeHistoUsage :: SegSpace-                  -> HistOp KernelsMem-                  -> CallKernelGen (Imp.Count Imp.Bytes Imp.Exp,-                                    Imp.Count Imp.Bytes Imp.Exp,-                                    SegHistSlug)-computeHistoUsage space op = do-  let segment_dims = init $ unSegSpace space-      num_segments = length segment_dims--  -- Create names for the intermediate array memory blocks,-  -- memory block sizes, arrays, and number of subhistograms.-  num_subhistos <- dPrim "num_subhistos" int32-  subhisto_infos <- forM (zip (histDest op) (histNeutral op)) $ \(dest, ne) -> do-    dest_t <- lookupType dest-    dest_mem <- entryArrayLocation <$> lookupArray dest--    subhistos_mem <--      sDeclareMem (baseString dest ++ "_subhistos_mem") (Space "device")--    let subhistos_shape = Shape (map snd segment_dims++[Var num_subhistos]) <>-                          stripDims num_segments (arrayShape dest_t)-        subhistos_membind = ArrayIn subhistos_mem $ IxFun.iota $-                            map (primExpFromSubExp int32) $ shapeDims subhistos_shape-    subhistos <- sArray (baseString dest ++ "_subhistos")-                 (elemType dest_t) subhistos_shape subhistos_membind--    return $ SubhistosInfo subhistos $ do-      let unitHistoCase =-            emit $-            Imp.SetMem subhistos_mem (memLocationName dest_mem) $-            Space "device"--          multiHistoCase = do-            let num_elems = foldl' (*) (Imp.var num_subhistos int32) $-                            map (toExp' int32) $ arrayDims dest_t--            let subhistos_mem_size =-                  Imp.bytes $-                  Imp.unCount (Imp.elements num_elems `Imp.withElemType` elemType dest_t)--            sAlloc_ subhistos_mem subhistos_mem_size $ Space "device"-            sReplicate subhistos ne-            subhistos_t <- lookupType subhistos-            let slice = fullSliceNum (map (toExp' int32) $ arrayDims subhistos_t) $-                        map (unitSlice 0 . toExp' int32 . snd) segment_dims ++-                        [DimFix 0]-            sUpdate subhistos slice $ Var dest--      sIf (Imp.var num_subhistos int32 .==. 1) unitHistoCase multiHistoCase--  let h = histoSpaceUsage op-      segmented_h = h * product (map (Imp.bytes . toExp' int32) $ init $ segSpaceDims space)--  atomics <- hostAtomics <$> askEnv--  return (h,-          segmented_h,-          SegHistSlug op num_subhistos subhisto_infos $-          atomicUpdateLocking atomics $ histOp op)--prepareAtomicUpdateGlobal :: Maybe Locking -> [VName] -> SegHistSlug-                          -> CallKernelGen (Maybe Locking,-                                            [Imp.Exp] -> InKernelGen ())-prepareAtomicUpdateGlobal l dests slug =-  -- We need a separate lock array if the operators are not all of a-  -- particularly simple form that permits pure atomic operations.-  case (l, slugAtomicUpdate slug) of-    (_, AtomicPrim f) -> return (l, f (Space "global") dests)-    (_, AtomicCAS f) -> return (l, f (Space "global") dests)-    (Just l', AtomicLocking f) -> return (l, f l' (Space "global") dests)-    (Nothing, AtomicLocking f) -> do-      -- The number of locks used here is too low, but since we are-      -- currently forced to inline a huge list, I'm keeping it down-      -- for now.  Some quick experiments suggested that it has little-      -- impact anyway (maybe the locking case is just too slow).-      ---      -- A fun solution would also be to use a simple hashing-      -- algorithm to ensure good distribution of locks.-      let num_locks = 100151-          dims = map (toExp' int32) $-                 shapeDims (histShape (slugOp slug)) ++-                 [ Var (slugNumSubhistos slug)-                 , histWidth (slugOp slug)]-      locks <--        sStaticArray "hist_locks" (Space "device") int32 $-        Imp.ArrayZeros num_locks-      let l' = Locking locks 0 1 0 (pure . (`rem` fromIntegral num_locks) . flattenIndex dims)-      return (Just l', f l' (Space "global") dests)---- | Some kernel bodies are not safe (or efficient) to execute--- multiple times.-data Passage = MustBeSinglePass | MayBeMultiPass deriving (Eq, Ord)--bodyPassage :: KernelBody KernelsMem -> Passage-bodyPassage kbody-  | mempty == consumedInKernelBody (aliasAnalyseKernelBody kbody) =-      MayBeMultiPass-  | otherwise =-      MustBeSinglePass--prepareIntermediateArraysGlobal :: Passage -> Imp.Exp -> Imp.Exp -> [SegHistSlug]-                                -> CallKernelGen-                                   (Imp.Exp,-                                    [[Imp.Exp] -> InKernelGen ()])-prepareIntermediateArraysGlobal passage hist_T hist_N slugs = do-  -- The paper formulae assume there is only one histogram, but in our-  -- implementation there can be multiple that have been horisontally-  -- fused.  We do a bit of trickery with summings and averages to-  -- pretend there is really only one.  For the case of a single-  -- histogram, the actual calculations should be the same as in the-  -- paper.--  -- The sum of all Hs.-  hist_H <- dPrimVE "hist_H" . sum =<< mapM (toExp . histWidth . slugOp) slugs--  hist_RF <- dPrimVE "hist_RF" $-    sum (map (r64 . toExp' int32 . histRaceFactor . slugOp) slugs)-    / r64 (genericLength slugs)--  hist_el_size <- dPrimVE "hist_el_size" $ sum $ map slugElAvgSize slugs--  hist_C_max <- dPrimVE "hist_C_max" $-    Imp.BinOpExp (FMin Float64) (r64 hist_T) $ r64 hist_H / hist_k_ct_min--  hist_M_min <- dPrimVE "hist_M_min" $-    Imp.BinOpExp (SMax Int32) 1 $ t64 $ r64 hist_T / hist_C_max--  -- Querying L2 cache size is not reliable.  Instead we provide a-  -- tunable knob with a hopefully sane default.-  let hist_L2_def = 4 * 1024 * 1024-  hist_L2 <- dPrim "L2_size" int32-  entry <- askFunction-  -- Equivalent to F_L2*L2 in paper.-  sOp $ Imp.GetSize hist_L2-    (keyWithEntryPoint entry $ nameFromString (pretty hist_L2)) $-    Imp.SizeBespoke (nameFromString "L2_for_histogram") hist_L2_def--  let hist_L2_ln_sz = 16*4 -- L2 cache line size approximation--  hist_RACE_exp <- dPrimVE "hist_RACE_exp" $-    Imp.BinOpExp (FMax Float64) 1 $-    (hist_k_RF * hist_RF) /-    (hist_L2_ln_sz / r64 hist_el_size)--  hist_S <- dPrim "hist_S" int32--  -- For sparse histograms (H exceeds N) we only want a single chunk.-  sIf (hist_N .<. hist_H)-    (hist_S <-- 1) $-    hist_S <---    case passage of-      MayBeMultiPass ->-        (hist_M_min * hist_H * hist_el_size) `divUp`-        t64 (hist_F_L2 * r64 (Imp.vi32 hist_L2) * hist_RACE_exp)-      MustBeSinglePass ->-        1--  emit $ Imp.DebugPrint "Race expansion factor (RACE^exp)" $ Just hist_RACE_exp-  emit $ Imp.DebugPrint "Number of chunks (S)" $ Just $ Imp.vi32 hist_S--  histograms <- snd <$> mapAccumLM (onOp (Imp.vi32 hist_L2) hist_M_min (Imp.vi32 hist_S) hist_RACE_exp) Nothing slugs--  return (Imp.vi32 hist_S, histograms)-  where-    hist_k_ct_min = 2 -- Chosen experimentally-    hist_k_RF = 0.75 -- Chosen experimentally-    hist_F_L2 = 0.4 -- Chosen experimentally--    r64 = ConvOpExp (SIToFP Int32 Float64)-    t64 = ConvOpExp (FPToSI Float64 Int32)--    -- "Average element size" as computed by a formula that also takes-    -- locking into account.-    slugElAvgSize slug@(SegHistSlug op _ _ do_op) =-      case do_op of-        AtomicLocking{} ->-          slugElSize slug `quot` (1+genericLength (lambdaReturnType (histOp op)))-        _ ->-          slugElSize slug `quot` genericLength (lambdaReturnType (histOp op))--    -- "Average element size" as computed by a formula that also takes-    -- locking into account.-    slugElSize (SegHistSlug op _ _ do_op) =-      case do_op of-        AtomicLocking{} ->-          sExt Int32 $ unCount $-          sum $ map (typeSize . (`arrayOfShape` histShape op)) $-          Prim int32 : lambdaReturnType (histOp op)-        _ ->-          sExt Int32 $ unCount $ sum $-          map (typeSize . (`arrayOfShape` histShape op)) $-          lambdaReturnType (histOp op)--    onOp hist_L2 hist_M_min hist_S hist_RACE_exp l slug = do-      let SegHistSlug op num_subhistos subhisto_info do_op = slug-      hist_H <- toExp $ histWidth op--      hist_H_chk <- dPrimVE "hist_H_chk" $-                    hist_H `divUp` hist_S--      emit $ Imp.DebugPrint "Chunk size (H_chk)" $ Just hist_H_chk--      hist_k_max <- dPrimVE "hist_k_max" $-        Imp.BinOpExp (FMin Float64)-        (hist_F_L2 * (r64 hist_L2 / r64 (slugElSize slug)) * hist_RACE_exp)-        (r64 hist_N)-        / r64 hist_T--      hist_u <- dPrimVE "hist_u" $-                case do_op of-                  AtomicPrim{} -> 2-                  _            -> 1--      hist_C <- dPrimVE "hist_C" $-                Imp.BinOpExp (FMin Float64) (r64 hist_T) $-                r64 (hist_u * hist_H_chk) / hist_k_max--      -- Number of subhistograms per result histogram.-      hist_M <- dPrimVE "hist_M" $-        case slugAtomicUpdate slug of-          AtomicPrim{} -> 1-          _ -> Imp.BinOpExp (SMax Int32) hist_M_min $-               t64 $ r64 hist_T / hist_C--      emit $ Imp.DebugPrint "Elements/thread in L2 cache (k_max)" $ Just hist_k_max-      emit $ Imp.DebugPrint "Multiplication degree (M)" $ Just hist_M-      emit $ Imp.DebugPrint "Cooperation level (C)" $ Just hist_C--      -- num_subhistos is the variable we use to communicate back.-      num_subhistos <-- hist_M--      -- Initialise sub-histograms.-      ---      -- If hist_M is 1, then we just reuse the original-      -- destination.  The idea is to avoid a copy if we are writing a-      -- small number of values into a very large prior histogram.-      dests <- forM (zip (histDest op) subhisto_info) $ \(dest, info) -> do-        dest_mem <- entryArrayLocation <$> lookupArray dest--        sub_mem <- fmap memLocationName $-                   entryArrayLocation <$>-                   lookupArray (subhistosArray info)--        let unitHistoCase =-              emit $-              Imp.SetMem sub_mem (memLocationName dest_mem) $-              Space "device"--            multiHistoCase = subhistosAlloc info--        sIf (hist_M .==. 1) unitHistoCase multiHistoCase--        return $ subhistosArray info--      (l', do_op') <- prepareAtomicUpdateGlobal l dests slug--      return (l', do_op')--histKernelGlobalPass :: [PatElem KernelsMem]-                     -> Count NumGroups Imp.Exp-                     -> Count GroupSize Imp.Exp-                     -> SegSpace-                     -> [SegHistSlug]-                     -> KernelBody KernelsMem-                     -> [[Imp.Exp] -> InKernelGen ()]-                     -> Imp.Exp -> Imp.Exp-                     -> CallKernelGen ()-histKernelGlobalPass map_pes num_groups group_size space slugs kbody histograms hist_S chk_i = do--  let (space_is, space_sizes) = unzip $ unSegSpace space-      space_sizes_64 = map (sExt Int64 . toExp' int32) space_sizes-      total_w_64 = product space_sizes_64--  hist_H_chks <- forM (map (histWidth . slugOp) slugs) $ \w -> do-    w' <- toExp w-    dPrimVE "hist_H_chk" $ w' `divUp` hist_S--  sKernelThread "seghist_global" num_groups group_size (segFlat space) $ do-    constants <- kernelConstants <$> askEnv--    -- Compute subhistogram index for each thread, per histogram.-    subhisto_inds <- forM slugs $ \slug ->-      dPrimVE "subhisto_ind" $-      kernelGlobalThreadId constants `quot`-      (kernelNumThreads constants `divUp` Imp.vi32 (slugNumSubhistos slug))--    -- Loop over flat offsets into the input and output.  The-    -- calculation is done with 64-bit integers to avoid overflow,-    -- but the final unflattened segment indexes are 32 bit.-    let gtid = sExt Int64 $ kernelGlobalThreadId constants-        num_threads = sExt Int64 $ kernelNumThreads constants-    kernelLoop gtid num_threads total_w_64 $ \offset -> do--      -- Construct segment indices.-      let setIndex v e = do dPrim_ v int32-                            v <-- e-      zipWithM_ setIndex space_is $-        map (sExt Int32) $ unflattenIndex space_sizes_64 offset--      -- We execute the bucket function once and update each histogram serially.-      -- We apply the bucket function if j=offset+ltid is less than-      -- num_elements.  This also involves writing to the mapout-      -- arrays.-      let input_in_bounds = offset .<. total_w_64--      sWhen input_in_bounds $ compileStms mempty (kernelBodyStms kbody) $ do-        let (red_res, map_res) = splitFromEnd (length map_pes) $ kernelBodyResult kbody--        sComment "save map-out results" $-          forM_ (zip map_pes map_res) $ \(pe, res) ->-          copyDWIMFix (patElemName pe)-          (map (Imp.vi32 . fst) $ unSegSpace space)-          (kernelResultSubExp res) []--        let (buckets, vs) = splitAt (length slugs) red_res-            perOp = chunks $ map (length . histDest . slugOp) slugs--        sComment "perform atomic updates" $-          forM_ (zip6 (map slugOp slugs) histograms buckets (perOp vs) subhisto_inds hist_H_chks) $-          \(HistOp dest_w _ _ _ shape lam,-            do_op, bucket, vs', subhisto_ind, hist_H_chk) -> do--            let chk_beg = chk_i * hist_H_chk-                bucket' = toExp' int32 $ kernelResultSubExp bucket-                dest_w' = toExp' int32 dest_w-                bucket_in_bounds = chk_beg .<=. bucket' .&&.-                                   bucket' .<. (chk_beg + hist_H_chk) .&&.-                                   bucket' .<. dest_w'-                vs_params = takeLast (length vs') $ lambdaParams lam--            sWhen bucket_in_bounds $ do-              let bucket_is = map Imp.vi32 (init space_is) ++-                              [subhisto_ind, bucket']-              dLParams $ lambdaParams lam-              sLoopNest shape $ \is -> do-                forM_ (zip vs_params vs') $ \(p, res) ->-                  copyDWIMFix (paramName p) [] (kernelResultSubExp res) is-                do_op (bucket_is ++ is)---histKernelGlobal :: [PatElem KernelsMem]-                 -> Count NumGroups SubExp -> Count GroupSize SubExp-                 -> SegSpace-                 -> [SegHistSlug]-                 -> KernelBody KernelsMem-                 -> CallKernelGen ()-histKernelGlobal map_pes num_groups group_size space slugs kbody = do-  num_groups' <- traverse toExp num_groups-  group_size' <- traverse toExp group_size-  let (_space_is, space_sizes) = unzip $ unSegSpace space-      num_threads = unCount num_groups' * unCount group_size'--  emit $ Imp.DebugPrint "## Using global memory" Nothing--  (hist_S, histograms) <--    prepareIntermediateArraysGlobal (bodyPassage kbody)-    num_threads (toExp' int32 $ last space_sizes) slugs--  sFor "chk_i" hist_S $ \chk_i ->-    histKernelGlobalPass map_pes num_groups' group_size' space slugs kbody-    histograms hist_S chk_i--type InitLocalHistograms = [([VName],-                              SubExp ->-                              InKernelGen ([VName],-                                            [Imp.Exp] -> InKernelGen ()))]--prepareIntermediateArraysLocal :: VName-                               -> Count NumGroups Imp.Exp-                               -> SegSpace -> [SegHistSlug]-                               -> CallKernelGen InitLocalHistograms-prepareIntermediateArraysLocal num_subhistos_per_group groups_per_segment space slugs = do-  num_segments <- dPrimVE "num_segments" $-                  product $ map (toExp' int32 . snd) $ init $ unSegSpace space-  mapM (onOp num_segments) slugs-  where-    onOp num_segments (SegHistSlug op num_subhistos subhisto_info do_op) = do--      num_subhistos <-- unCount groups_per_segment * num_segments--      emit $ Imp.DebugPrint "Number of subhistograms in global memory" $-        Just $ Imp.vi32 num_subhistos--      mk_op <--        case do_op of-          AtomicPrim f -> return $ const $ return f-          AtomicCAS f -> return $ const $ return f-          AtomicLocking f -> return $ \hist_H_chk -> do-            let lock_shape =-                  Shape $ Var num_subhistos_per_group :-                  shapeDims (histShape op) ++-                  [hist_H_chk]--            dims <- mapM toExp $ shapeDims lock_shape--            locks <- sAllocArray "locks" int32 lock_shape $ Space "local"--            sComment "All locks start out unlocked" $-              groupCoverSpace dims $ \is ->-              copyDWIMFix locks is (intConst Int32 0) []--            return $ f $ Locking locks 0 1 0 id--      -- Initialise local-memory sub-histograms.  These are-      -- represented as two-dimensional arrays.-      let init_local_subhistos hist_H_chk = do-            local_subhistos <--              forM (histType op) $ \t -> do-                let sub_local_shape =-                      Shape [Var num_subhistos_per_group] <>-                      (arrayShape t `setOuterDim` hist_H_chk)-                sAllocArray "subhistogram_local"-                  (elemType t) sub_local_shape (Space "local")--            do_op' <- mk_op hist_H_chk--            return (local_subhistos, do_op' (Space "local") local_subhistos)--      -- Initialise global-memory sub-histograms.-      glob_subhistos <- forM subhisto_info $ \info -> do-        subhistosAlloc info-        return $ subhistosArray info--      return (glob_subhistos, init_local_subhistos)--histKernelLocalPass :: VName -> Count NumGroups Imp.Exp-                    -> [PatElem KernelsMem]-                    -> Count NumGroups Imp.Exp -> Count GroupSize Imp.Exp-                    -> SegSpace-                    -> [SegHistSlug]-                    -> KernelBody KernelsMem-                    -> InitLocalHistograms -> Imp.Exp -> Imp.Exp-                    -> CallKernelGen ()-histKernelLocalPass num_subhistos_per_group_var groups_per_segment map_pes num_groups group_size space slugs kbody-                    init_histograms hist_S chk_i = do-  let (space_is, space_sizes) = unzip $ unSegSpace space-      segment_is = init space_is-      segment_dims = init space_sizes-      (i_in_segment, segment_size) = last $ unSegSpace space-      num_subhistos_per_group = Imp.var num_subhistos_per_group_var int32--  segment_size' <- toExp segment_size--  num_segments <- dPrimVE "num_segments" $-                  product $ map (toExp' int32) segment_dims--  hist_H_chks <- forM (map (histWidth . slugOp) slugs) $ \w -> do-    w' <- toExp w-    dPrimV "hist_H_chk" $ w' `divUp` hist_S--  sKernelThread "seghist_local" num_groups group_size (segFlat space) $-    virtualiseGroups SegVirt (unCount groups_per_segment * num_segments) $ \group_id_var -> do--    constants <- kernelConstants <$> askEnv--    let group_id = Imp.vi32 group_id_var--    flat_segment_id <- dPrimVE "flat_segment_id" $ group_id `quot` unCount groups_per_segment-    gid_in_segment <- dPrimVE "gid_in_segment" $ group_id `rem` unCount groups_per_segment-    -- This pgtid is kind of a "virtualised physical" gtid - not the-    -- same thing as the gtid used for the SegHist itself.-    pgtid_in_segment <- dPrimVE "pgtid_in_segment" $-      gid_in_segment * kernelGroupSize constants + kernelLocalThreadId constants-    threads_per_segment <- dPrimVE "threads_per_segment" $-      unCount groups_per_segment * kernelGroupSize constants--    -- Set segment indices.-    zipWithM_ dPrimV_ segment_is $-      unflattenIndex (map (toExp' int32) segment_dims) flat_segment_id--    histograms <- forM (zip init_histograms hist_H_chks) $-                  \((glob_subhistos, init_local_subhistos), hist_H_chk) -> do-      (local_subhistos, do_op) <- init_local_subhistos $ Var hist_H_chk-      return (zip glob_subhistos local_subhistos, hist_H_chk, do_op)--    -- Find index of local subhistograms updated by this thread.  We-    -- try to ensure, as much as possible, that threads in the same-    -- warp use different subhistograms, to avoid conflicts.-    thread_local_subhisto_i <--      dPrimVE "thread_local_subhisto_i" $-      kernelLocalThreadId constants `rem` num_subhistos_per_group--    let onSlugs f = forM_ (zip slugs histograms) $ \(slug, (dests, hist_H_chk, _)) -> do-          let histo_dims = fmap (toExp' int32) $ Var hist_H_chk :-                           shapeDims (histShape (slugOp slug))-          histo_size <- dPrimVE "histo_size" $ product histo_dims-          f slug dests (Imp.vi32 hist_H_chk) histo_dims histo_size--    let onAllHistograms f =-          onSlugs $ \slug dests hist_H_chk histo_dims histo_size -> do-            let group_hists_size = num_subhistos_per_group * histo_size-            init_per_thread <- dPrimVE "init_per_thread" $-                               group_hists_size `divUp`-                               kernelGroupSize constants--            forM_ (zip dests (histNeutral $ slugOp slug)) $-              \((dest_global, dest_local), ne) ->-                sFor "local_i" init_per_thread $ \i -> do-                  j <- dPrimVE "j" $-                       i * kernelGroupSize constants +-                       kernelLocalThreadId constants-                  j_offset <- dPrimVE "j_offset" $-                              num_subhistos_per_group * histo_size * gid_in_segment + j--                  local_subhisto_i <- dPrimVE "local_subhisto_i" $ j `quot` histo_size-                  let local_bucket_is = unflattenIndex histo_dims $ j `rem` histo_size-                      global_bucket_is = head local_bucket_is + chk_i * hist_H_chk :-                                         tail local_bucket_is-                  global_subhisto_i <- dPrimVE "global_subhisto_i" $ j_offset `quot` histo_size--                  sWhen (j .<. group_hists_size) $-                    f dest_local dest_global (slugOp slug) ne-                    local_subhisto_i global_subhisto_i-                    local_bucket_is global_bucket_is--    sComment "initialize histograms in local memory" $-      onAllHistograms $ \dest_local dest_global op ne local_subhisto_i global_subhisto_i local_bucket_is global_bucket_is ->-      sComment "First subhistogram is initialised from global memory; others with neutral element." $ do-      let global_is = map Imp.vi32 segment_is ++ [0] ++ global_bucket_is-          local_is = local_subhisto_i : local_bucket_is-      sIf (global_subhisto_i .==. 0)-        (copyDWIMFix dest_local local_is (Var dest_global) global_is)-        (sLoopNest (histShape op) $ \is ->-            copyDWIMFix dest_local (local_is++is) ne [])--    sOp $ Imp.Barrier Imp.FenceLocal--    kernelLoop pgtid_in_segment threads_per_segment segment_size' $ \ie -> do-      dPrimV_ i_in_segment ie--      -- We execute the bucket function once and update each histogram-      -- serially.  This also involves writing to the mapout arrays if-      -- this is the first chunk.--      compileStms mempty (kernelBodyStms kbody) $ do--        let (red_res, map_res) = splitFromEnd (length map_pes) $-                             map kernelResultSubExp $ kernelBodyResult kbody-            (buckets, vs) = splitAt (length slugs) red_res-            perOp = chunks $ map (length . histDest . slugOp) slugs--        sWhen (chk_i .==. 0) $-          sComment "save map-out results" $-          forM_ (zip map_pes map_res) $ \(pe, se) ->-          copyDWIMFix (patElemName pe)-          (map Imp.vi32 space_is) se []--        forM_ (zip4 (map slugOp slugs) histograms buckets (perOp vs)) $-          \(HistOp dest_w _ _ _ shape lam,-            (_, hist_H_chk, do_op), bucket, vs') -> do--            let chk_beg = chk_i * Imp.vi32 hist_H_chk-                bucket' = toExp' int32 bucket-                dest_w' = toExp' int32 dest_w-                bucket_in_bounds = bucket' .<. dest_w' .&&.-                                   chk_beg .<=. bucket' .&&.-                                   bucket' .<. (chk_beg + Imp.vi32 hist_H_chk)-                bucket_is = [thread_local_subhisto_i, bucket' - chk_beg]-                vs_params = takeLast (length vs') $ lambdaParams lam--            sComment "perform atomic updates" $-              sWhen bucket_in_bounds $ do-              dLParams $ lambdaParams lam-              sLoopNest shape $ \is -> do-                forM_ (zip vs_params vs') $ \(p, v) ->-                  copyDWIMFix (paramName p) [] v is-                do_op (bucket_is ++ is)--    sOp $ Imp.ErrorSync Imp.FenceGlobal--    sComment "Compact the multiple local memory subhistograms to result in global memory" $-      onSlugs $ \slug dests hist_H_chk histo_dims histo_size -> do-      bins_per_thread <- dPrimVE "init_per_thread" $-                         histo_size `divUp` kernelGroupSize constants--      trunc_H <- dPrimV "trunc_H" $-                 Imp.BinOpExp (SMin Int32) hist_H_chk $-                 toExp' int32 (histWidth (slugOp slug)) --                 chk_i * head histo_dims-      let trunc_histo_dims = map (toExp' int32) $ Var trunc_H :-                             shapeDims (histShape (slugOp slug))-      trunc_histo_size <- dPrimVE "histo_size" $ product trunc_histo_dims--      sFor "local_i" bins_per_thread $ \i -> do-        j <- dPrimVE "j" $-             i * kernelGroupSize constants + kernelLocalThreadId constants-        sWhen (j .<. trunc_histo_size) $ do-          -- We are responsible for compacting the flat bin 'j', which-          -- we immediately unflatten.-          let local_bucket_is = unflattenIndex histo_dims j-              global_bucket_is = head local_bucket_is + chk_i * hist_H_chk :-                                 tail local_bucket_is-          dLParams $ lambdaParams $ histOp $ slugOp slug-          let (global_dests, local_dests) = unzip dests-              (xparams, yparams) = splitAt (length local_dests) $-                                   lambdaParams $ histOp $ slugOp slug--          sComment "Read values from subhistogram 0." $-            forM_ (zip xparams local_dests) $ \(xp, subhisto) ->-            copyDWIMFix-            (paramName xp) []-            (Var subhisto) (0:local_bucket_is)--          sComment "Accumulate based on values in other subhistograms." $-            sFor "subhisto_id" (num_subhistos_per_group - 1) $ \subhisto_id -> do-              forM_ (zip yparams local_dests) $ \(yp, subhisto) ->-                copyDWIMFix-                (paramName yp) []-                (Var subhisto) (subhisto_id + 1 : local_bucket_is)-              compileBody' xparams $ lambdaBody $ histOp $ slugOp slug--          sComment "Put final bucket value in global memory." $ do-            let global_is =-                  map Imp.vi32 segment_is ++-                  [group_id `rem` unCount groups_per_segment] ++-                  global_bucket_is-            forM_ (zip xparams global_dests) $ \(xp, global_dest) ->-              copyDWIMFix global_dest global_is (Var $ paramName xp) []--histKernelLocal :: VName -> Count NumGroups Imp.Exp-                -> [PatElem KernelsMem]-                -> Count NumGroups SubExp -> Count GroupSize SubExp-                -> SegSpace-                -> Imp.Exp-                -> [SegHistSlug]-                -> KernelBody KernelsMem-                -> CallKernelGen ()-histKernelLocal num_subhistos_per_group_var groups_per_segment map_pes num_groups group_size space hist_S slugs kbody = do-  num_groups' <- traverse toExp num_groups-  group_size' <- traverse toExp group_size-  let num_subhistos_per_group = Imp.var num_subhistos_per_group_var int32--  emit $ Imp.DebugPrint "Number of local subhistograms per group" $ Just num_subhistos_per_group--  init_histograms <--    prepareIntermediateArraysLocal num_subhistos_per_group_var groups_per_segment space slugs--  sFor "chk_i" hist_S $ \chk_i ->-    histKernelLocalPass-    num_subhistos_per_group_var groups_per_segment map_pes num_groups' group_size' space slugs kbody-    init_histograms hist_S chk_i---- | The maximum number of passes we are willing to accept for this--- kind of atomic update.-slugMaxLocalMemPasses :: SegHistSlug -> Int-slugMaxLocalMemPasses slug =-  case slugAtomicUpdate slug of-    AtomicPrim _ -> 3-    AtomicCAS _  -> 4-    AtomicLocking _ -> 6--localMemoryCase :: [PatElem KernelsMem]-                -> Imp.Exp-                -> SegSpace-                -> Imp.Exp -> Imp.Exp -> Imp.Exp -> Imp.Exp-                -> [SegHistSlug]-                -> KernelBody KernelsMem-                -> CallKernelGen (Imp.Exp, CallKernelGen ())-localMemoryCase map_pes hist_T space hist_H hist_el_size hist_N _ slugs kbody = do-  let space_sizes = segSpaceDims space-      segment_dims = init space_sizes-      segmented = not $ null segment_dims--  hist_L <- dPrim "hist_L" int32-  sOp $ Imp.GetSizeMax hist_L Imp.SizeLocalMemory--  max_group_size <- dPrim "max_group_size" int32-  sOp $ Imp.GetSizeMax max_group_size Imp.SizeGroup-  let group_size = Imp.Count $ Var max_group_size-  num_groups <- fmap (Imp.Count . Var) $ dPrimV "num_groups" $-                hist_T `divUp` toExp' int32 (unCount group_size)-  let num_groups' = toExp' int32 <$> num_groups-      group_size' = toExp' int32 <$> group_size--  let r64 = ConvOpExp (SIToFP Int32 Float64)-      t64 = ConvOpExp (FPToSI Float64 Int32)--  -- M approximation.-  hist_m' <- dPrimVE "hist_m_prime" $-             r64 (Imp.BinOpExp (SMin Int32)-                  (Imp.vi32 hist_L `quot` hist_el_size)-                  (hist_N `divUp` unCount num_groups'))-             / r64 hist_H--  let hist_B = unCount group_size'--  -- M in the paper, but not adjusted for asymptotic efficiency.-  hist_M0 <- dPrimVE "hist_M0" $-             Imp.BinOpExp (SMax Int32) 1 $-             Imp.BinOpExp (SMin Int32) (t64 hist_m') hist_B--  -- Minimal sequential chunking factor.-  let q_small = 2--  -- The number of segments/histograms produced..-  hist_Nout <- dPrimVE "hist_Nout" $ product $ map (toExp' int32) segment_dims--  hist_Nin <- dPrimVE "hist_Nin" $ toExp' int32 $ last space_sizes--  -- Maximum M for work efficiency.-  work_asymp_M_max <--    if segmented then do--      hist_T_hist_min <- dPrimVE "hist_T_hist_min" $-                         sExt Int32 $-                         Imp.BinOpExp (SMin Int64)-                         (sExt Int64 hist_Nin * sExt Int64 hist_Nout) (sExt Int64 hist_T)-                         `divUp`-                         sExt Int64 hist_Nout--      -- Number of groups, rounded up.-      let r = hist_T_hist_min `divUp` hist_B--      dPrimVE "work_asymp_M_max" $ hist_Nin `quot` (r * hist_H)--    else dPrimVE "work_asymp_M_max" $-         (hist_Nout * hist_N) `quot`-         ((q_small * unCount num_groups' * hist_H)-          `quot` genericLength slugs)--  -- Number of subhistograms per result histogram.-  hist_M <- dPrimV "hist_M" $-            Imp.BinOpExp (SMin Int32) hist_M0 work_asymp_M_max--  -- hist_M may be zero (which we'll check for below), but we need it-  -- for some divisions first, so crudely make a nonzero form.-  let hist_M_nonzero = Imp.BinOpExp (SMax Int32) 1 $ Imp.vi32 hist_M--  -- "Cooperation factor" - the number of threads cooperatively-  -- working on the same (sub)histogram.-  hist_C <- dPrimVE "hist_C" $-            hist_B `divUp` hist_M_nonzero--  emit $ Imp.DebugPrint "local hist_M0" $ Just hist_M0-  emit $ Imp.DebugPrint "local work asymp M max" $ Just work_asymp_M_max-  emit $ Imp.DebugPrint "local C" $ Just hist_C-  emit $ Imp.DebugPrint "local B" $ Just hist_B-  emit $ Imp.DebugPrint "local M" $ Just $ Imp.vi32 hist_M-  emit $ Imp.DebugPrint "local memory needed" $-    Just $ hist_H * hist_el_size * Imp.vi32 hist_M--  -- local_mem_needed is what we need to keep a single bucket in local-  -- memory - this is an absolute minimum.  We can fit anything else-  -- by doing multiple passes, although more than a few is-  -- (heuristically) not efficient.-  local_mem_needed <- dPrimVE "local_mem_needed" $ hist_el_size * Imp.vi32 hist_M-  hist_S <- dPrimVE "hist_S" $ (hist_H * local_mem_needed) `divUp` Imp.vi32 hist_L-  let max_S = case bodyPassage kbody of-                MustBeSinglePass -> 1-                MayBeMultiPass -> fromIntegral $ maxinum $ map slugMaxLocalMemPasses slugs--  -- We only use local memory if the number of updates per histogram-  -- at least matches the histogram size, as otherwise it is not-  -- asymptotically efficient.  This mostly matters for the segmented-  -- case.-  let pick_local =-        hist_Nin .>=. hist_H-        .&&. (local_mem_needed .<=. Imp.vi32 hist_L)-        .&&. (hist_S .<=. max_S)-        .&&. hist_C .<=. hist_B-        .&&. Imp.vi32 hist_M .>. 0--      groups_per_segment-        | segmented = num_groups' `divUp` Imp.Count hist_Nout-        | otherwise = num_groups'--      run = do-        emit $ Imp.DebugPrint "## Using local memory" Nothing-        emit $ Imp.DebugPrint "Histogram size (H)" $ Just hist_H-        emit $ Imp.DebugPrint "Multiplication degree (M)" $ Just $ Imp.vi32 hist_M-        emit $ Imp.DebugPrint "Cooperation level (C)" $ Just hist_C-        emit $ Imp.DebugPrint "Number of chunks (S)" $ Just hist_S-        when segmented $-          emit $ Imp.DebugPrint "Groups per segment" $ Just $ unCount groups_per_segment-        histKernelLocal hist_M groups_per_segment map_pes-          num_groups group_size space hist_S slugs kbody--  return (pick_local, run)---- | Generate code for a segmented histogram called from the host.-compileSegHist :: Pattern KernelsMem-               -> Count NumGroups SubExp -> Count GroupSize SubExp-               -> SegSpace-               -> [HistOp KernelsMem]-               -> KernelBody KernelsMem-               -> CallKernelGen ()-compileSegHist (Pattern _ pes) num_groups group_size space ops kbody = do-  -- Most of this function is not the histogram part itself, but-  -- rather figuring out whether to use a local or global memory-  -- strategy, as well as collapsing the subhistograms produced (which-  -- are always in global memory, but their number may vary).-  num_groups' <- traverse toExp num_groups-  group_size' <- traverse toExp group_size--  dims <- mapM toExp $ segSpaceDims space--  let num_red_res = length ops + sum (map (length . histNeutral) ops)-      (all_red_pes, map_pes) = splitAt num_red_res pes-      segment_size = last dims--  (op_hs, op_seg_hs, slugs) <- unzip3 <$> mapM (computeHistoUsage space) ops-  h <- dPrimVE "h" $ Imp.unCount $ sum op_hs-  seg_h <- dPrimVE "seg_h" $ Imp.unCount $ sum op_seg_hs--  -- Check for emptyness to avoid division-by-zero.-  sUnless (seg_h .==. 0) $ do--    -- Maximum group size (or actual, in this case).-    let hist_B = unCount group_size'--    -- Size of a histogram.-    hist_H <- dPrimVE "hist_H" $ sum $ map (toExp' int32 . histWidth) ops--    -- Size of a single histogram element.  Actually the weighted-    -- average of histogram elements in cases where we have more than-    -- one histogram operation, plus any locks.-    let lockSize slug = case slugAtomicUpdate slug of-                          AtomicLocking{} -> Just $ primByteSize int32-                          _               -> Nothing-    hist_el_size <- dPrimVE "hist_el_size" $ foldl' (+) (h `divUp` hist_H) $-                    mapMaybe lockSize slugs--    -- Input elements contributing to each histogram.-    hist_N <- dPrimVE "hist_N" segment_size--    -- Compute RF as the average RF over all the histograms.-    hist_RF <- dPrimVE "hist_RF" $-               sum (map (toExp' int32. histRaceFactor . slugOp) slugs)-               `quot`-               genericLength slugs--    let hist_T = unCount num_groups' * unCount group_size'-    emit $ Imp.DebugPrint "\n# SegHist" Nothing-    emit $ Imp.DebugPrint "Number of threads (T)" $ Just hist_T-    emit $ Imp.DebugPrint "Desired group size (B)" $ Just hist_B-    emit $ Imp.DebugPrint "Histogram size (H)" $ Just hist_H-    emit $ Imp.DebugPrint "Input elements per histogram (N)" $ Just hist_N-    emit $ Imp.DebugPrint "Number of segments" $-      Just $ product $ map (toExp' int32 . snd) segment_dims-    emit $ Imp.DebugPrint "Histogram element size (el_size)" $ Just hist_el_size-    emit $ Imp.DebugPrint "Race factor (RF)" $ Just hist_RF-    emit $ Imp.DebugPrint "Memory per set of subhistograms per segment" $ Just h-    emit $ Imp.DebugPrint "Memory per set of subhistograms times segments" $ Just seg_h--    (use_local_memory, run_in_local_memory) <--      localMemoryCase map_pes hist_T space hist_H hist_el_size hist_N hist_RF slugs kbody--    sIf use_local_memory run_in_local_memory $-      histKernelGlobal map_pes num_groups group_size space slugs kbody--    let pes_per_op = chunks (map (length . histDest) ops) all_red_pes--    forM_ (zip3 slugs pes_per_op ops) $ \(slug, red_pes, op) -> do-      let num_histos = slugNumSubhistos slug-          subhistos = map subhistosArray $ slugSubhistos slug--      let unitHistoCase =-            -- This is OK because the memory blocks are at least as-            -- large as the ones we are supposed to use for the result.-            forM_ (zip red_pes subhistos) $ \(pe, subhisto) -> do-              pe_mem <- memLocationName . entryArrayLocation <$>-                        lookupArray (patElemName pe)-              subhisto_mem <- memLocationName . entryArrayLocation <$>-                              lookupArray subhisto-              emit $ Imp.SetMem pe_mem subhisto_mem $ Space "device"--      sIf (Imp.var num_histos int32 .==. 1) unitHistoCase $ do-        -- For the segmented reduction, we keep the segment dimensions-        -- unchanged.  To this, we add two dimensions: one over the number-        -- of buckets, and one over the number of subhistograms.  This-        -- inner dimension is the one that is collapsed in the reduction.-        let num_buckets = histWidth op--        bucket_id <- newVName "bucket_id"-        subhistogram_id <- newVName "subhistogram_id"-        vector_ids <- mapM (const $ newVName "vector_id") $-                      shapeDims $ histShape op--        flat_gtid <- newVName "flat_gtid"--        let lvl = SegThread num_groups group_size SegVirt-            segred_space =-              SegSpace flat_gtid $-              segment_dims ++-              [(bucket_id, num_buckets)] ++-              zip vector_ids (shapeDims $ histShape op) ++-              [(subhistogram_id, Var num_histos)]--        let segred_op = SegBinOp Commutative (histOp op) (histNeutral op) mempty-        compileSegRed' (Pattern [] red_pes) lvl segred_space [segred_op] $ \red_cont ->-          red_cont $ flip map subhistos $ \subhisto ->-            (Var subhisto, map Imp.vi32 $-              map fst segment_dims ++ [subhistogram_id, bucket_id] ++ vector_ids)--  where segment_dims = init $ unSegSpace space+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}++-- | Our compilation strategy for 'SegHist' is based around avoiding+-- bin conflicts.  We do this by splitting the input into chunks, and+-- for each chunk computing a single subhistogram.  Then we combine+-- the subhistograms using an ordinary segmented reduction ('SegRed').+--+-- There are some branches around to efficiently handle the case where+-- we use only a single subhistogram (because it's large), so that we+-- respect the asymptotics, and do not copy the destination array.+--+-- We also use a heuristic strategy for computing subhistograms in+-- local memory when possible.  Given:+--+-- H: total size of histograms in bytes, including any lock arrays.+--+-- G: group size+--+-- T: number of bytes of local memory each thread can be given without+-- impacting occupancy (determined experimentally, e.g. 32).+--+-- LMAX: maximum amount of local memory per workgroup (hard limit).+--+-- We wish to compute:+--+-- COOP: cooperation level (number of threads per subhistogram)+--+-- LH: number of local memory subhistograms+--+-- We do this as:+--+-- COOP = ceil(H / T)+-- LH = ceil((G*T)/H)+-- if COOP <= G && H <= LMAX then+--   use local memory+-- else+--   use global memory+module Futhark.CodeGen.ImpGen.Kernels.SegHist (compileSegHist) where++import Control.Monad.Except+import Data.List (foldl', genericLength, zip4, zip6)+import Data.Maybe+import qualified Futhark.CodeGen.ImpCode.Kernels as Imp+import Futhark.CodeGen.ImpGen+import Futhark.CodeGen.ImpGen.Kernels.Base+import Futhark.CodeGen.ImpGen.Kernels.SegRed (compileSegRed')+import Futhark.Construct (fullSliceNum)+import Futhark.IR.KernelsMem+import qualified Futhark.IR.Mem.IxFun as IxFun+import Futhark.MonadFreshNames+import Futhark.Pass.ExplicitAllocations ()+import Futhark.Util (chunks, mapAccumLM, maxinum, splitFromEnd, takeLast)+import Futhark.Util.IntegralExp (divUp, quot, rem)+import Prelude hiding (quot, rem)++data SubhistosInfo = SubhistosInfo+  { subhistosArray :: VName,+    subhistosAlloc :: CallKernelGen ()+  }++data SegHistSlug = SegHistSlug+  { slugOp :: HistOp KernelsMem,+    slugNumSubhistos :: TV Int64,+    slugSubhistos :: [SubhistosInfo],+    slugAtomicUpdate :: AtomicUpdate KernelsMem KernelEnv+  }++histoSpaceUsage ::+  HistOp KernelsMem ->+  Imp.Count Imp.Bytes (Imp.TExp Int64)+histoSpaceUsage op =+  sum $+    map+      ( typeSize+          . (`arrayOfRow` histWidth op)+          . (`arrayOfShape` histShape op)+      )+      $ lambdaReturnType $ histOp op++-- | Figure out how much memory is needed per histogram, both+-- segmented and unsegmented,, and compute some other auxiliary+-- information.+computeHistoUsage ::+  SegSpace ->+  HistOp KernelsMem ->+  CallKernelGen+    ( Imp.Count Imp.Bytes (Imp.TExp Int64),+      Imp.Count Imp.Bytes (Imp.TExp Int64),+      SegHistSlug+    )+computeHistoUsage space op = do+  let segment_dims = init $ unSegSpace space+      num_segments = length segment_dims++  -- Create names for the intermediate array memory blocks,+  -- memory block sizes, arrays, and number of subhistograms.+  num_subhistos <- dPrim "num_subhistos" int32+  subhisto_infos <- forM (zip (histDest op) (histNeutral op)) $ \(dest, ne) -> do+    dest_t <- lookupType dest+    dest_mem <- entryArrayLocation <$> lookupArray dest++    subhistos_mem <-+      sDeclareMem (baseString dest ++ "_subhistos_mem") (Space "device")++    let subhistos_shape =+          Shape (map snd segment_dims ++ [tvSize num_subhistos])+            <> stripDims num_segments (arrayShape dest_t)+        subhistos_membind =+          ArrayIn subhistos_mem $+            IxFun.iota $+              map pe64 $ shapeDims subhistos_shape+    subhistos <-+      sArray+        (baseString dest ++ "_subhistos")+        (elemType dest_t)+        subhistos_shape+        subhistos_membind++    return $+      SubhistosInfo subhistos $ do+        let unitHistoCase =+              emit $+                Imp.SetMem subhistos_mem (memLocationName dest_mem) $+                  Space "device"++            multiHistoCase = do+              let num_elems =+                    foldl' (*) (sExt64 $ tvExp num_subhistos) $+                      map toInt64Exp $ arrayDims dest_t++              let subhistos_mem_size =+                    Imp.bytes $+                      Imp.unCount (Imp.elements num_elems `Imp.withElemType` elemType dest_t)++              sAlloc_ subhistos_mem subhistos_mem_size $ Space "device"+              sReplicate subhistos ne+              subhistos_t <- lookupType subhistos+              let slice =+                    fullSliceNum (map toInt64Exp $ arrayDims subhistos_t) $+                      map (unitSlice 0 . toInt64Exp . snd) segment_dims+                        ++ [DimFix 0]+              sUpdate subhistos slice $ Var dest++        sIf (tvExp num_subhistos .==. 1) unitHistoCase multiHistoCase++  let h = histoSpaceUsage op+      segmented_h = h * product (map (Imp.bytes . toInt64Exp) $ init $ segSpaceDims space)++  atomics <- hostAtomics <$> askEnv++  return+    ( h,+      segmented_h,+      SegHistSlug op num_subhistos subhisto_infos $+        atomicUpdateLocking atomics $ histOp op+    )++prepareAtomicUpdateGlobal ::+  Maybe Locking ->+  [VName] ->+  SegHistSlug ->+  CallKernelGen+    ( Maybe Locking,+      [Imp.TExp Int64] -> InKernelGen ()+    )+prepareAtomicUpdateGlobal l dests slug =+  -- We need a separate lock array if the operators are not all of a+  -- particularly simple form that permits pure atomic operations.+  case (l, slugAtomicUpdate slug) of+    (_, AtomicPrim f) -> return (l, f (Space "global") dests)+    (_, AtomicCAS f) -> return (l, f (Space "global") dests)+    (Just l', AtomicLocking f) -> return (l, f l' (Space "global") dests)+    (Nothing, AtomicLocking f) -> do+      -- The number of locks used here is too low, but since we are+      -- currently forced to inline a huge list, I'm keeping it down+      -- for now.  Some quick experiments suggested that it has little+      -- impact anyway (maybe the locking case is just too slow).+      --+      -- A fun solution would also be to use a simple hashing+      -- algorithm to ensure good distribution of locks.+      let num_locks = 100151+          dims =+            map toInt64Exp $+              shapeDims (histShape (slugOp slug))+                ++ [ tvSize (slugNumSubhistos slug),+                     histWidth (slugOp slug)+                   ]+      locks <-+        sStaticArray "hist_locks" (Space "device") int32 $+          Imp.ArrayZeros num_locks+      let l' = Locking locks 0 1 0 (pure . (`rem` fromIntegral num_locks) . flattenIndex dims)+      return (Just l', f l' (Space "global") dests)++-- | Some kernel bodies are not safe (or efficient) to execute+-- multiple times.+data Passage = MustBeSinglePass | MayBeMultiPass deriving (Eq, Ord)++bodyPassage :: KernelBody KernelsMem -> Passage+bodyPassage kbody+  | mempty == consumedInKernelBody (aliasAnalyseKernelBody kbody) =+    MayBeMultiPass+  | otherwise =+    MustBeSinglePass++prepareIntermediateArraysGlobal ::+  Passage ->+  Imp.TExp Int32 ->+  Imp.TExp Int64 ->+  [SegHistSlug] ->+  CallKernelGen+    ( Imp.TExp Int32,+      [[Imp.TExp Int64] -> InKernelGen ()]+    )+prepareIntermediateArraysGlobal passage hist_T hist_N slugs = do+  -- The paper formulae assume there is only one histogram, but in our+  -- implementation there can be multiple that have been horisontally+  -- fused.  We do a bit of trickery with summings and averages to+  -- pretend there is really only one.  For the case of a single+  -- histogram, the actual calculations should be the same as in the+  -- paper.++  -- The sum of all Hs.+  hist_H <- dPrimVE "hist_H" $ sum $ map (toInt64Exp . histWidth . slugOp) slugs++  hist_RF <-+    dPrimVE "hist_RF" $+      sum (map (r64 . toInt64Exp . histRaceFactor . slugOp) slugs)+        / genericLength slugs++  hist_el_size <- dPrimVE "hist_el_size" $ sum $ map slugElAvgSize slugs++  hist_C_max <-+    dPrimVE "hist_C_max" $+      fMin64 (r64 hist_T) $ r64 hist_H / hist_k_ct_min++  hist_M_min <-+    dPrimVE "hist_M_min" $+      sMax32 1 $ sExt32 $ t64 $ r64 hist_T / hist_C_max++  -- Querying L2 cache size is not reliable.  Instead we provide a+  -- tunable knob with a hopefully sane default.+  let hist_L2_def = 4 * 1024 * 1024+  hist_L2 <- dPrim "L2_size" int32+  entry <- askFunction+  -- Equivalent to F_L2*L2 in paper.+  sOp $+    Imp.GetSize+      (tvVar hist_L2)+      (keyWithEntryPoint entry $ nameFromString (pretty (tvVar hist_L2)))+      $ Imp.SizeBespoke (nameFromString "L2_for_histogram") hist_L2_def++  let hist_L2_ln_sz = 16 * 4 -- L2 cache line size approximation+  hist_RACE_exp <-+    dPrimVE "hist_RACE_exp" $+      fMax64 1 $+        (hist_k_RF * hist_RF)+          / (hist_L2_ln_sz / r64 hist_el_size)++  hist_S <- dPrim "hist_S" int32++  -- For sparse histograms (H exceeds N) we only want a single chunk.+  sIf+    (hist_N .<. hist_H)+    (hist_S <-- (1 :: Imp.TExp Int32))+    $ hist_S+      <-- case passage of+        MayBeMultiPass ->+          sExt32 $+            (sExt64 hist_M_min * hist_H * sExt64 hist_el_size)+              `divUp` t64 (hist_F_L2 * r64 (tvExp hist_L2) * hist_RACE_exp)+        MustBeSinglePass ->+          1++  emit $ Imp.DebugPrint "Race expansion factor (RACE^exp)" $ Just $ untyped hist_RACE_exp+  emit $ Imp.DebugPrint "Number of chunks (S)" $ Just $ untyped $ tvExp hist_S++  histograms <-+    snd+      <$> mapAccumLM+        (onOp (tvExp hist_L2) hist_M_min (tvExp hist_S) hist_RACE_exp)+        Nothing+        slugs++  return (tvExp hist_S, histograms)+  where+    hist_k_ct_min = 2 -- Chosen experimentally+    hist_k_RF = 0.75 -- Chosen experimentally+    hist_F_L2 = 0.4 -- Chosen experimentally+    r64 = isF64 . ConvOpExp (SIToFP Int32 Float64) . untyped+    t64 = isInt64 . ConvOpExp (FPToSI Float64 Int64) . untyped++    -- "Average element size" as computed by a formula that also takes+    -- locking into account.+    slugElAvgSize slug@(SegHistSlug op _ _ do_op) =+      case do_op of+        AtomicLocking {} ->+          slugElSize slug `quot` (1 + genericLength (lambdaReturnType (histOp op)))+        _ ->+          slugElSize slug `quot` genericLength (lambdaReturnType (histOp op))++    -- "Average element size" as computed by a formula that also takes+    -- locking into account.+    slugElSize (SegHistSlug op _ _ do_op) =+      case do_op of+        AtomicLocking {} ->+          sExt32 $+            unCount $+              sum $+                map (typeSize . (`arrayOfShape` histShape op)) $+                  Prim int32 : lambdaReturnType (histOp op)+        _ ->+          sExt32 $+            unCount $+              sum $+                map (typeSize . (`arrayOfShape` histShape op)) $+                  lambdaReturnType (histOp op)++    onOp hist_L2 hist_M_min hist_S hist_RACE_exp l slug = do+      let SegHistSlug op num_subhistos subhisto_info do_op = slug+          hist_H = toInt64Exp $ histWidth op++      hist_H_chk <- dPrimVE "hist_H_chk" $ hist_H `divUp` sExt64 hist_S++      emit $ Imp.DebugPrint "Chunk size (H_chk)" $ Just $ untyped hist_H_chk++      hist_k_max <-+        dPrimVE "hist_k_max" $+          fMin64+            (hist_F_L2 * (r64 hist_L2 / r64 (slugElSize slug)) * hist_RACE_exp)+            (r64 hist_N)+            / r64 hist_T++      hist_u <- dPrimVE "hist_u" $+        case do_op of+          AtomicPrim {} -> 2+          _ -> 1++      hist_C <-+        dPrimVE "hist_C" $+          fMin64 (r64 hist_T) $ r64 (hist_u * hist_H_chk) / hist_k_max++      -- Number of subhistograms per result histogram.+      hist_M <- dPrimVE "hist_M" $+        case slugAtomicUpdate slug of+          AtomicPrim {} -> 1+          _ -> sMax32 hist_M_min $ sExt32 $ t64 $ r64 hist_T / hist_C++      emit $ Imp.DebugPrint "Elements/thread in L2 cache (k_max)" $ Just $ untyped hist_k_max+      emit $ Imp.DebugPrint "Multiplication degree (M)" $ Just $ untyped hist_M+      emit $ Imp.DebugPrint "Cooperation level (C)" $ Just $ untyped hist_C++      -- num_subhistos is the variable we use to communicate back.+      num_subhistos <-- sExt64 hist_M++      -- Initialise sub-histograms.+      --+      -- If hist_M is 1, then we just reuse the original+      -- destination.  The idea is to avoid a copy if we are writing a+      -- small number of values into a very large prior histogram.+      dests <- forM (zip (histDest op) subhisto_info) $ \(dest, info) -> do+        dest_mem <- entryArrayLocation <$> lookupArray dest++        sub_mem <-+          fmap memLocationName $+            entryArrayLocation+              <$> lookupArray (subhistosArray info)++        let unitHistoCase =+              emit $+                Imp.SetMem sub_mem (memLocationName dest_mem) $+                  Space "device"++            multiHistoCase = subhistosAlloc info++        sIf (hist_M .==. 1) unitHistoCase multiHistoCase++        return $ subhistosArray info++      (l', do_op') <- prepareAtomicUpdateGlobal l dests slug++      return (l', do_op')++histKernelGlobalPass ::+  [PatElem KernelsMem] ->+  Count NumGroups (Imp.TExp Int64) ->+  Count GroupSize (Imp.TExp Int64) ->+  SegSpace ->+  [SegHistSlug] ->+  KernelBody KernelsMem ->+  [[Imp.TExp Int64] -> InKernelGen ()] ->+  Imp.TExp Int32 ->+  Imp.TExp Int32 ->+  CallKernelGen ()+histKernelGlobalPass map_pes num_groups group_size space slugs kbody histograms hist_S chk_i = do+  let (space_is, space_sizes) = unzip $ unSegSpace space+      space_sizes_64 = map (sExt64 . toInt64Exp) space_sizes+      total_w_64 = product space_sizes_64++  hist_H_chks <- forM (map (histWidth . slugOp) slugs) $ \w ->+    dPrimVE "hist_H_chk" $ toInt64Exp w `divUp` sExt64 hist_S++  sKernelThread "seghist_global" num_groups group_size (segFlat space) $ do+    constants <- kernelConstants <$> askEnv++    -- Compute subhistogram index for each thread, per histogram.+    subhisto_inds <- forM slugs $ \slug ->+      dPrimVE "subhisto_ind" $+        kernelGlobalThreadId constants+          `quot` ( kernelNumThreads constants+                     `divUp` sExt32 (tvExp (slugNumSubhistos slug))+                 )++    -- Loop over flat offsets into the input and output.  The+    -- calculation is done with 64-bit integers to avoid overflow,+    -- but the final unflattened segment indexes are 32 bit.+    let gtid = sExt64 $ kernelGlobalThreadId constants+        num_threads = sExt64 $ kernelNumThreads constants+    kernelLoop gtid num_threads total_w_64 $ \offset -> do+      -- Construct segment indices.+      zipWithM_ dPrimV_ space_is $+        map sExt32 $ unflattenIndex space_sizes_64 offset++      -- We execute the bucket function once and update each histogram serially.+      -- We apply the bucket function if j=offset+ltid is less than+      -- num_elements.  This also involves writing to the mapout+      -- arrays.+      let input_in_bounds = offset .<. total_w_64++      sWhen input_in_bounds $+        compileStms mempty (kernelBodyStms kbody) $ do+          let (red_res, map_res) = splitFromEnd (length map_pes) $ kernelBodyResult kbody++          sComment "save map-out results" $+            forM_ (zip map_pes map_res) $ \(pe, res) ->+              copyDWIMFix+                (patElemName pe)+                (map (Imp.vi64 . fst) $ unSegSpace space)+                (kernelResultSubExp res)+                []++          let (buckets, vs) = splitAt (length slugs) red_res+              perOp = chunks $ map (length . histDest . slugOp) slugs++          sComment "perform atomic updates" $+            forM_ (zip6 (map slugOp slugs) histograms buckets (perOp vs) subhisto_inds hist_H_chks) $+              \( HistOp dest_w _ _ _ shape lam,+                 do_op,+                 bucket,+                 vs',+                 subhisto_ind,+                 hist_H_chk+                 ) -> do+                  let chk_beg = sExt64 chk_i * hist_H_chk+                      bucket' = toInt64Exp $ kernelResultSubExp bucket+                      dest_w' = toInt64Exp dest_w+                      bucket_in_bounds =+                        chk_beg .<=. bucket'+                          .&&. bucket' .<. (chk_beg + hist_H_chk)+                          .&&. bucket' .<. dest_w'+                      vs_params = takeLast (length vs') $ lambdaParams lam++                  sWhen bucket_in_bounds $ do+                    let bucket_is =+                          map Imp.vi64 (init space_is)+                            ++ [sExt64 subhisto_ind, bucket']+                    dLParams $ lambdaParams lam+                    sLoopNest shape $ \is -> do+                      forM_ (zip vs_params vs') $ \(p, res) ->+                        copyDWIMFix (paramName p) [] (kernelResultSubExp res) is+                      do_op (bucket_is ++ is)++histKernelGlobal ::+  [PatElem KernelsMem] ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  SegSpace ->+  [SegHistSlug] ->+  KernelBody KernelsMem ->+  CallKernelGen ()+histKernelGlobal map_pes num_groups group_size space slugs kbody = do+  let num_groups' = fmap toInt64Exp num_groups+      group_size' = fmap toInt64Exp group_size+  let (_space_is, space_sizes) = unzip $ unSegSpace space+      num_threads = sExt32 $ unCount num_groups' * unCount group_size'++  emit $ Imp.DebugPrint "## Using global memory" Nothing++  (hist_S, histograms) <-+    prepareIntermediateArraysGlobal+      (bodyPassage kbody)+      num_threads+      (toInt64Exp $ last space_sizes)+      slugs++  sFor "chk_i" hist_S $ \chk_i ->+    histKernelGlobalPass+      map_pes+      num_groups'+      group_size'+      space+      slugs+      kbody+      histograms+      hist_S+      chk_i++type InitLocalHistograms =+  [ ( [VName],+      SubExp ->+      InKernelGen+        ( [VName],+          [Imp.TExp Int64] -> InKernelGen ()+        )+    )+  ]++prepareIntermediateArraysLocal ::+  TV Int32 ->+  Count NumGroups (Imp.TExp Int64) ->+  SegSpace ->+  [SegHistSlug] ->+  CallKernelGen InitLocalHistograms+prepareIntermediateArraysLocal num_subhistos_per_group groups_per_segment space slugs = do+  num_segments <-+    dPrimVE "num_segments" $+      product $ map (toInt64Exp . snd) $ init $ unSegSpace space+  mapM (onOp num_segments) slugs+  where+    onOp num_segments (SegHistSlug op num_subhistos subhisto_info do_op) = do+      num_subhistos <-- sExt64 (unCount groups_per_segment) * num_segments++      emit $+        Imp.DebugPrint "Number of subhistograms in global memory" $+          Just $ untyped $ tvExp num_subhistos++      mk_op <-+        case do_op of+          AtomicPrim f -> return $ const $ return f+          AtomicCAS f -> return $ const $ return f+          AtomicLocking f -> return $ \hist_H_chk -> do+            let lock_shape =+                  Shape $+                    tvSize num_subhistos_per_group :+                    shapeDims (histShape op)+                      ++ [hist_H_chk]++            let dims = map toInt64Exp $ shapeDims lock_shape++            locks <- sAllocArray "locks" int32 lock_shape $ Space "local"++            sComment "All locks start out unlocked" $+              groupCoverSpace dims $ \is ->+                copyDWIMFix locks is (intConst Int32 0) []++            return $ f $ Locking locks 0 1 0 id++      -- Initialise local-memory sub-histograms.  These are+      -- represented as two-dimensional arrays.+      let init_local_subhistos hist_H_chk = do+            local_subhistos <-+              forM (histType op) $ \t -> do+                let sub_local_shape =+                      Shape [tvSize num_subhistos_per_group]+                        <> (arrayShape t `setOuterDim` hist_H_chk)+                sAllocArray+                  "subhistogram_local"+                  (elemType t)+                  sub_local_shape+                  (Space "local")++            do_op' <- mk_op hist_H_chk++            return (local_subhistos, do_op' (Space "local") local_subhistos)++      -- Initialise global-memory sub-histograms.+      glob_subhistos <- forM subhisto_info $ \info -> do+        subhistosAlloc info+        return $ subhistosArray info++      return (glob_subhistos, init_local_subhistos)++histKernelLocalPass ::+  TV Int32 ->+  Count NumGroups (Imp.TExp Int64) ->+  [PatElem KernelsMem] ->+  Count NumGroups (Imp.TExp Int64) ->+  Count GroupSize (Imp.TExp Int64) ->+  SegSpace ->+  [SegHistSlug] ->+  KernelBody KernelsMem ->+  InitLocalHistograms ->+  Imp.TExp Int32 ->+  Imp.TExp Int32 ->+  CallKernelGen ()+histKernelLocalPass+  num_subhistos_per_group_var+  groups_per_segment+  map_pes+  num_groups+  group_size+  space+  slugs+  kbody+  init_histograms+  hist_S+  chk_i = do+    let (space_is, space_sizes) = unzip $ unSegSpace space+        segment_is = init space_is+        segment_dims = init space_sizes+        (i_in_segment, segment_size) = last $ unSegSpace space+        num_subhistos_per_group = tvExp num_subhistos_per_group_var+        segment_size' = toInt64Exp segment_size++    num_segments <-+      dPrimVE "num_segments" $+        product $ map toInt64Exp segment_dims++    hist_H_chks <- forM (map (histWidth . slugOp) slugs) $ \w ->+      dPrimV "hist_H_chk" $ toInt64Exp w `divUp` sExt64 hist_S++    sKernelThread "seghist_local" num_groups group_size (segFlat space) $+      virtualiseGroups SegVirt (sExt32 $ unCount groups_per_segment * num_segments) $ \group_id -> do+        constants <- kernelConstants <$> askEnv++        flat_segment_id <- dPrimVE "flat_segment_id" $ group_id `quot` sExt32 (unCount groups_per_segment)+        gid_in_segment <- dPrimVE "gid_in_segment" $ group_id `rem` sExt32 (unCount groups_per_segment)+        -- This pgtid is kind of a "virtualised physical" gtid - not the+        -- same thing as the gtid used for the SegHist itself.+        pgtid_in_segment <-+          dPrimVE "pgtid_in_segment" $+            gid_in_segment * sExt32 (kernelGroupSize constants)+              + kernelLocalThreadId constants+        threads_per_segment <-+          dPrimVE "threads_per_segment" $+            sExt32 $ unCount groups_per_segment * kernelGroupSize constants++        -- Set segment indices.+        zipWithM_ dPrimV_ segment_is $+          unflattenIndex (map toInt64Exp segment_dims) $ sExt64 flat_segment_id++        histograms <- forM (zip init_histograms hist_H_chks) $+          \((glob_subhistos, init_local_subhistos), hist_H_chk) -> do+            (local_subhistos, do_op) <- init_local_subhistos $ Var $ tvVar hist_H_chk+            return (zip glob_subhistos local_subhistos, hist_H_chk, do_op)++        -- Find index of local subhistograms updated by this thread.  We+        -- try to ensure, as much as possible, that threads in the same+        -- warp use different subhistograms, to avoid conflicts.+        thread_local_subhisto_i <-+          dPrimVE "thread_local_subhisto_i" $+            kernelLocalThreadId constants `rem` num_subhistos_per_group++        let onSlugs f = forM_ (zip slugs histograms) $ \(slug, (dests, hist_H_chk, _)) -> do+              let histo_dims =+                    tvExp hist_H_chk :+                    map toInt64Exp (shapeDims (histShape (slugOp slug)))+              histo_size <- dPrimVE "histo_size" $ product histo_dims+              f slug dests (tvExp hist_H_chk) histo_dims histo_size++        let onAllHistograms f =+              onSlugs $ \slug dests hist_H_chk histo_dims histo_size -> do+                let group_hists_size = num_subhistos_per_group * sExt32 histo_size+                init_per_thread <-+                  dPrimVE "init_per_thread" $+                    group_hists_size+                      `divUp` sExt32 (kernelGroupSize constants)++                forM_ (zip dests (histNeutral $ slugOp slug)) $+                  \((dest_global, dest_local), ne) ->+                    sFor "local_i" init_per_thread $ \i -> do+                      j <-+                        dPrimVE "j" $+                          i * sExt32 (kernelGroupSize constants)+                            + kernelLocalThreadId constants+                      j_offset <-+                        dPrimVE "j_offset" $+                          num_subhistos_per_group * sExt32 histo_size * gid_in_segment + j++                      local_subhisto_i <- dPrimVE "local_subhisto_i" $ j `quot` sExt32 histo_size+                      let local_bucket_is = unflattenIndex histo_dims $ sExt64 $ j `rem` sExt32 histo_size+                          global_bucket_is =+                            head local_bucket_is + sExt64 chk_i * hist_H_chk :+                            tail local_bucket_is+                      global_subhisto_i <- dPrimVE "global_subhisto_i" $ j_offset `quot` sExt32 histo_size++                      sWhen (j .<. group_hists_size) $+                        f+                          dest_local+                          dest_global+                          (slugOp slug)+                          ne+                          local_subhisto_i+                          global_subhisto_i+                          local_bucket_is+                          global_bucket_is++        sComment "initialize histograms in local memory" $+          onAllHistograms $ \dest_local dest_global op ne local_subhisto_i global_subhisto_i local_bucket_is global_bucket_is ->+            sComment "First subhistogram is initialised from global memory; others with neutral element." $ do+              let global_is = map Imp.vi64 segment_is ++ [0] ++ global_bucket_is+                  local_is = sExt64 local_subhisto_i : local_bucket_is+              sIf+                (global_subhisto_i .==. 0)+                (copyDWIMFix dest_local local_is (Var dest_global) global_is)+                ( sLoopNest (histShape op) $ \is ->+                    copyDWIMFix dest_local (local_is ++ is) ne []+                )++        sOp $ Imp.Barrier Imp.FenceLocal++        kernelLoop pgtid_in_segment threads_per_segment (sExt32 segment_size') $ \ie -> do+          dPrimV_ i_in_segment ie++          -- We execute the bucket function once and update each histogram+          -- serially.  This also involves writing to the mapout arrays if+          -- this is the first chunk.++          compileStms mempty (kernelBodyStms kbody) $ do+            let (red_res, map_res) =+                  splitFromEnd (length map_pes) $+                    map kernelResultSubExp $ kernelBodyResult kbody+                (buckets, vs) = splitAt (length slugs) red_res+                perOp = chunks $ map (length . histDest . slugOp) slugs++            sWhen (chk_i .==. 0) $+              sComment "save map-out results" $+                forM_ (zip map_pes map_res) $ \(pe, se) ->+                  copyDWIMFix+                    (patElemName pe)+                    (map Imp.vi64 space_is)+                    se+                    []++            forM_ (zip4 (map slugOp slugs) histograms buckets (perOp vs)) $+              \( HistOp dest_w _ _ _ shape lam,+                 (_, hist_H_chk, do_op),+                 bucket,+                 vs'+                 ) -> do+                  let chk_beg = sExt64 chk_i * tvExp hist_H_chk+                      bucket' = toInt64Exp bucket+                      dest_w' = toInt64Exp dest_w+                      bucket_in_bounds =+                        bucket' .<. dest_w'+                          .&&. chk_beg .<=. bucket'+                          .&&. bucket' .<. (chk_beg + tvExp hist_H_chk)+                      bucket_is = [sExt64 thread_local_subhisto_i, bucket' - chk_beg]+                      vs_params = takeLast (length vs') $ lambdaParams lam++                  sComment "perform atomic updates" $+                    sWhen bucket_in_bounds $ do+                      dLParams $ lambdaParams lam+                      sLoopNest shape $ \is -> do+                        forM_ (zip vs_params vs') $ \(p, v) ->+                          copyDWIMFix (paramName p) [] v is+                        do_op (bucket_is ++ is)++        sOp $ Imp.ErrorSync Imp.FenceGlobal++        sComment "Compact the multiple local memory subhistograms to result in global memory" $+          onSlugs $ \slug dests hist_H_chk histo_dims histo_size -> do+            bins_per_thread <-+              dPrimVE "init_per_thread" $+                histo_size `divUp` sExt64 (kernelGroupSize constants)++            trunc_H <-+              dPrimV "trunc_H" $+                sMin64 hist_H_chk $+                  toInt64Exp (histWidth (slugOp slug))+                    - sExt64 chk_i * head histo_dims+            let trunc_histo_dims =+                  tvExp trunc_H :+                  map toInt64Exp (shapeDims (histShape (slugOp slug)))+            trunc_histo_size <- dPrimVE "histo_size" $ product trunc_histo_dims++            sFor "local_i" bins_per_thread $ \i -> do+              j <-+                dPrimVE "j" $+                  i * sExt64 (kernelGroupSize constants)+                    + sExt64 (kernelLocalThreadId constants)+              sWhen (j .<. trunc_histo_size) $ do+                -- We are responsible for compacting the flat bin 'j', which+                -- we immediately unflatten.+                let local_bucket_is = unflattenIndex histo_dims j+                    global_bucket_is =+                      head local_bucket_is + sExt64 chk_i * hist_H_chk :+                      tail local_bucket_is+                dLParams $ lambdaParams $ histOp $ slugOp slug+                let (global_dests, local_dests) = unzip dests+                    (xparams, yparams) =+                      splitAt (length local_dests) $+                        lambdaParams $ histOp $ slugOp slug++                sComment "Read values from subhistogram 0." $+                  forM_ (zip xparams local_dests) $ \(xp, subhisto) ->+                    copyDWIMFix+                      (paramName xp)+                      []+                      (Var subhisto)+                      (0 : local_bucket_is)++                sComment "Accumulate based on values in other subhistograms." $+                  sFor "subhisto_id" (num_subhistos_per_group - 1) $ \subhisto_id -> do+                    forM_ (zip yparams local_dests) $ \(yp, subhisto) ->+                      copyDWIMFix+                        (paramName yp)+                        []+                        (Var subhisto)+                        (sExt64 subhisto_id + 1 : local_bucket_is)+                    compileBody' xparams $ lambdaBody $ histOp $ slugOp slug++                sComment "Put final bucket value in global memory." $ do+                  let global_is =+                        map Imp.vi64 segment_is+                          ++ [sExt64 group_id `rem` unCount groups_per_segment]+                          ++ global_bucket_is+                  forM_ (zip xparams global_dests) $ \(xp, global_dest) ->+                    copyDWIMFix global_dest global_is (Var $ paramName xp) []++histKernelLocal ::+  TV Int32 ->+  Count NumGroups (Imp.TExp Int64) ->+  [PatElem KernelsMem] ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  SegSpace ->+  Imp.TExp Int32 ->+  [SegHistSlug] ->+  KernelBody KernelsMem ->+  CallKernelGen ()+histKernelLocal num_subhistos_per_group_var groups_per_segment map_pes num_groups group_size space hist_S slugs kbody = do+  let num_groups' = fmap toInt64Exp num_groups+      group_size' = fmap toInt64Exp group_size+      num_subhistos_per_group = tvExp num_subhistos_per_group_var++  emit $+    Imp.DebugPrint "Number of local subhistograms per group" $+      Just $ untyped num_subhistos_per_group++  init_histograms <-+    prepareIntermediateArraysLocal num_subhistos_per_group_var groups_per_segment space slugs++  sFor "chk_i" hist_S $ \chk_i ->+    histKernelLocalPass+      num_subhistos_per_group_var+      groups_per_segment+      map_pes+      num_groups'+      group_size'+      space+      slugs+      kbody+      init_histograms+      hist_S+      chk_i++-- | The maximum number of passes we are willing to accept for this+-- kind of atomic update.+slugMaxLocalMemPasses :: SegHistSlug -> Int+slugMaxLocalMemPasses slug =+  case slugAtomicUpdate slug of+    AtomicPrim _ -> 3+    AtomicCAS _ -> 4+    AtomicLocking _ -> 6++localMemoryCase ::+  [PatElem KernelsMem] ->+  Imp.TExp Int32 ->+  SegSpace ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  Imp.TExp Int32 ->+  [SegHistSlug] ->+  KernelBody KernelsMem ->+  CallKernelGen (Imp.TExp Bool, CallKernelGen ())+localMemoryCase map_pes hist_T space hist_H hist_el_size hist_N _ slugs kbody = do+  let space_sizes = segSpaceDims space+      segment_dims = init space_sizes+      segmented = not $ null segment_dims++  hist_L <- dPrim "hist_L" int32+  sOp $ Imp.GetSizeMax (tvVar hist_L) Imp.SizeLocalMemory++  max_group_size <- dPrim "max_group_size" int32+  sOp $ Imp.GetSizeMax (tvVar max_group_size) Imp.SizeGroup+  let group_size = Imp.Count $ Var $ tvVar max_group_size+  num_groups <-+    fmap (Imp.Count . tvSize) $+      dPrimV "num_groups" $+        hist_T `divUp` sExt32 (toInt64Exp (unCount group_size))+  let num_groups' = toInt64Exp <$> num_groups+      group_size' = toInt64Exp <$> group_size++  let r64 = isF64 . ConvOpExp (SIToFP Int64 Float64) . untyped+      t64 = isInt64 . ConvOpExp (FPToSI Float64 Int64) . untyped++  -- M approximation.+  hist_m' <-+    dPrimVE "hist_m_prime" $+      r64+        ( sMin64+            (sExt64 (tvExp hist_L `quot` hist_el_size))+            (hist_N `divUp` sExt64 (unCount num_groups'))+        )+        / r64 hist_H++  let hist_B = unCount group_size'++  -- M in the paper, but not adjusted for asymptotic efficiency.+  hist_M0 <-+    dPrimVE "hist_M0" $+      sMax64 1 $ sMin64 (t64 hist_m') hist_B++  -- Minimal sequential chunking factor.+  let q_small = 2++  -- The number of segments/histograms produced..+  hist_Nout <- dPrimVE "hist_Nout" $ product $ map toInt64Exp segment_dims++  hist_Nin <- dPrimVE "hist_Nin" $ toInt64Exp $ last space_sizes++  -- Maximum M for work efficiency.+  work_asymp_M_max <-+    if segmented+      then do+        hist_T_hist_min <-+          dPrimVE "hist_T_hist_min" $+            sExt32 $+              sMin64 (sExt64 hist_Nin * sExt64 hist_Nout) (sExt64 hist_T)+                `divUp` sExt64 hist_Nout++        -- Number of groups, rounded up.+        let r = hist_T_hist_min `divUp` sExt32 hist_B++        dPrimVE "work_asymp_M_max" $ hist_Nin `quot` (sExt64 r * hist_H)+      else+        dPrimVE "work_asymp_M_max" $+          (hist_Nout * hist_N)+            `quot` ( (q_small * unCount num_groups' * hist_H)+                       `quot` genericLength slugs+                   )++  -- Number of subhistograms per result histogram.+  hist_M <- dPrimV "hist_M" $ sExt32 $ sMin64 hist_M0 work_asymp_M_max++  -- hist_M may be zero (which we'll check for below), but we need it+  -- for some divisions first, so crudely make a nonzero form.+  let hist_M_nonzero = sMax32 1 $ tvExp hist_M++  -- "Cooperation factor" - the number of threads cooperatively+  -- working on the same (sub)histogram.+  hist_C <-+    dPrimVE "hist_C" $+      hist_B `divUp` sExt64 hist_M_nonzero++  emit $ Imp.DebugPrint "local hist_M0" $ Just $ untyped hist_M0+  emit $ Imp.DebugPrint "local work asymp M max" $ Just $ untyped work_asymp_M_max+  emit $ Imp.DebugPrint "local C" $ Just $ untyped hist_C+  emit $ Imp.DebugPrint "local B" $ Just $ untyped hist_B+  emit $ Imp.DebugPrint "local M" $ Just $ untyped $ tvExp hist_M+  emit $+    Imp.DebugPrint "local memory needed" $+      Just $ untyped $ hist_H * hist_el_size * sExt64 (tvExp hist_M)++  -- local_mem_needed is what we need to keep a single bucket in local+  -- memory - this is an absolute minimum.  We can fit anything else+  -- by doing multiple passes, although more than a few is+  -- (heuristically) not efficient.+  local_mem_needed <-+    dPrimVE "local_mem_needed" $+      hist_el_size * sExt64 (tvExp hist_M)+  hist_S <-+    dPrimVE "hist_S" $+      sExt32 $+        (hist_H * local_mem_needed) `divUp` tvExp hist_L+  let max_S = case bodyPassage kbody of+        MustBeSinglePass -> 1+        MayBeMultiPass -> fromIntegral $ maxinum $ map slugMaxLocalMemPasses slugs++  -- We only use local memory if the number of updates per histogram+  -- at least matches the histogram size, as otherwise it is not+  -- asymptotically efficient.  This mostly matters for the segmented+  -- case.+  let pick_local =+        hist_Nin .>=. hist_H+          .&&. (local_mem_needed .<=. tvExp hist_L)+          .&&. (hist_S .<=. max_S)+          .&&. hist_C .<=. hist_B+          .&&. tvExp hist_M .>. 0++      groups_per_segment+        | segmented = num_groups' `divUp` Imp.Count hist_Nout+        | otherwise = num_groups'++      run = do+        emit $ Imp.DebugPrint "## Using local memory" Nothing+        emit $ Imp.DebugPrint "Histogram size (H)" $ Just $ untyped hist_H+        emit $ Imp.DebugPrint "Multiplication degree (M)" $ Just $ untyped $ tvExp hist_M+        emit $ Imp.DebugPrint "Cooperation level (C)" $ Just $ untyped hist_C+        emit $ Imp.DebugPrint "Number of chunks (S)" $ Just $ untyped hist_S+        when segmented $+          emit $ Imp.DebugPrint "Groups per segment" $ Just $ untyped $ unCount groups_per_segment+        histKernelLocal+          hist_M+          groups_per_segment+          map_pes+          num_groups+          group_size+          space+          hist_S+          slugs+          kbody++  return (pick_local, run)++-- | Generate code for a segmented histogram called from the host.+compileSegHist ::+  Pattern KernelsMem ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  SegSpace ->+  [HistOp KernelsMem] ->+  KernelBody KernelsMem ->+  CallKernelGen ()+compileSegHist (Pattern _ pes) num_groups group_size space ops kbody = do+  -- Most of this function is not the histogram part itself, but+  -- rather figuring out whether to use a local or global memory+  -- strategy, as well as collapsing the subhistograms produced (which+  -- are always in global memory, but their number may vary).+  let num_groups' = fmap toInt64Exp num_groups+      group_size' = fmap toInt64Exp group_size+      dims = map toInt64Exp $ segSpaceDims space++      num_red_res = length ops + sum (map (length . histNeutral) ops)+      (all_red_pes, map_pes) = splitAt num_red_res pes+      segment_size = last dims++  (op_hs, op_seg_hs, slugs) <- unzip3 <$> mapM (computeHistoUsage space) ops+  h <- dPrimVE "h" $ Imp.unCount $ sum op_hs+  seg_h <- dPrimVE "seg_h" $ Imp.unCount $ sum op_seg_hs++  -- Check for emptyness to avoid division-by-zero.+  sUnless (seg_h .==. 0) $ do+    -- Maximum group size (or actual, in this case).+    let hist_B = unCount group_size'++    -- Size of a histogram.+    hist_H <- dPrimVE "hist_H" $ sum $ map (toInt64Exp . histWidth) ops++    -- Size of a single histogram element.  Actually the weighted+    -- average of histogram elements in cases where we have more than+    -- one histogram operation, plus any locks.+    let lockSize slug = case slugAtomicUpdate slug of+          AtomicLocking {} -> Just $ primByteSize int32+          _ -> Nothing+    hist_el_size <-+      dPrimVE "hist_el_size" $+        foldl' (+) (h `divUp` hist_H) $+          mapMaybe lockSize slugs++    -- Input elements contributing to each histogram.+    hist_N <- dPrimVE "hist_N" segment_size++    -- Compute RF as the average RF over all the histograms.+    hist_RF <-+      dPrimVE "hist_RF" $+        sum (map (toInt32Exp . histRaceFactor . slugOp) slugs)+          `quot` genericLength slugs++    let hist_T = sExt32 $ unCount num_groups' * unCount group_size'+    emit $ Imp.DebugPrint "\n# SegHist" Nothing+    emit $ Imp.DebugPrint "Number of threads (T)" $ Just $ untyped hist_T+    emit $ Imp.DebugPrint "Desired group size (B)" $ Just $ untyped hist_B+    emit $ Imp.DebugPrint "Histogram size (H)" $ Just $ untyped hist_H+    emit $ Imp.DebugPrint "Input elements per histogram (N)" $ Just $ untyped hist_N+    emit $+      Imp.DebugPrint "Number of segments" $+        Just $ untyped $ product $ map (toInt64Exp . snd) segment_dims+    emit $ Imp.DebugPrint "Histogram element size (el_size)" $ Just $ untyped hist_el_size+    emit $ Imp.DebugPrint "Race factor (RF)" $ Just $ untyped hist_RF+    emit $ Imp.DebugPrint "Memory per set of subhistograms per segment" $ Just $ untyped h+    emit $ Imp.DebugPrint "Memory per set of subhistograms times segments" $ Just $ untyped seg_h++    (use_local_memory, run_in_local_memory) <-+      localMemoryCase map_pes hist_T space hist_H hist_el_size hist_N hist_RF slugs kbody++    sIf use_local_memory run_in_local_memory $+      histKernelGlobal map_pes num_groups group_size space slugs kbody++    let pes_per_op = chunks (map (length . histDest) ops) all_red_pes++    forM_ (zip3 slugs pes_per_op ops) $ \(slug, red_pes, op) -> do+      let num_histos = slugNumSubhistos slug+          subhistos = map subhistosArray $ slugSubhistos slug++      let unitHistoCase =+            -- This is OK because the memory blocks are at least as+            -- large as the ones we are supposed to use for the result.+            forM_ (zip red_pes subhistos) $ \(pe, subhisto) -> do+              pe_mem <-+                memLocationName . entryArrayLocation+                  <$> lookupArray (patElemName pe)+              subhisto_mem <-+                memLocationName . entryArrayLocation+                  <$> lookupArray subhisto+              emit $ Imp.SetMem pe_mem subhisto_mem $ Space "device"++      sIf (tvExp num_histos .==. 1) unitHistoCase $ do+        -- For the segmented reduction, we keep the segment dimensions+        -- unchanged.  To this, we add two dimensions: one over the number+        -- of buckets, and one over the number of subhistograms.  This+        -- inner dimension is the one that is collapsed in the reduction.+        let num_buckets = histWidth op++        bucket_id <- newVName "bucket_id"+        subhistogram_id <- newVName "subhistogram_id"+        vector_ids <-+          mapM (const $ newVName "vector_id") $+            shapeDims $ histShape op++        flat_gtid <- newVName "flat_gtid"++        let lvl = SegThread num_groups group_size SegVirt+            segred_space =+              SegSpace flat_gtid $+                segment_dims+                  ++ [(bucket_id, num_buckets)]+                  ++ zip vector_ids (shapeDims $ histShape op)+                  ++ [(subhistogram_id, Var $ tvVar num_histos)]++        let segred_op = SegBinOp Commutative (histOp op) (histNeutral op) mempty+        compileSegRed' (Pattern [] red_pes) lvl segred_space [segred_op] $ \red_cont ->+          red_cont $+            flip map subhistos $ \subhisto ->+              ( Var subhisto,+                map Imp.vi64 $+                  map fst segment_dims ++ [subhistogram_id, bucket_id] ++ vector_ids+              )+  where+    segment_dims = init $ unSegSpace space
src/Futhark/CodeGen/ImpGen/Kernels/SegMap.hs view
@@ -1,61 +1,59 @@-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+ -- | Code generation for 'SegMap' is quite straightforward.  The only -- trick is virtualisation in case the physical number of threads is -- not sufficient to cover the logical thread space.  This is handled -- by having actual workgroups run a loop to imitate multiple workgroups.-module Futhark.CodeGen.ImpGen.Kernels.SegMap-  ( compileSegMap )-where+module Futhark.CodeGen.ImpGen.Kernels.SegMap (compileSegMap) where  import Control.Monad.Except--import Prelude hiding (quot, rem)--import Futhark.IR.KernelsMem-import Futhark.CodeGen.ImpGen.Kernels.Base-import Futhark.CodeGen.ImpGen import qualified Futhark.CodeGen.ImpCode.Kernels as Imp+import Futhark.CodeGen.ImpGen+import Futhark.CodeGen.ImpGen.Kernels.Base+import Futhark.IR.KernelsMem import Futhark.Util.IntegralExp (divUp)+import Prelude hiding (quot, rem)  -- | Compile 'SegMap' instance code.-compileSegMap :: Pattern KernelsMem-              -> SegLevel-              -> SegSpace-              -> KernelBody KernelsMem-              -> CallKernelGen ()-+compileSegMap ::+  Pattern KernelsMem ->+  SegLevel ->+  SegSpace ->+  KernelBody KernelsMem ->+  CallKernelGen () compileSegMap pat lvl space kbody = do   let (is, dims) = unzip $ unSegSpace space-  dims' <- mapM toExp dims--  num_groups' <- traverse toExp $ segNumGroups lvl-  group_size' <- traverse toExp $ segGroupSize lvl+      dims' = map toInt64Exp dims+      num_groups' = toInt64Exp <$> segNumGroups lvl+      group_size' = toInt64Exp <$> segGroupSize lvl    case lvl of-    SegThread{} -> do+    SegThread {} -> do       emit $ Imp.DebugPrint "\n# SegMap" Nothing-      let virt_num_groups = product dims' `divUp` unCount group_size'+      let virt_num_groups =+            sExt32 $ product dims' `divUp` unCount group_size'       sKernelThread "segmap" num_groups' group_size' (segFlat space) $         virtualiseGroups (segVirt lvl) virt_num_groups $ \group_id -> do-        local_tid <- kernelLocalThreadId . kernelConstants <$> askEnv-        let global_tid = Imp.vi32 group_id * unCount group_size' + local_tid--        zipWithM_ dPrimV_ is $ unflattenIndex dims' global_tid+          local_tid <- kernelLocalThreadId . kernelConstants <$> askEnv+          let global_tid =+                sExt64 group_id * sExt64 (unCount group_size')+                  + sExt64 local_tid -        sWhen (isActive $ unSegSpace space) $-          compileStms mempty (kernelBodyStms kbody) $-          zipWithM_ (compileThreadResult space) (patternElements pat) $-          kernelBodyResult kbody+          zipWithM_ dPrimV_ is $+            map sExt64 $ unflattenIndex (map sExt64 dims') global_tid -    SegGroup{} ->+          sWhen (isActive $ unSegSpace space) $+            compileStms mempty (kernelBodyStms kbody) $+              zipWithM_ (compileThreadResult space) (patternElements pat) $+                kernelBodyResult kbody+    SegGroup {} ->       sKernelGroup "segmap_intragroup" num_groups' group_size' (segFlat space) $ do-      let virt_num_groups = product dims'-      precomputeSegOpIDs (kernelBodyStms kbody) $-        virtualiseGroups (segVirt lvl) virt_num_groups $ \group_id -> do--        zipWithM_ dPrimV_ is $ unflattenIndex dims' $ Imp.vi32 group_id+        let virt_num_groups = sExt32 $ product dims'+        precomputeSegOpIDs (kernelBodyStms kbody) $+          virtualiseGroups (segVirt lvl) virt_num_groups $ \group_id -> do+            zipWithM_ dPrimV_ is $ unflattenIndex dims' $ sExt64 group_id -        compileStms mempty (kernelBodyStms kbody) $-          zipWithM_ (compileGroupResult space) (patternElements pat) $-          kernelBodyResult kbody+            compileStms mempty (kernelBodyStms kbody) $+              zipWithM_ (compileGroupResult space) (patternElements pat) $+                kernelBodyResult kbody
src/Futhark/CodeGen/ImpGen/Kernels/SegRed.hs view
@@ -1,5 +1,6 @@-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+ -- | We generate code for non-segmented/single-segment SegRed using -- the basic approach outlined in the paper "Design and GPGPU -- Performance of Futhark’s Redomap Construct" (ARRAY '16).  The main@@ -42,27 +43,25 @@ --   large strategy, but at most 50% of the threads in the group would --   have any element to read, which becomes highly inefficient. module Futhark.CodeGen.ImpGen.Kernels.SegRed-  ( compileSegRed-  , compileSegRed'-  , DoSegBody+  ( compileSegRed,+    compileSegRed',+    DoSegBody,   )-  where+where  import Control.Monad.Except-import Data.Maybe import Data.List (genericLength, zip7)--import Prelude hiding (quot, rem)--import Futhark.Error-import Futhark.Transform.Rename-import Futhark.IR.KernelsMem+import Data.Maybe import qualified Futhark.CodeGen.ImpCode.Kernels as Imp import Futhark.CodeGen.ImpGen import Futhark.CodeGen.ImpGen.Kernels.Base+import Futhark.Error+import Futhark.IR.KernelsMem import qualified Futhark.IR.Mem.IxFun as IxFun+import Futhark.Transform.Rename import Futhark.Util (chunks) import Futhark.Util.IntegralExp (divUp, quot, rem)+import Prelude hiding (quot, rem)  -- | The maximum number of operators we support in a single SegRed. -- This limit arises out of the static allocation of counters.@@ -73,47 +72,53 @@ -- for saving the results of the body.  The results should be -- represented as a pairing of a t'SubExp' along with a list of -- indexes into that 'SubExp' for reading the result.-type DoSegBody = ([(SubExp, [Imp.Exp])] -> InKernelGen ()) -> InKernelGen ()+type DoSegBody = ([(SubExp, [Imp.TExp Int64])] -> InKernelGen ()) -> InKernelGen ()  -- | Compile 'SegRed' instance to host-level code with calls to -- various kernels.-compileSegRed :: Pattern KernelsMem-              -> SegLevel -> SegSpace-              -> [SegBinOp KernelsMem]-              -> KernelBody KernelsMem-              -> CallKernelGen ()+compileSegRed ::+  Pattern KernelsMem ->+  SegLevel ->+  SegSpace ->+  [SegBinOp KernelsMem] ->+  KernelBody KernelsMem ->+  CallKernelGen () compileSegRed pat lvl space reds body =   compileSegRed' pat lvl space reds $ \red_cont ->-  compileStms mempty (kernelBodyStms body) $ do-  let (red_res, map_res) = splitAt (segBinOpResults reds) $ kernelBodyResult body+    compileStms mempty (kernelBodyStms body) $ do+      let (red_res, map_res) = splitAt (segBinOpResults reds) $ kernelBodyResult body -  sComment "save map-out results" $ do-    let map_arrs = drop (segBinOpResults reds) $ patternElements pat-    zipWithM_ (compileThreadResult space) map_arrs map_res+      sComment "save map-out results" $ do+        let map_arrs = drop (segBinOpResults reds) $ patternElements pat+        zipWithM_ (compileThreadResult space) map_arrs map_res -  red_cont $ zip (map kernelResultSubExp red_res) $ repeat []+      red_cont $ zip (map kernelResultSubExp red_res) $ repeat []  -- | Like 'compileSegRed', but where the body is a monadic action.-compileSegRed' :: Pattern KernelsMem-               -> SegLevel -> SegSpace-               -> [SegBinOp KernelsMem]-               -> DoSegBody-               -> CallKernelGen ()+compileSegRed' ::+  Pattern KernelsMem ->+  SegLevel ->+  SegSpace ->+  [SegBinOp KernelsMem] ->+  DoSegBody ->+  CallKernelGen () compileSegRed' pat lvl space reds body   | genericLength reds > maxNumOps =-      compilerLimitationS $+    compilerLimitationS $       "compileSegRed': at most " ++ show maxNumOps ++ " reduction operators are supported."-  | [(_, Constant (IntValue (Int32Value 1))), _] <- unSegSpace space =-      nonsegmentedReduction pat num_groups group_size space reds body+  | [(_, Constant (IntValue (Int64Value 1))), _] <- unSegSpace space =+    nonsegmentedReduction pat num_groups group_size space reds body   | otherwise = do-      group_size' <- toExp $ unCount group_size-      segment_size <- toExp $ last $ segSpaceDims space-      let use_small_segments = segment_size * 2 .<. group_size'-      sIf use_small_segments-        (smallSegmentsReduction pat num_groups group_size space reds body)-        (largeSegmentsReduction pat num_groups group_size space reds body)-  where num_groups = segNumGroups lvl-        group_size = segGroupSize lvl+    let group_size' = toInt32Exp $ unCount group_size+        segment_size = toInt32Exp $ last $ segSpaceDims space+        use_small_segments = segment_size * 2 .<. group_size'+    sIf+      use_small_segments+      (smallSegmentsReduction pat num_groups group_size space reds body)+      (largeSegmentsReduction pat num_groups group_size space reds body)+  where+    num_groups = segNumGroups lvl+    group_size = segGroupSize lvl  -- | Prepare intermediate arrays for the reduction.  Prim-typed -- arguments go in local memory (so we need to do the allocation of@@ -121,9 +126,11 @@ -- global memory.  Allocations for the former have already been -- performed.  This policy is baked into how the allocations are done -- in ExplicitAllocations.-intermediateArrays :: Count GroupSize SubExp -> SubExp-                   -> SegBinOp KernelsMem-                   -> InKernelGen [VName]+intermediateArrays ::+  Count GroupSize SubExp ->+  SubExp ->+  SegBinOp KernelsMem ->+  InKernelGen [VName] intermediateArrays (Count group_size) num_threads (SegBinOp _ red_op nes _) = do   let red_op_params = lambdaParams red_op       (red_acc_params, _) = splitAt (length nes) red_op_params@@ -132,7 +139,7 @@       MemArray pt shape _ (ArrayIn mem _) -> do         let shape' = Shape [num_threads] <> shape         sArray "red_arr" pt shape' $-          ArrayIn mem $ IxFun.iota $ map (primExpFromSubExp int32) $ shapeDims shape'+          ArrayIn mem $ IxFun.iota $ map pe64 $ shapeDims shape'       _ -> do         let pt = elemType $ paramType p             shape = Shape [group_size]@@ -144,121 +151,168 @@ -- because they are also used for keeping vectorised accumulators for -- first-stage reduction, if necessary.  When actually storing group -- results, the first index is set to 0.-groupResultArrays :: Count NumGroups SubExp -> Count GroupSize SubExp-                  -> [SegBinOp KernelsMem]-                  -> CallKernelGen [[VName]]+groupResultArrays ::+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  [SegBinOp KernelsMem] ->+  CallKernelGen [[VName]] groupResultArrays (Count virt_num_groups) (Count group_size) reds =   forM reds $ \(SegBinOp _ lam _ shape) ->     forM (lambdaReturnType lam) $ \t -> do-    let pt = elemType t-        full_shape = Shape [group_size, virt_num_groups] <> shape <> arrayShape t-        -- Move the groupsize dimension last to ensure coalesced-        -- memory access.-        perm = [1..shapeRank full_shape-1] ++ [0]-    sAllocArrayPerm "group_res_arr" pt full_shape (Space "device") perm+      let pt = elemType t+          full_shape = Shape [group_size, virt_num_groups] <> shape <> arrayShape t+          -- Move the groupsize dimension last to ensure coalesced+          -- memory access.+          perm = [1 .. shapeRank full_shape -1] ++ [0]+      sAllocArrayPerm "group_res_arr" pt full_shape (Space "device") perm -nonsegmentedReduction :: Pattern KernelsMem-                      -> Count NumGroups SubExp -> Count GroupSize SubExp -> SegSpace-                      -> [SegBinOp KernelsMem]-                      -> DoSegBody-                      -> CallKernelGen ()+nonsegmentedReduction ::+  Pattern KernelsMem ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  SegSpace ->+  [SegBinOp KernelsMem] ->+  DoSegBody ->+  CallKernelGen () nonsegmentedReduction segred_pat num_groups group_size space reds body = do   let (gtids, dims) = unzip $ unSegSpace space-  dims' <- mapM toExp dims--  num_groups' <- traverse toExp num_groups-  group_size' <- traverse toExp group_size--  let global_tid = Imp.vi32 $ segFlat space+      dims' = map toInt64Exp dims+      num_groups' = fmap toInt64Exp num_groups+      group_size' = fmap toInt64Exp group_size+      global_tid = Imp.vi32 $ segFlat space       w = last dims'    counter <-     sStaticArray "counter" (Space "device") int32 $-    Imp.ArrayValues $ replicate (fromIntegral maxNumOps) $ IntValue $ Int32Value 0+      Imp.ArrayValues $ replicate (fromIntegral maxNumOps) $ IntValue $ Int32Value 0    reds_group_res_arrs <- groupResultArrays num_groups group_size reds -  num_threads <- dPrimV "num_threads" $ unCount num_groups' * unCount group_size'+  num_threads <-+    dPrimV "num_threads" $+      unCount num_groups' * unCount group_size'    emit $ Imp.DebugPrint "\n# SegRed" Nothing    sKernelThread "segred_nonseg" num_groups' group_size' (segFlat space) $ do     constants <- kernelConstants <$> askEnv     sync_arr <- sAllocArray "sync_arr" Bool (Shape [intConst Int32 1]) $ Space "local"-    reds_arrs <- mapM (intermediateArrays group_size (Var num_threads)) reds+    reds_arrs <- mapM (intermediateArrays group_size (tvSize num_threads)) reds      -- Since this is the nonsegmented case, all outer segment IDs must     -- necessarily be 0.-    forM_ gtids $ \v -> dPrimV_ v 0+    forM_ gtids $ \v -> dPrimV_ v (0 :: Imp.TExp Int32)      let num_elements = Imp.elements w-    let elems_per_thread = num_elements `divUp` Imp.elements (kernelNumThreads constants)+        elems_per_thread =+          num_elements+            `divUp` Imp.elements (sExt64 (kernelNumThreads constants)) -    slugs <- mapM (segBinOpSlug-                   (kernelLocalThreadId constants)-                   (kernelGroupId constants)) $-             zip3 reds reds_arrs reds_group_res_arrs+    slugs <-+      mapM+        ( segBinOpSlug+            (kernelLocalThreadId constants)+            (kernelGroupId constants)+        )+        $ zip3 reds reds_arrs reds_group_res_arrs     reds_op_renamed <--      reductionStageOne constants (zip gtids dims') num_elements-      global_tid elems_per_thread num_threads-      slugs body+      reductionStageOne+        constants+        (zip gtids dims')+        num_elements+        global_tid+        elems_per_thread+        (tvVar num_threads)+        slugs+        body -    let segred_pes = chunks (map (length . segBinOpNeutral) reds) $-                     patternElements segred_pat-    forM_ (zip7 reds reds_arrs reds_group_res_arrs segred_pes-           slugs reds_op_renamed [0..]) $-      \(SegBinOp _ red_op nes _,-        red_arrs, group_res_arrs, pes, slug, red_op_renamed, i) -> do-      let (red_x_params, red_y_params) = splitAt (length nes) $ lambdaParams red_op-      reductionStageTwo constants pes (kernelGroupId constants) 0 [0] 0-        (kernelNumGroups constants) slug red_x_params red_y_params-        red_op_renamed nes-        1 counter (ValueExp $ IntValue $ Int32Value i)-        sync_arr group_res_arrs red_arrs+    let segred_pes =+          chunks (map (length . segBinOpNeutral) reds) $+            patternElements segred_pat+    forM_+      ( zip7+          reds+          reds_arrs+          reds_group_res_arrs+          segred_pes+          slugs+          reds_op_renamed+          [0 ..]+      )+      $ \( SegBinOp _ red_op nes _,+           red_arrs,+           group_res_arrs,+           pes,+           slug,+           red_op_renamed,+           i+           ) -> do+          let (red_x_params, red_y_params) = splitAt (length nes) $ lambdaParams red_op+          reductionStageTwo+            constants+            pes+            (kernelGroupId constants)+            0+            [0]+            0+            (sExt64 $ kernelNumGroups constants)+            slug+            red_x_params+            red_y_params+            red_op_renamed+            nes+            1+            counter+            (fromInteger i)+            sync_arr+            group_res_arrs+            red_arrs -smallSegmentsReduction :: Pattern KernelsMem-                       -> Count NumGroups SubExp -> Count GroupSize SubExp-                       -> SegSpace-                       -> [SegBinOp KernelsMem]-                       -> DoSegBody-                       -> CallKernelGen ()+smallSegmentsReduction ::+  Pattern KernelsMem ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  SegSpace ->+  [SegBinOp KernelsMem] ->+  DoSegBody ->+  CallKernelGen () smallSegmentsReduction (Pattern _ segred_pes) num_groups group_size space reds body = do   let (gtids, dims) = unzip $ unSegSpace space-  dims' <- mapM toExp dims+      dims' = map toInt64Exp dims+      segment_size = last dims' -  let segment_size = last dims'   -- Careful to avoid division by zero now.-  segment_size_nonzero_v <- dPrimV "segment_size_nonzero" $-                            BinOpExp (SMax Int32) 1 segment_size+  segment_size_nonzero <-+    dPrimVE "segment_size_nonzero" $ sMax64 1 segment_size -  num_groups' <- traverse toExp num_groups-  group_size' <- traverse toExp group_size+  let num_groups' = fmap toInt64Exp num_groups+      group_size' = fmap toInt64Exp group_size   num_threads <- dPrimV "num_threads" $ unCount num_groups' * unCount group_size'-  let segment_size_nonzero = Imp.var segment_size_nonzero_v int32-      num_segments = product $ init dims'+  let num_segments = product $ init dims'       segments_per_group = unCount group_size' `quot` segment_size_nonzero-      required_groups = num_segments `divUp` segments_per_group+      required_groups = sExt32 $ num_segments `divUp` segments_per_group    emit $ Imp.DebugPrint "\n# SegRed-small" Nothing-  emit $ Imp.DebugPrint "num_segments" $ Just num_segments-  emit $ Imp.DebugPrint "segment_size" $ Just segment_size-  emit $ Imp.DebugPrint "segments_per_group" $ Just segments_per_group-  emit $ Imp.DebugPrint "required_groups" $ Just required_groups+  emit $ Imp.DebugPrint "num_segments" $ Just $ untyped num_segments+  emit $ Imp.DebugPrint "segment_size" $ Just $ untyped segment_size+  emit $ Imp.DebugPrint "segments_per_group" $ Just $ untyped segments_per_group+  emit $ Imp.DebugPrint "required_groups" $ Just $ untyped required_groups    sKernelThread "segred_small" num_groups' group_size' (segFlat space) $ do     constants <- kernelConstants <$> askEnv-    reds_arrs <- mapM (intermediateArrays group_size (Var num_threads)) reds+    reds_arrs <- mapM (intermediateArrays group_size (Var $ tvVar num_threads)) reds      -- We probably do not have enough actual workgroups to cover the     -- entire iteration space.  Some groups thus have to perform double     -- duty; we put an outer loop to accomplish this.-    virtualiseGroups SegVirt required_groups $ \group_id_var' -> do-      let group_id' = Imp.vi32 group_id_var'+    virtualiseGroups SegVirt required_groups $ \group_id' -> do       -- Compute the 'n' input indices.  The outer 'n-1' correspond to       -- the segment ID, and are computed from the group id.  The inner       -- is computed from the local thread id, and may be out-of-bounds.-      let ltid = kernelLocalThreadId constants-          segment_index = (ltid `quot` segment_size_nonzero) + (group_id' * segments_per_group)+      let ltid = sExt64 $ kernelLocalThreadId constants+          segment_index =+            (ltid `quot` segment_size_nonzero)+              + (sExt64 group_id' * sExt64 segments_per_group)           index_within_segment = ltid `rem` segment_size        zipWithM_ dPrimV_ (init gtids) $ unflattenIndex (init dims') segment_index@@ -266,83 +320,105 @@        let out_of_bounds =             forM_ (zip reds reds_arrs) $ \(SegBinOp _ _ nes _, red_arrs) ->-            forM_ (zip red_arrs nes) $ \(arr, ne) ->-            copyDWIMFix arr [ltid] ne []+              forM_ (zip red_arrs nes) $ \(arr, ne) ->+                copyDWIMFix arr [ltid] ne []            in_bounds =             body $ \red_res ->-            sComment "save results to be reduced" $ do-            let red_dests = zip (concat reds_arrs) $ repeat [ltid]-            forM_ (zip red_dests red_res) $ \((d,d_is), (res, res_is)) ->-              copyDWIMFix d d_is res res_is+              sComment "save results to be reduced" $ do+                let red_dests = zip (concat reds_arrs) $ repeat [ltid]+                forM_ (zip red_dests red_res) $ \((d, d_is), (res, res_is)) ->+                  copyDWIMFix d d_is res res_is        sComment "apply map function if in bounds" $-        sIf (segment_size .>. 0 .&&.-             isActive (init $ zip gtids dims) .&&.-             ltid .<. segment_size * segments_per_group) in_bounds out_of_bounds+        sIf+          ( segment_size .>. 0+              .&&. isActive (init $ zip gtids dims)+              .&&. ltid .<. segment_size * segments_per_group+          )+          in_bounds+          out_of_bounds        sOp $ Imp.ErrorSync Imp.FenceLocal -- Also implicitly barrier.--      let crossesSegment from to = (to-from) .>. (to `rem` segment_size)+      let crossesSegment from to =+            (sExt64 to - sExt64 from) .>. (sExt64 to `rem` segment_size)       sWhen (segment_size .>. 0) $         sComment "perform segmented scan to imitate reduction" $-        forM_ (zip reds reds_arrs) $ \(SegBinOp _ red_op _ _, red_arrs) ->-        groupScan (Just crossesSegment) (Imp.vi32 num_threads)-        (segment_size*segments_per_group) red_op red_arrs+          forM_ (zip reds reds_arrs) $ \(SegBinOp _ red_op _ _, red_arrs) ->+            groupScan+              (Just crossesSegment)+              (sExt64 $ tvExp num_threads)+              (segment_size * segments_per_group)+              red_op+              red_arrs        sOp $ Imp.Barrier Imp.FenceLocal        sComment "save final values of segments" $-        sWhen (group_id' * segments_per_group + ltid .<. num_segments .&&.-               ltid .<. segments_per_group) $-        forM_ (zip segred_pes (concat reds_arrs)) $ \(pe, arr) -> do-        -- Figure out which segment result this thread should write...-        let flat_segment_index = group_id' * segments_per_group + ltid-            gtids' = unflattenIndex (init dims') flat_segment_index-        copyDWIMFix (patElemName pe) gtids'-                        (Var arr) [(ltid+1) * segment_size_nonzero - 1]+        sWhen+          ( sExt64 group_id' * segments_per_group + sExt64 ltid .<. num_segments+              .&&. ltid .<. segments_per_group+          )+          $ forM_ (zip segred_pes (concat reds_arrs)) $ \(pe, arr) -> do+            -- Figure out which segment result this thread should write...+            let flat_segment_index =+                  sExt64 group_id' * segments_per_group + sExt64 ltid+                gtids' =+                  unflattenIndex (init dims') flat_segment_index+            copyDWIMFix+              (patElemName pe)+              gtids'+              (Var arr)+              [(ltid + 1) * segment_size_nonzero - 1]        -- Finally another barrier, because we will be writing to the       -- local memory array first thing in the next iteration.       sOp $ Imp.Barrier Imp.FenceLocal -largeSegmentsReduction :: Pattern KernelsMem-                       -> Count NumGroups SubExp -> Count GroupSize SubExp-                       -> SegSpace-                       -> [SegBinOp KernelsMem]-                       -> DoSegBody-                       -> CallKernelGen ()+largeSegmentsReduction ::+  Pattern KernelsMem ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  SegSpace ->+  [SegBinOp KernelsMem] ->+  DoSegBody ->+  CallKernelGen () largeSegmentsReduction segred_pat num_groups group_size space reds body = do   let (gtids, dims) = unzip $ unSegSpace space-  dims' <- mapM toExp dims-  let segment_size = last dims'+      dims' = map toInt64Exp dims       num_segments = product $ init dims'--  num_groups' <- traverse toExp num_groups-  group_size' <- traverse toExp group_size+      segment_size = last dims'+      num_groups' = fmap toInt64Exp num_groups+      group_size' = fmap toInt64Exp group_size    (groups_per_segment, elems_per_thread) <--    groupsPerSegmentAndElementsPerThread segment_size num_segments-    num_groups' group_size'-  virt_num_groups <- dPrimV "virt_num_groups" $-                     groups_per_segment * num_segments+    groupsPerSegmentAndElementsPerThread+      segment_size+      num_segments+      num_groups'+      group_size'+  virt_num_groups <-+    dPrimV "virt_num_groups" $+      groups_per_segment * num_segments -  num_threads <- dPrimV "num_threads" $-                 unCount num_groups' * unCount group_size'+  num_threads <-+    dPrimV "num_threads" $+      unCount num_groups' * unCount group_size' -  threads_per_segment <- dPrimV "threads_per_segment" $-    groups_per_segment * unCount group_size'+  threads_per_segment <-+    dPrimV "threads_per_segment" $+      groups_per_segment * unCount group_size'    emit $ Imp.DebugPrint "\n# SegRed-large" Nothing-  emit $ Imp.DebugPrint "num_segments" $ Just num_segments-  emit $ Imp.DebugPrint "segment_size" $ Just segment_size-  emit $ Imp.DebugPrint "virt_num_groups" $ Just $ Imp.vi32 virt_num_groups-  emit $ Imp.DebugPrint "num_groups" $ Just $ Imp.unCount num_groups'-  emit $ Imp.DebugPrint "group_size" $ Just $ Imp.unCount group_size'-  emit $ Imp.DebugPrint "elems_per_thread" $ Just $ Imp.unCount elems_per_thread-  emit $ Imp.DebugPrint "groups_per_segment" $ Just groups_per_segment+  emit $ Imp.DebugPrint "num_segments" $ Just $ untyped num_segments+  emit $ Imp.DebugPrint "segment_size" $ Just $ untyped segment_size+  emit $ Imp.DebugPrint "virt_num_groups" $ Just $ untyped $ tvExp virt_num_groups+  emit $ Imp.DebugPrint "num_groups" $ Just $ untyped $ Imp.unCount num_groups'+  emit $ Imp.DebugPrint "group_size" $ Just $ untyped $ Imp.unCount group_size'+  emit $ Imp.DebugPrint "elems_per_thread" $ Just $ untyped $ Imp.unCount elems_per_thread+  emit $ Imp.DebugPrint "groups_per_segment" $ Just $ untyped groups_per_segment -  reds_group_res_arrs <- groupResultArrays (Count (Var virt_num_groups)) group_size reds+  reds_group_res_arrs <- groupResultArrays (Count (tvSize virt_num_groups)) group_size reds    -- In principle we should have a counter for every segment.  Since   -- the number of segments is a dynamic quantity, we would have to@@ -357,91 +433,133 @@   let num_counters = fromIntegral maxNumOps * 1024   counter <-     sStaticArray "counter" (Space "device") int32 $-    Imp.ArrayZeros num_counters+      Imp.ArrayZeros num_counters    sKernelThread "segred_large" num_groups' group_size' (segFlat space) $ do     constants <- kernelConstants <$> askEnv-    reds_arrs <- mapM (intermediateArrays group_size (Var num_threads)) reds+    reds_arrs <- mapM (intermediateArrays group_size (tvSize num_threads)) reds     sync_arr <- sAllocArray "sync_arr" Bool (Shape [intConst Int32 1]) $ Space "local"      -- We probably do not have enough actual workgroups to cover the     -- entire iteration space.  Some groups thus have to perform double     -- duty; we put an outer loop to accomplish this.-    virtualiseGroups SegVirt (Imp.vi32 virt_num_groups) $ \group_id_var -> do+    virtualiseGroups SegVirt (sExt32 (tvExp virt_num_groups)) $ \group_id -> do       let segment_gtids = init gtids           w = last dims-          group_id = Imp.vi32 group_id_var           local_tid = kernelLocalThreadId constants -      flat_segment_id <- dPrimVE "flat_segment_id" $-                         group_id `quot` groups_per_segment+      flat_segment_id <-+        dPrimVE "flat_segment_id" $+          group_id `quot` sExt32 groups_per_segment -      global_tid <- dPrimVE "global_tid" $-                    (group_id * unCount group_size' + local_tid)-                    `rem` (unCount group_size' * groups_per_segment)+      global_tid <-+        dPrimVE "global_tid" $+          (sExt64 group_id * sExt64 (unCount group_size') + sExt64 local_tid)+            `rem` (sExt64 (unCount group_size') * groups_per_segment) -      let first_group_for_segment = flat_segment_id * groups_per_segment+      let first_group_for_segment = sExt64 flat_segment_id * groups_per_segment -      zipWithM_ dPrimV_ segment_gtids $ unflattenIndex (init dims') flat_segment_id-      dPrim_ (last gtids) int32-      num_elements <- Imp.elements <$> toExp w+      zipWithM_ dPrimV_ segment_gtids $+        unflattenIndex (init dims') $ sExt64 flat_segment_id+      dPrim_ (last gtids) int64+      let num_elements = Imp.elements $ toInt64Exp w -      slugs <- mapM (segBinOpSlug local_tid group_id) $-               zip3 reds reds_arrs reds_group_res_arrs+      slugs <-+        mapM (segBinOpSlug local_tid group_id) $+          zip3 reds reds_arrs reds_group_res_arrs       reds_op_renamed <--        reductionStageOne constants (zip gtids dims') num_elements-        global_tid elems_per_thread threads_per_segment-        slugs body+        reductionStageOne+          constants+          (zip gtids dims')+          num_elements+          (sExt32 global_tid)+          elems_per_thread+          (tvVar threads_per_segment)+          slugs+          body -      let segred_pes = chunks (map (length . segBinOpNeutral) reds) $-                       patternElements segred_pat+      let segred_pes =+            chunks (map (length . segBinOpNeutral) reds) $+              patternElements segred_pat            multiple_groups_per_segment =-            forM_ (zip7 reds reds_arrs reds_group_res_arrs segred_pes-                   slugs reds_op_renamed [0..]) $-            \(SegBinOp _ red_op nes _, red_arrs, group_res_arrs, pes,-              slug, red_op_renamed, i) -> do-              let (red_x_params, red_y_params) =-                    splitAt (length nes) $ lambdaParams red_op-              reductionStageTwo constants pes-                group_id flat_segment_id (map (`Imp.var` int32) segment_gtids)-                first_group_for_segment groups_per_segment-                slug red_x_params red_y_params red_op_renamed nes-                (fromIntegral num_counters) counter (ValueExp $ IntValue $ Int32Value i)-                sync_arr group_res_arrs red_arrs+            forM_+              ( zip7+                  reds+                  reds_arrs+                  reds_group_res_arrs+                  segred_pes+                  slugs+                  reds_op_renamed+                  [0 ..]+              )+              $ \( SegBinOp _ red_op nes _,+                   red_arrs,+                   group_res_arrs,+                   pes,+                   slug,+                   red_op_renamed,+                   i+                   ) -> do+                  let (red_x_params, red_y_params) =+                        splitAt (length nes) $ lambdaParams red_op+                  reductionStageTwo+                    constants+                    pes+                    group_id+                    flat_segment_id+                    (map Imp.vi64 segment_gtids)+                    (sExt64 first_group_for_segment)+                    groups_per_segment+                    slug+                    red_x_params+                    red_y_params+                    red_op_renamed+                    nes+                    (fromIntegral num_counters)+                    counter+                    (fromInteger i)+                    sync_arr+                    group_res_arrs+                    red_arrs            one_group_per_segment =             comment "first thread in group saves final result to memory" $-            forM_ (zip slugs segred_pes) $ \(slug, pes) ->-            sWhen (local_tid .==. 0) $-              forM_ (zip pes (slugAccs slug)) $ \(v, (acc, acc_is)) ->-              copyDWIMFix (patElemName v) (map (`Imp.var` int32) segment_gtids) (Var acc) acc_is+              forM_ (zip slugs segred_pes) $ \(slug, pes) ->+                sWhen (local_tid .==. 0) $+                  forM_ (zip pes (slugAccs slug)) $ \(v, (acc, acc_is)) ->+                    copyDWIMFix (patElemName v) (map Imp.vi64 segment_gtids) (Var acc) acc_is        sIf (groups_per_segment .==. 1) one_group_per_segment multiple_groups_per_segment  -- Careful to avoid division by zero here.  We have at least one group -- per segment.-groupsPerSegmentAndElementsPerThread :: Imp.Exp -> Imp.Exp-                                     -> Count NumGroups Imp.Exp -> Count GroupSize Imp.Exp-                                     -> CallKernelGen (Imp.Exp, Imp.Count Imp.Elements Imp.Exp)+groupsPerSegmentAndElementsPerThread ::+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  Count NumGroups (Imp.TExp Int64) ->+  Count GroupSize (Imp.TExp Int64) ->+  CallKernelGen+    ( Imp.TExp Int64,+      Imp.Count Imp.Elements (Imp.TExp Int64)+    ) groupsPerSegmentAndElementsPerThread segment_size num_segments num_groups_hint group_size = do   groups_per_segment <-     dPrimVE "groups_per_segment" $-    unCount num_groups_hint `divUp` BinOpExp (SMax Int32) 1 num_segments+      unCount num_groups_hint `divUp` sMax64 1 num_segments   elements_per_thread <-     dPrimVE "elements_per_thread" $-    segment_size `divUp` (unCount group_size * groups_per_segment)+      segment_size `divUp` (unCount group_size * groups_per_segment)   return (groups_per_segment, Imp.elements elements_per_thread)  -- | A SegBinOp with auxiliary information.-data SegBinOpSlug =-  SegBinOpSlug-  { slugOp :: SegBinOp KernelsMem-  , slugArrs :: [VName]-    -- ^ The arrays used for computing the intra-group reduction+data SegBinOpSlug = SegBinOpSlug+  { slugOp :: SegBinOp KernelsMem,+    -- | The arrays used for computing the intra-group reduction     -- (either local or global memory).-  , slugAccs :: [(VName, [Imp.Exp])]-    -- ^ Places to store accumulator in stage 1 reduction.+    slugArrs :: [VName],+    -- | Places to store accumulator in stage 1 reduction.+    slugAccs :: [(VName, [Imp.TExp Int64])]   }  slugBody :: SegBinOpSlug -> Body KernelsMem@@ -463,70 +581,71 @@ accParams slug = take (length (slugNeutral slug)) $ slugParams slug nextParams slug = drop (length (slugNeutral slug)) $ slugParams slug -segBinOpSlug :: Imp.Exp -> Imp.Exp -> (SegBinOp KernelsMem, [VName], [VName]) -> InKernelGen SegBinOpSlug+segBinOpSlug :: Imp.TExp Int32 -> Imp.TExp Int32 -> (SegBinOp KernelsMem, [VName], [VName]) -> InKernelGen SegBinOpSlug segBinOpSlug local_tid group_id (op, group_res_arrs, param_arrs) =-  SegBinOpSlug op group_res_arrs <$>-  zipWithM mkAcc (lambdaParams (segBinOpLambda op)) param_arrs-  where mkAcc p param_arr-          | Prim t <- paramType p,-            shapeRank (segBinOpShape op) == 0 = do-              acc <- dPrim (baseString (paramName p) <> "_acc") t-              return (acc, [])-          | otherwise =-              return (param_arr, [local_tid, group_id])+  SegBinOpSlug op group_res_arrs+    <$> zipWithM mkAcc (lambdaParams (segBinOpLambda op)) param_arrs+  where+    mkAcc p param_arr+      | Prim t <- paramType p,+        shapeRank (segBinOpShape op) == 0 = do+        acc <- dPrim (baseString (paramName p) <> "_acc") t+        return (tvVar acc, [])+      | otherwise =+        return (param_arr, [sExt64 local_tid, sExt64 group_id]) -reductionStageZero :: KernelConstants-                   -> [(VName, Imp.Exp)]-                   -> Imp.Count Imp.Elements Imp.Exp-                   -> Imp.Exp-                   -> Imp.Count Imp.Elements Imp.Exp-                   -> VName-                   -> [SegBinOpSlug]-                   -> DoSegBody-                   -> InKernelGen ([Lambda KernelsMem], InKernelGen ())+reductionStageZero ::+  KernelConstants ->+  [(VName, Imp.TExp Int64)] ->+  Imp.Count Imp.Elements (Imp.TExp Int64) ->+  Imp.TExp Int32 ->+  Imp.Count Imp.Elements (Imp.TExp Int64) ->+  VName ->+  [SegBinOpSlug] ->+  DoSegBody ->+  InKernelGen ([Lambda KernelsMem], InKernelGen ()) reductionStageZero constants ispace num_elements global_tid elems_per_thread threads_per_segment slugs body = do   let (gtids, _dims) = unzip ispace-      gtid = last gtids-      local_tid = kernelLocalThreadId constants+      gtid = mkTV (last gtids) int64+      local_tid = sExt64 $ kernelLocalThreadId constants    -- Figure out how many elements this thread should process.-  chunk_size <- dPrim "chunk_size" int32+  chunk_size <- dPrim "chunk_size" int64   let ordering = case slugsComm slugs of-                   Commutative -> SplitStrided $ Var threads_per_segment-                   Noncommutative -> SplitContiguous-  computeThreadChunkSize ordering global_tid elems_per_thread num_elements chunk_size+        Commutative -> SplitStrided $ Var threads_per_segment+        Noncommutative -> SplitContiguous+  computeThreadChunkSize ordering (sExt64 global_tid) elems_per_thread num_elements chunk_size    dScope Nothing $ scopeOfLParams $ concatMap slugParams slugs    sComment "neutral-initialise the accumulators" $     forM_ slugs $ \slug ->-    forM_ (zip (slugAccs slug) (slugNeutral slug)) $ \((acc, acc_is), ne) ->-    sLoopNest (slugShape slug) $ \vec_is ->-    copyDWIMFix acc (acc_is++vec_is) ne []+      forM_ (zip (slugAccs slug) (slugNeutral slug)) $ \((acc, acc_is), ne) ->+        sLoopNest (slugShape slug) $ \vec_is ->+          copyDWIMFix acc (acc_is ++ vec_is) ne []    slugs_op_renamed <- mapM (renameLambda . segBinOpLambda . slugOp) slugs    let doTheReduction =         forM_ (zip slugs_op_renamed slugs) $ \(slug_op_renamed, slug) ->-        sLoopNest (slugShape slug) $ \vec_is -> do-          comment "to reduce current chunk, first store our result in memory" $ do-            forM_ (zip (slugParams slug) (slugAccs slug)) $ \(p, (acc, acc_is)) ->-              copyDWIMFix (paramName p) [] (Var acc) (acc_is++vec_is)--            forM_ (zip (slugArrs slug) (slugParams slug)) $ \(arr, p) ->-              when (primType $ paramType p) $-              copyDWIMFix arr [local_tid] (Var $ paramName p) []+          sLoopNest (slugShape slug) $ \vec_is -> do+            comment "to reduce current chunk, first store our result in memory" $ do+              forM_ (zip (slugParams slug) (slugAccs slug)) $ \(p, (acc, acc_is)) ->+                copyDWIMFix (paramName p) [] (Var acc) (acc_is ++ vec_is) -          sOp $ Imp.ErrorSync Imp.FenceLocal -- Also implicitly barrier.+              forM_ (zip (slugArrs slug) (slugParams slug)) $ \(arr, p) ->+                when (primType $ paramType p) $+                  copyDWIMFix arr [local_tid] (Var $ paramName p) [] -          groupReduce (kernelGroupSize constants) slug_op_renamed (slugArrs slug)+            sOp $ Imp.ErrorSync Imp.FenceLocal -- Also implicitly barrier.+            groupReduce (sExt32 (kernelGroupSize constants)) slug_op_renamed (slugArrs slug) -          sOp $ Imp.Barrier Imp.FenceLocal+            sOp $ Imp.Barrier Imp.FenceLocal -          sComment "first thread saves the result in accumulator" $-            sWhen (local_tid .==. 0) $-            forM_ (zip (slugAccs slug) (lambdaParams slug_op_renamed)) $ \((acc, acc_is), p) ->-            copyDWIMFix acc (acc_is++vec_is) (Var $ paramName p) []+            sComment "first thread saves the result in accumulator" $+              sWhen (local_tid .==. 0) $+                forM_ (zip (slugAccs slug) (lambdaParams slug_op_renamed)) $ \((acc, acc_is), p) ->+                  copyDWIMFix acc (acc_is ++ vec_is) (Var $ paramName p) []    -- If this is a non-commutative reduction, each thread must run the   -- loop the same number of iterations, because we will be performing@@ -534,42 +653,47 @@   let comm = slugsComm slugs       (bound, check_bounds) =         case comm of-          Commutative -> (Imp.var chunk_size int32, id)-          Noncommutative -> (Imp.unCount elems_per_thread,-                             sWhen (Imp.var gtid int32 .<. Imp.unCount num_elements))+          Commutative -> (tvExp chunk_size, id)+          Noncommutative ->+            ( Imp.unCount elems_per_thread,+              sWhen (tvExp gtid .<. Imp.unCount num_elements)+            )    sFor "i" bound $ \i -> do-    gtid <---      case comm of+    gtid+      <-- case comm of         Commutative ->-          global_tid +-          Imp.var threads_per_segment int32 * i+          sExt64 global_tid+            + Imp.vi64 threads_per_segment * i         Noncommutative ->-          let index_in_segment = global_tid `quot` kernelGroupSize constants-          in local_tid +-             (index_in_segment * Imp.unCount elems_per_thread + i) *-             kernelGroupSize constants--    check_bounds $ sComment "apply map function" $-      body $ \all_red_res -> do+          let index_in_segment = global_tid `quot` sExt32 (kernelGroupSize constants)+           in sExt64 local_tid+                + (sExt64 index_in_segment * Imp.unCount elems_per_thread + i)+                * sExt64 (kernelGroupSize constants) -      let slugs_res = chunks (map (length . slugNeutral) slugs) all_red_res+    check_bounds $+      sComment "apply map function" $+        body $ \all_red_res -> do+          let slugs_res = chunks (map (length . slugNeutral) slugs) all_red_res -      forM_ (zip slugs slugs_res) $ \(slug, red_res) ->-        sLoopNest (slugShape slug) $ \vec_is -> do-        sComment "load accumulator" $-          forM_ (zip (accParams slug) (slugAccs slug)) $ \(p, (acc, acc_is)) ->-          copyDWIMFix (paramName p) [] (Var acc) (acc_is ++ vec_is)-        sComment "load new values" $-          forM_ (zip (nextParams slug) red_res) $ \(p, (res, res_is)) ->-          copyDWIMFix (paramName p) [] res (res_is ++ vec_is)-        sComment "apply reduction operator" $-          compileStms mempty (bodyStms $ slugBody slug) $-          sComment "store in accumulator" $-          forM_ (zip-                  (slugAccs slug)-                  (bodyResult $ slugBody slug)) $ \((acc, acc_is), se) ->-          copyDWIMFix acc (acc_is ++ vec_is) se []+          forM_ (zip slugs slugs_res) $ \(slug, red_res) ->+            sLoopNest (slugShape slug) $ \vec_is -> do+              sComment "load accumulator" $+                forM_ (zip (accParams slug) (slugAccs slug)) $ \(p, (acc, acc_is)) ->+                  copyDWIMFix (paramName p) [] (Var acc) (acc_is ++ vec_is)+              sComment "load new values" $+                forM_ (zip (nextParams slug) red_res) $ \(p, (res, res_is)) ->+                  copyDWIMFix (paramName p) [] res (res_is ++ vec_is)+              sComment "apply reduction operator" $+                compileStms mempty (bodyStms $ slugBody slug) $+                  sComment "store in accumulator" $+                    forM_+                      ( zip+                          (slugAccs slug)+                          (bodyResult $ slugBody slug)+                      )+                      $ \((acc, acc_is), se) ->+                        copyDWIMFix acc (acc_is ++ vec_is) se []      case comm of       Noncommutative -> do@@ -577,23 +701,24 @@         sComment "first thread keeps accumulator; others reset to neutral element" $ do           let reset_to_neutral =                 forM_ slugs $ \slug ->-                forM_ (zip (slugAccs slug) (slugNeutral slug)) $ \((acc, acc_is), ne) ->-                sLoopNest (slugShape slug) $ \vec_is ->-                copyDWIMFix acc (acc_is++vec_is) ne []+                  forM_ (zip (slugAccs slug) (slugNeutral slug)) $ \((acc, acc_is), ne) ->+                    sLoopNest (slugShape slug) $ \vec_is ->+                      copyDWIMFix acc (acc_is ++ vec_is) ne []           sUnless (local_tid .==. 0) reset_to_neutral       _ -> return ()    return (slugs_op_renamed, doTheReduction) -reductionStageOne :: KernelConstants-                  -> [(VName, Imp.Exp)]-                  -> Imp.Count Imp.Elements Imp.Exp-                  -> Imp.Exp-                  -> Imp.Count Imp.Elements Imp.Exp-                  -> VName-                  -> [SegBinOpSlug]-                  -> DoSegBody-                  -> InKernelGen [Lambda KernelsMem]+reductionStageOne ::+  KernelConstants ->+  [(VName, Imp.TExp Int64)] ->+  Imp.Count Imp.Elements (Imp.TExp Int64) ->+  Imp.TExp Int32 ->+  Imp.Count Imp.Elements (Imp.TExp Int64) ->+  VName ->+  [SegBinOpSlug] ->+  DoSegBody ->+  InKernelGen [Lambda KernelsMem] reductionStageOne constants ispace num_elements global_tid elems_per_thread threads_per_segment slugs body = do   (slugs_op_renamed, doTheReduction) <-     reductionStageZero constants ispace num_elements global_tid elems_per_thread threads_per_segment slugs body@@ -601,105 +726,146 @@   case slugsComm slugs of     Noncommutative ->       forM_ slugs $ \slug ->-      forM_ (zip (accParams slug) (slugAccs slug)) $ \(p, (acc, acc_is)) ->-      copyDWIMFix (paramName p) [] (Var acc) acc_is+        forM_ (zip (accParams slug) (slugAccs slug)) $ \(p, (acc, acc_is)) ->+          copyDWIMFix (paramName p) [] (Var acc) acc_is     _ -> doTheReduction    return slugs_op_renamed -reductionStageTwo :: KernelConstants-                  -> [PatElem KernelsMem]-                  -> Imp.Exp-                  -> Imp.Exp-                  -> [Imp.Exp]-                  -> Imp.Exp-                  -> Imp.Exp-                  -> SegBinOpSlug-                  -> [LParam KernelsMem] -> [LParam KernelsMem]-                  -> Lambda KernelsMem -> [SubExp]-                  -> Imp.Exp -> VName -> Imp.Exp -> VName -> [VName] -> [VName]-                  -> InKernelGen ()-reductionStageTwo constants segred_pes-                  group_id flat_segment_id segment_gtids first_group_for_segment groups_per_segment-                  slug red_x_params red_y_params-                  red_op_renamed nes-                  num_counters counter counter_i sync_arr group_res_arrs red_arrs = do-  let local_tid = kernelLocalThreadId constants-      group_size = kernelGroupSize constants-  old_counter <- dPrim "old_counter" int32-  (counter_mem, _, counter_offset) <- fullyIndexArray counter [counter_i * num_counters +-                                                               flat_segment_id `rem` num_counters]-  comment "first thread in group saves group result to global memory" $-    sWhen (local_tid .==. 0) $ do-    forM_ (take (length nes) $ zip group_res_arrs (slugAccs slug)) $ \(v, (acc, acc_is)) ->-      copyDWIMFix v [0, group_id] (Var acc) acc_is-    sOp $ Imp.MemFence Imp.FenceGlobal-    -- Increment the counter, thus stating that our result is-    -- available.-    sOp $ Imp.Atomic DefaultSpace $ Imp.AtomicAdd Int32 old_counter counter_mem counter_offset 1-    -- Now check if we were the last group to write our result.  If-    -- so, it is our responsibility to produce the final result.-    sWrite sync_arr [0] $ Imp.var old_counter int32 .==. groups_per_segment - 1--  sOp $ Imp.Barrier Imp.FenceGlobal+reductionStageTwo ::+  KernelConstants ->+  [PatElem KernelsMem] ->+  Imp.TExp Int32 ->+  Imp.TExp Int32 ->+  [Imp.TExp Int64] ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  SegBinOpSlug ->+  [LParam KernelsMem] ->+  [LParam KernelsMem] ->+  Lambda KernelsMem ->+  [SubExp] ->+  Imp.TExp Int32 ->+  VName ->+  Imp.TExp Int32 ->+  VName ->+  [VName] ->+  [VName] ->+  InKernelGen ()+reductionStageTwo+  constants+  segred_pes+  group_id+  flat_segment_id+  segment_gtids+  first_group_for_segment+  groups_per_segment+  slug+  red_x_params+  red_y_params+  red_op_renamed+  nes+  num_counters+  counter+  counter_i+  sync_arr+  group_res_arrs+  red_arrs = do+    let local_tid = kernelLocalThreadId constants+        group_size = kernelGroupSize constants+    old_counter <- dPrim "old_counter" int32+    (counter_mem, _, counter_offset) <-+      fullyIndexArray+        counter+        [ sExt64 $+            counter_i * num_counters+              + flat_segment_id `rem` num_counters+        ]+    comment "first thread in group saves group result to global memory" $+      sWhen (local_tid .==. 0) $ do+        forM_ (take (length nes) $ zip group_res_arrs (slugAccs slug)) $ \(v, (acc, acc_is)) ->+          copyDWIMFix v [0, sExt64 group_id] (Var acc) acc_is+        sOp $ Imp.MemFence Imp.FenceGlobal+        -- Increment the counter, thus stating that our result is+        -- available.+        sOp $+          Imp.Atomic DefaultSpace $+            Imp.AtomicAdd+              Int32+              (tvVar old_counter)+              counter_mem+              counter_offset+              $ untyped (1 :: Imp.TExp Int32)+        -- Now check if we were the last group to write our result.  If+        -- so, it is our responsibility to produce the final result.+        sWrite sync_arr [0] $ untyped $ tvExp old_counter .==. groups_per_segment - 1 -  is_last_group <- dPrim "is_last_group" Bool-  copyDWIMFix is_last_group [] (Var sync_arr) [0]-  sWhen (Imp.var is_last_group Bool) $ do-    -- The final group has written its result (and it was-    -- us!), so read in all the group results and perform the-    -- final stage of the reduction.  But first, we reset the-    -- counter so it is ready for next time.  This is done-    -- with an atomic to avoid warnings about write/write-    -- races in oclgrind.-    sWhen (local_tid .==. 0) $-      sOp $ Imp.Atomic DefaultSpace $-      Imp.AtomicAdd Int32 old_counter counter_mem counter_offset $-      negate groups_per_segment+    sOp $ Imp.Barrier Imp.FenceGlobal -    sLoopNest (slugShape slug) $ \vec_is -> do-      -- There is no guarantee that the number of workgroups for the-      -- segment is less than the workgroup size, so each thread may-      -- have to read multiple elements.  We do this in a sequential-      -- way that may induce non-coalesced accesses, but the total-      -- number of accesses should be tiny here.-      comment "read in the per-group-results" $ do-        read_per_thread <- dPrimVE "read_per_thread" $-                           groups_per_segment `divUp` group_size+    is_last_group <- dPrim "is_last_group" Bool+    copyDWIMFix (tvVar is_last_group) [] (Var sync_arr) [0]+    sWhen (tvExp is_last_group) $ do+      -- The final group has written its result (and it was+      -- us!), so read in all the group results and perform the+      -- final stage of the reduction.  But first, we reset the+      -- counter so it is ready for next time.  This is done+      -- with an atomic to avoid warnings about write/write+      -- races in oclgrind.+      sWhen (local_tid .==. 0) $+        sOp $+          Imp.Atomic DefaultSpace $+            Imp.AtomicAdd Int32 (tvVar old_counter) counter_mem counter_offset $+              untyped $ negate groups_per_segment -        forM_ (zip red_x_params nes) $ \(p, ne) ->-          copyDWIMFix (paramName p) [] ne []+      sLoopNest (slugShape slug) $ \vec_is -> do+        -- There is no guarantee that the number of workgroups for the+        -- segment is less than the workgroup size, so each thread may+        -- have to read multiple elements.  We do this in a sequential+        -- way that may induce non-coalesced accesses, but the total+        -- number of accesses should be tiny here.+        comment "read in the per-group-results" $ do+          read_per_thread <-+            dPrimVE "read_per_thread" $+              groups_per_segment `divUp` sExt64 group_size -        sFor "i" read_per_thread $ \i -> do+          forM_ (zip red_x_params nes) $ \(p, ne) ->+            copyDWIMFix (paramName p) [] ne [] -          group_res_id <- dPrimVE "group_res_id" $-                          local_tid * read_per_thread + i-          index_of_group_res <- dPrimVE "index_of_group_res" $-                                first_group_for_segment + group_res_id+          sFor "i" read_per_thread $ \i -> do+            group_res_id <-+              dPrimVE "group_res_id" $+                sExt64 local_tid * read_per_thread + i+            index_of_group_res <-+              dPrimVE "index_of_group_res" $+                first_group_for_segment + group_res_id -          sWhen (group_res_id .<. groups_per_segment) $ do-            forM_ (zip red_y_params group_res_arrs) $-              \(p, group_res_arr) ->-                copyDWIMFix (paramName p) []-                (Var group_res_arr)-                ([0, index_of_group_res] ++ vec_is)+            sWhen (group_res_id .<. groups_per_segment) $ do+              forM_ (zip red_y_params group_res_arrs) $+                \(p, group_res_arr) ->+                  copyDWIMFix+                    (paramName p)+                    []+                    (Var group_res_arr)+                    ([0, index_of_group_res] ++ vec_is) -            compileStms mempty (bodyStms $ slugBody slug) $-              forM_ (zip red_x_params (bodyResult $ slugBody slug)) $ \(p, se) ->-              copyDWIMFix (paramName p) [] se []+              compileStms mempty (bodyStms $ slugBody slug) $+                forM_ (zip red_x_params (bodyResult $ slugBody slug)) $ \(p, se) ->+                  copyDWIMFix (paramName p) [] se [] -      forM_ (zip red_x_params red_arrs) $ \(p, arr) ->-        when (primType $ paramType p) $-        copyDWIMFix arr [local_tid] (Var $ paramName p) []+        forM_ (zip red_x_params red_arrs) $ \(p, arr) ->+          when (primType $ paramType p) $+            copyDWIMFix arr [sExt64 local_tid] (Var $ paramName p) [] -      sOp $ Imp.Barrier Imp.FenceLocal+        sOp $ Imp.Barrier Imp.FenceLocal -      sComment "reduce the per-group results" $ do-        groupReduce group_size red_op_renamed red_arrs+        sComment "reduce the per-group results" $ do+          groupReduce (sExt32 group_size) red_op_renamed red_arrs -        sComment "and back to memory with the final result" $-          sWhen (local_tid .==. 0) $-          forM_ (zip segred_pes $ lambdaParams red_op_renamed) $ \(pe, p) ->-          copyDWIMFix-          (patElemName pe) (segment_gtids++vec_is)-          (Var $ paramName p) []+          sComment "and back to memory with the final result" $+            sWhen (local_tid .==. 0) $+              forM_ (zip segred_pes $ lambdaParams red_op_renamed) $ \(pe, p) ->+                copyDWIMFix+                  (patElemName pe)+                  (segment_gtids ++ vec_is)+                  (Var $ paramName p)+                  []
src/Futhark/CodeGen/ImpGen/Kernels/SegScan.hs view
@@ -1,86 +1,89 @@-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+ -- | Code generation for segmented and non-segmented scans.  Uses a -- fairly inefficient two-pass algorithm.-module Futhark.CodeGen.ImpGen.Kernels.SegScan-  ( compileSegScan )-  where+module Futhark.CodeGen.ImpGen.Kernels.SegScan (compileSegScan) where  import Control.Monad.Except import Control.Monad.State-import Data.Maybe import Data.List (delete, find, foldl', zip4)--import Prelude hiding (quot, rem)--import Futhark.Transform.Rename-import Futhark.IR.KernelsMem+import Data.Maybe import qualified Futhark.CodeGen.ImpCode.Kernels as Imp import Futhark.CodeGen.ImpGen import Futhark.CodeGen.ImpGen.Kernels.Base+import Futhark.IR.KernelsMem import qualified Futhark.IR.Mem.IxFun as IxFun-import Futhark.Util.IntegralExp (divUp, quot, rem)+import Futhark.Transform.Rename import Futhark.Util (takeLast)+import Futhark.Util.IntegralExp (divUp, quot, rem)+import Prelude hiding (quot, rem)  -- Aggressively try to reuse memory for different SegBinOps, because -- we will run them sequentially after another.-makeLocalArrays :: Count GroupSize SubExp -> SubExp -> [SegBinOp KernelsMem]-                -> InKernelGen [[VName]]+makeLocalArrays ::+  Count GroupSize SubExp ->+  SubExp ->+  [SegBinOp KernelsMem] ->+  InKernelGen [[VName]] makeLocalArrays (Count group_size) num_threads scans = do   (arrs, mems_and_sizes) <- runStateT (mapM onScan scans) mempty-  let maxSize sizes =-        Imp.bytes $ foldl' (Imp.BinOpExp (SMax Int32)) 1 $-        map Imp.unCount sizes+  let maxSize sizes = Imp.bytes $ foldl' sMax64 1 $ map Imp.unCount sizes   forM_ mems_and_sizes $ \(sizes, mem) ->     sAlloc_ mem (maxSize sizes) (Space "local")   return arrs--  where onScan (SegBinOp _ scan_op nes _) = do-          let (scan_x_params, _scan_y_params) =-                splitAt (length nes) $ lambdaParams scan_op-          (arrs, used_mems) <- fmap unzip $ forM scan_x_params $ \p ->-            case paramDec p of-              MemArray pt shape _ (ArrayIn mem _) -> do-                let shape' = Shape [num_threads] <> shape-                arr <- lift $ sArray "scan_arr" pt shape' $-                  ArrayIn mem $ IxFun.iota $ map (primExpFromSubExp int32) $ shapeDims shape'-                return (arr, [])-              _ -> do-                let pt = elemType $ paramType p-                    shape = Shape [group_size]-                (sizes, mem') <- getMem pt shape-                arr <- lift $ sArrayInMem "scan_arr" pt shape mem'-                return (arr, [(sizes, mem')])-          modify (<>concat used_mems)-          return arrs+  where+    onScan (SegBinOp _ scan_op nes _) = do+      let (scan_x_params, _scan_y_params) =+            splitAt (length nes) $ lambdaParams scan_op+      (arrs, used_mems) <- fmap unzip $+        forM scan_x_params $ \p ->+          case paramDec p of+            MemArray pt shape _ (ArrayIn mem _) -> do+              let shape' = Shape [num_threads] <> shape+              arr <-+                lift $+                  sArray "scan_arr" pt shape' $+                    ArrayIn mem $ IxFun.iota $ map pe64 $ shapeDims shape'+              return (arr, [])+            _ -> do+              let pt = elemType $ paramType p+                  shape = Shape [group_size]+              (sizes, mem') <- getMem pt shape+              arr <- lift $ sArrayInMem "scan_arr" pt shape mem'+              return (arr, [(sizes, mem')])+      modify (<> concat used_mems)+      return arrs -        getMem pt shape = do-          let size = typeSize $ Array pt shape NoUniqueness-          mems <- get-          case (find ((size `elem`) . fst) mems, mems) of-            (Just mem, _) -> do-              modify $ delete mem-              return mem-            (Nothing, (size', mem) : mems') -> do-              put mems'-              return (size : size', mem)-            (Nothing, []) -> do-              mem <- lift $ sDeclareMem "scan_arr_mem" $ Space "local"-              return ([size], mem)+    getMem pt shape = do+      let size = typeSize $ Array pt shape NoUniqueness+      mems <- get+      case (find ((size `elem`) . fst) mems, mems) of+        (Just mem, _) -> do+          modify $ delete mem+          return mem+        (Nothing, (size', mem) : mems') -> do+          put mems'+          return (size : size', mem)+        (Nothing, []) -> do+          mem <- lift $ sDeclareMem "scan_arr_mem" $ Space "local"+          return ([size], mem) -type CrossesSegment = Maybe (Imp.Exp -> Imp.Exp -> Imp.Exp)+type CrossesSegment = Maybe (Imp.TExp Int64 -> Imp.TExp Int64 -> Imp.TExp Bool) -localArrayIndex :: KernelConstants -> Type -> Imp.Exp+localArrayIndex :: KernelConstants -> Type -> Imp.TExp Int64 localArrayIndex constants t =   if primType t-  then kernelLocalThreadId constants-  else kernelGlobalThreadId constants+    then sExt64 (kernelLocalThreadId constants)+    else sExt64 (kernelGlobalThreadId constants)  barrierFor :: Lambda KernelsMem -> (Bool, Imp.Fence, InKernelGen ()) barrierFor scan_op = (array_scan, fence, sOp $ Imp.Barrier fence)-  where array_scan = not $ all primType $ lambdaReturnType scan_op-        fence | array_scan = Imp.FenceGlobal-              | otherwise = Imp.FenceLocal+  where+    array_scan = not $ all primType $ lambdaReturnType scan_op+    fence+      | array_scan = Imp.FenceGlobal+      | otherwise = Imp.FenceLocal  xParams, yParams :: SegBinOp KernelsMem -> [LParam KernelsMem] xParams scan =@@ -88,72 +91,88 @@ yParams scan =   drop (length (segBinOpNeutral scan)) (lambdaParams (segBinOpLambda scan)) -writeToScanValues :: [VName]-                  -> ([PatElem KernelsMem], SegBinOp KernelsMem, [KernelResult])-                  -> InKernelGen ()+writeToScanValues ::+  [VName] ->+  ([PatElem KernelsMem], SegBinOp KernelsMem, [KernelResult]) ->+  InKernelGen () writeToScanValues gtids (pes, scan, scan_res)   | shapeRank (segBinOpShape scan) > 0 =-      forM_ (zip pes scan_res) $ \(pe, res) ->-      copyDWIMFix (patElemName pe) (map Imp.vi32 gtids)-      (kernelResultSubExp res) []+    forM_ (zip pes scan_res) $ \(pe, res) ->+      copyDWIMFix+        (patElemName pe)+        (map Imp.vi64 gtids)+        (kernelResultSubExp res)+        []   | otherwise =-      forM_ (zip (yParams scan) scan_res) $ \(p, res) ->+    forM_ (zip (yParams scan) scan_res) $ \(p, res) ->       copyDWIMFix (paramName p) [] (kernelResultSubExp res) [] -readToScanValues :: [Imp.Exp] -> [PatElem KernelsMem] -> SegBinOp KernelsMem-                 -> InKernelGen ()+readToScanValues ::+  [Imp.TExp Int64] ->+  [PatElem KernelsMem] ->+  SegBinOp KernelsMem ->+  InKernelGen () readToScanValues is pes scan   | shapeRank (segBinOpShape scan) > 0 =-      forM_ (zip (yParams scan) pes) $ \(p, pe) ->+    forM_ (zip (yParams scan) pes) $ \(p, pe) ->       copyDWIMFix (paramName p) [] (Var (patElemName pe)) is   | otherwise =-      return ()+    return () -readCarries :: Imp.Exp -> [Imp.Exp] -> [Imp.Exp]-            -> [PatElem KernelsMem]-            -> SegBinOp KernelsMem-            -> InKernelGen ()+readCarries ::+  Imp.TExp Int64 ->+  [Imp.TExp Int64] ->+  [Imp.TExp Int64] ->+  [PatElem KernelsMem] ->+  SegBinOp KernelsMem ->+  InKernelGen () readCarries chunk_offset dims' vec_is pes scan   | shapeRank (segBinOpShape scan) > 0 = do-      ltid <- kernelLocalThreadId . kernelConstants <$> askEnv-      -- We may have to reload the carries from the output of the-      -- previous chunk.-      sIf (chunk_offset .>. 0 .&&. ltid .==. 0)-        (do let is = unflattenIndex dims' $ chunk_offset - 1-            forM_ (zip (xParams scan) pes) $ \(p, pe) ->-              copyDWIMFix (paramName p) [] (Var (patElemName pe)) (is++vec_is))-        (forM_ (zip (xParams scan) (segBinOpNeutral scan)) $ \(p, ne) ->-            copyDWIMFix (paramName p) [] ne [])+    ltid <- kernelLocalThreadId . kernelConstants <$> askEnv+    -- We may have to reload the carries from the output of the+    -- previous chunk.+    sIf+      (chunk_offset .>. 0 .&&. ltid .==. 0)+      ( do+          let is = unflattenIndex dims' $ chunk_offset - 1+          forM_ (zip (xParams scan) pes) $ \(p, pe) ->+            copyDWIMFix (paramName p) [] (Var (patElemName pe)) (is ++ vec_is)+      )+      ( forM_ (zip (xParams scan) (segBinOpNeutral scan)) $ \(p, ne) ->+          copyDWIMFix (paramName p) [] ne []+      )   | otherwise =-      return ()+    return ()  -- | Produce partially scanned intervals; one per workgroup.-scanStage1 :: Pattern KernelsMem-           -> Count NumGroups SubExp -> Count GroupSize SubExp -> SegSpace-           -> [SegBinOp KernelsMem]-           -> KernelBody KernelsMem-           -> CallKernelGen (VName, Imp.Exp, CrossesSegment)+scanStage1 ::+  Pattern KernelsMem ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  SegSpace ->+  [SegBinOp KernelsMem] ->+  KernelBody KernelsMem ->+  CallKernelGen (TV Int32, Imp.TExp Int64, CrossesSegment) scanStage1 (Pattern _ all_pes) num_groups group_size space scans kbody = do-  num_groups' <- traverse toExp num_groups-  group_size' <- traverse toExp group_size-  num_threads <- dPrimV "num_threads" $-                 unCount num_groups' * unCount group_size'+  let num_groups' = fmap toInt64Exp num_groups+      group_size' = fmap toInt64Exp group_size+  num_threads <- dPrimV "num_threads" $ sExt32 $ unCount num_groups' * unCount group_size'    let (gtids, dims) = unzip $ unSegSpace space-  dims' <- mapM toExp dims+      dims' = map toInt64Exp dims   let num_elements = product dims'-      elems_per_thread = num_elements `divUp` Imp.vi32 num_threads+      elems_per_thread = num_elements `divUp` sExt64 (tvExp num_threads)       elems_per_group = unCount group_size' * elems_per_thread    let crossesSegment =         case reverse dims' of           segment_size : _ : _ -> Just $ \from to ->-            (to-from) .>. (to `rem` segment_size)+            (to - from) .>. (to `rem` segment_size)           _ -> Nothing    sKernelThread "scan_stage1" num_groups' group_size' (segFlat space) $ do     constants <- kernelConstants <$> askEnv-    all_local_arrs <- makeLocalArrays group_size (Var num_threads) scans+    all_local_arrs <- makeLocalArrays group_size (tvSize num_threads) scans      -- The variables from scan_op will be used for the carry and such     -- in the big chunking loop.@@ -163,18 +182,20 @@         copyDWIMFix (paramName p) [] ne []      sFor "j" elems_per_thread $ \j -> do-      chunk_offset <- dPrimV "chunk_offset" $-                      kernelGroupSize constants * j +-                      kernelGroupId constants * elems_per_group-      flat_idx <- dPrimV "flat_idx" $-                  Imp.var chunk_offset int32 + kernelLocalThreadId constants+      chunk_offset <-+        dPrimV "chunk_offset" $+          sExt64 (kernelGroupSize constants) * j+            + sExt64 (kernelGroupId constants) * elems_per_group+      flat_idx <-+        dPrimV "flat_idx" $+          tvExp chunk_offset + sExt64 (kernelLocalThreadId constants)       -- Construct segment indices.-      zipWithM_ dPrimV_ gtids $ unflattenIndex dims' $ Imp.vi32 flat_idx+      zipWithM_ dPrimV_ gtids $ unflattenIndex dims' $ tvExp flat_idx        let per_scan_pes = segBinOpChunks scans all_pes            in_bounds =-            foldl1 (.&&.) $ zipWith (.<.) (map Imp.vi32 gtids) dims'+            foldl1 (.&&.) $ zipWith (.<.) (map Imp.vi64 gtids) dims'            when_in_bounds = compileStms mempty (kernelBodyStms kbody) $ do             let (all_scan_res, map_res) =@@ -184,241 +205,306 @@              sComment "write to-scan values to parameters" $               mapM_ (writeToScanValues gtids) $-              zip3 per_scan_pes scans per_scan_res+                zip3 per_scan_pes scans per_scan_res              sComment "write mapped values results to global memory" $               forM_ (zip (takeLast (length map_res) all_pes) map_res) $ \(pe, se) ->-              copyDWIMFix (patElemName pe) (map Imp.vi32 gtids)-              (kernelResultSubExp se) []+                copyDWIMFix+                  (patElemName pe)+                  (map Imp.vi64 gtids)+                  (kernelResultSubExp se)+                  []        sComment "threads in bounds read input" $         sWhen in_bounds when_in_bounds        forM_ (zip3 per_scan_pes scans all_local_arrs) $         \(pes, scan@(SegBinOp _ scan_op nes vec_shape), local_arrs) ->-        sComment "do one intra-group scan operation" $ do-        let rets = lambdaReturnType scan_op-            scan_x_params = xParams scan-            (array_scan, fence, barrier) = barrierFor scan_op+          sComment "do one intra-group scan operation" $ do+            let rets = lambdaReturnType scan_op+                scan_x_params = xParams scan+                (array_scan, fence, barrier) = barrierFor scan_op -        when array_scan barrier+            when array_scan barrier -        sLoopNest vec_shape $ \vec_is -> do-          sComment "maybe restore some to-scan values to parameters, or read neutral" $-            sIf in_bounds-            (do readToScanValues (map Imp.vi32 gtids++vec_is) pes scan-                readCarries (Imp.vi32 chunk_offset) dims' vec_is pes scan)-            (forM_ (zip (yParams scan) (segBinOpNeutral scan)) $ \(p, ne) ->-                copyDWIMFix (paramName p) [] ne [])+            sLoopNest vec_shape $ \vec_is -> do+              sComment "maybe restore some to-scan values to parameters, or read neutral" $+                sIf+                  in_bounds+                  ( do+                      readToScanValues (map Imp.vi64 gtids ++ vec_is) pes scan+                      readCarries (tvExp chunk_offset) dims' vec_is pes scan+                  )+                  ( forM_ (zip (yParams scan) (segBinOpNeutral scan)) $ \(p, ne) ->+                      copyDWIMFix (paramName p) [] ne []+                  ) -          sComment "combine with carry and write to local memory" $-            compileStms mempty (bodyStms $ lambdaBody scan_op) $-            forM_ (zip3 rets local_arrs (bodyResult $ lambdaBody scan_op)) $-            \(t, arr, se) -> copyDWIMFix arr [localArrayIndex constants t] se []+              sComment "combine with carry and write to local memory" $+                compileStms mempty (bodyStms $ lambdaBody scan_op) $+                  forM_ (zip3 rets local_arrs (bodyResult $ lambdaBody scan_op)) $+                    \(t, arr, se) ->+                      copyDWIMFix arr [localArrayIndex constants t] se [] -          let crossesSegment' = do-                f <- crossesSegment-                Just $ \from to ->-                  let from' = from + Imp.var chunk_offset int32-                      to' = to + Imp.var chunk_offset int32-                  in f from' to'+              let crossesSegment' = do+                    f <- crossesSegment+                    Just $ \from to ->+                      let from' = sExt64 from + tvExp chunk_offset+                          to' = sExt64 to + tvExp chunk_offset+                       in f from' to' -          sOp $ Imp.ErrorSync fence+              sOp $ Imp.ErrorSync fence -          -- We need to avoid parameter name clashes.-          scan_op_renamed <- renameLambda scan_op-          groupScan crossesSegment'-            (Imp.vi32 num_threads)-            (kernelGroupSize constants) scan_op_renamed local_arrs+              -- We need to avoid parameter name clashes.+              scan_op_renamed <- renameLambda scan_op+              groupScan+                crossesSegment'+                (sExt64 $ tvExp num_threads)+                (sExt64 $ kernelGroupSize constants)+                scan_op_renamed+                local_arrs -          sComment "threads in bounds write partial scan result" $-            sWhen in_bounds $ forM_ (zip3 rets pes local_arrs) $ \(t, pe, arr) ->-            copyDWIMFix (patElemName pe) (map Imp.vi32 gtids++vec_is)-            (Var arr) [localArrayIndex constants t]+              sComment "threads in bounds write partial scan result" $+                sWhen in_bounds $+                  forM_ (zip3 rets pes local_arrs) $ \(t, pe, arr) ->+                    copyDWIMFix+                      (patElemName pe)+                      (map Imp.vi64 gtids ++ vec_is)+                      (Var arr)+                      [localArrayIndex constants t] -          barrier+              barrier -          let load_carry =-                forM_ (zip local_arrs scan_x_params) $ \(arr, p) ->-                copyDWIMFix (paramName p) [] (Var arr)-                [if primType $ paramType p-                 then kernelGroupSize constants - 1-                 else (kernelGroupId constants+1) * kernelGroupSize constants - 1]-              load_neutral =-                forM_ (zip nes scan_x_params) $ \(ne, p) ->-                copyDWIMFix (paramName p) [] ne []+              let load_carry =+                    forM_ (zip local_arrs scan_x_params) $ \(arr, p) ->+                      copyDWIMFix+                        (paramName p)+                        []+                        (Var arr)+                        [ if primType $ paramType p+                            then sExt64 (kernelGroupSize constants) - 1+                            else+                              (sExt64 (kernelGroupId constants) + 1)+                                * sExt64 (kernelGroupSize constants) - 1+                        ]+                  load_neutral =+                    forM_ (zip nes scan_x_params) $ \(ne, p) ->+                      copyDWIMFix (paramName p) [] ne [] -          sComment "first thread reads last element as carry-in for next iteration" $ do-            crosses_segment <- dPrimVE "crosses_segment" $-              case crossesSegment of-                Nothing -> false-                Just f -> f (Imp.var chunk_offset int32 +-                             kernelGroupSize constants-1)-                            (Imp.var chunk_offset int32 +-                             kernelGroupSize constants)-            should_load_carry <- dPrimVE "should_load_carry" $-              kernelLocalThreadId constants .==. 0 .&&. UnOpExp Not crosses_segment-            sWhen should_load_carry load_carry-            when array_scan barrier-            sUnless should_load_carry load_neutral+              sComment "first thread reads last element as carry-in for next iteration" $ do+                crosses_segment <- dPrimVE "crosses_segment" $+                  case crossesSegment of+                    Nothing -> false+                    Just f ->+                      f+                        ( tvExp chunk_offset+                            + sExt64 (kernelGroupSize constants) -1+                        )+                        ( tvExp chunk_offset+                            + sExt64 (kernelGroupSize constants)+                        )+                should_load_carry <-+                  dPrimVE "should_load_carry" $+                    kernelLocalThreadId constants .==. 0 .&&. bNot crosses_segment+                sWhen should_load_carry load_carry+                when array_scan barrier+                sUnless should_load_carry load_neutral -          barrier+              barrier    return (num_threads, elems_per_group, crossesSegment) -scanStage2 :: Pattern KernelsMem-           -> VName -> Imp.Exp -> Count NumGroups SubExp -> CrossesSegment -> SegSpace-           -> [SegBinOp KernelsMem]-           -> CallKernelGen ()+scanStage2 ::+  Pattern KernelsMem ->+  TV Int32 ->+  Imp.TExp Int64 ->+  Count NumGroups SubExp ->+  CrossesSegment ->+  SegSpace ->+  [SegBinOp KernelsMem] ->+  CallKernelGen () scanStage2 (Pattern _ all_pes) stage1_num_threads elems_per_group num_groups crossesSegment space scans = do   let (gtids, dims) = unzip $ unSegSpace space-  dims' <- mapM toExp dims+      dims' = map toInt64Exp dims    -- Our group size is the number of groups for the stage 1 kernel.   let group_size = Count $ unCount num_groups-  group_size' <- traverse toExp group_size+      group_size' = fmap toInt64Exp group_size    let crossesSegment' = do         f <- crossesSegment         Just $ \from to ->-          f ((from + 1) * elems_per_group - 1) ((to + 1) * elems_per_group - 1)+          f+            ((sExt64 from + 1) * elems_per_group - 1)+            ((sExt64 to + 1) * elems_per_group - 1) -  sKernelThread  "scan_stage2" 1 group_size' (segFlat space) $ do+  sKernelThread "scan_stage2" 1 group_size' (segFlat space) $ do     constants <- kernelConstants <$> askEnv-    per_scan_local_arrs <- makeLocalArrays group_size (Var stage1_num_threads) scans+    per_scan_local_arrs <- makeLocalArrays group_size (tvSize stage1_num_threads) scans     let per_scan_rets = map (lambdaReturnType . segBinOpLambda) scans         per_scan_pes = segBinOpChunks scans all_pes -    flat_idx <- dPrimV "flat_idx" $-      (kernelLocalThreadId constants + 1) * elems_per_group - 1+    flat_idx <-+      dPrimV "flat_idx" $+        (sExt64 (kernelLocalThreadId constants) + 1) * elems_per_group - 1     -- Construct segment indices.-    zipWithM_ dPrimV_ gtids $ unflattenIndex dims' $ Imp.var flat_idx int32+    zipWithM_ dPrimV_ gtids $ unflattenIndex dims' $ tvExp flat_idx      forM_ (zip4 scans per_scan_local_arrs per_scan_rets per_scan_pes) $       \(SegBinOp _ scan_op nes vec_shape, local_arrs, rets, pes) ->         sLoopNest vec_shape $ \vec_is -> do-        let glob_is = map Imp.vi32 gtids ++ vec_is+          let glob_is = map Imp.vi64 gtids ++ vec_is -            in_bounds =-              foldl1 (.&&.) $ zipWith (.<.) (map Imp.vi32 gtids) dims'+              in_bounds =+                foldl1 (.&&.) $ zipWith (.<.) (map Imp.vi64 gtids) dims' -            when_in_bounds = forM_ (zip3 rets local_arrs pes) $ \(t, arr, pe) ->-              copyDWIMFix arr [localArrayIndex constants t]-              (Var $ patElemName pe) glob_is+              when_in_bounds = forM_ (zip3 rets local_arrs pes) $ \(t, arr, pe) ->+                copyDWIMFix+                  arr+                  [localArrayIndex constants t]+                  (Var $ patElemName pe)+                  glob_is -            when_out_of_bounds = forM_ (zip3 rets local_arrs nes) $ \(t, arr, ne) ->-              copyDWIMFix arr [localArrayIndex constants t] ne []-            (_, _, barrier) =-              barrierFor scan_op+              when_out_of_bounds = forM_ (zip3 rets local_arrs nes) $ \(t, arr, ne) ->+                copyDWIMFix arr [localArrayIndex constants t] ne []+              (_, _, barrier) =+                barrierFor scan_op -        sComment "threads in bound read carries; others get neutral element" $-          sIf in_bounds when_in_bounds when_out_of_bounds+          sComment "threads in bound read carries; others get neutral element" $+            sIf in_bounds when_in_bounds when_out_of_bounds -        barrier+          barrier -        groupScan crossesSegment'-          (Imp.vi32 stage1_num_threads) (kernelGroupSize constants) scan_op local_arrs+          groupScan+            crossesSegment'+            (sExt64 $ tvExp stage1_num_threads)+            (sExt64 $ kernelGroupSize constants)+            scan_op+            local_arrs -        sComment "threads in bounds write scanned carries" $-          sWhen in_bounds $ forM_ (zip3 rets pes local_arrs) $ \(t, pe, arr) ->-          copyDWIMFix (patElemName pe) glob_is-          (Var arr) [localArrayIndex constants t]+          sComment "threads in bounds write scanned carries" $+            sWhen in_bounds $+              forM_ (zip3 rets pes local_arrs) $ \(t, pe, arr) ->+                copyDWIMFix+                  (patElemName pe)+                  glob_is+                  (Var arr)+                  [localArrayIndex constants t] -scanStage3 :: Pattern KernelsMem-           -> Count NumGroups SubExp -> Count GroupSize SubExp-           -> Imp.Exp -> CrossesSegment -> SegSpace-           -> [SegBinOp KernelsMem]-           -> CallKernelGen ()+scanStage3 ::+  Pattern KernelsMem ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  Imp.TExp Int64 ->+  CrossesSegment ->+  SegSpace ->+  [SegBinOp KernelsMem] ->+  CallKernelGen () scanStage3 (Pattern _ all_pes) num_groups group_size elems_per_group crossesSegment space scans = do-  num_groups' <- traverse toExp num_groups-  group_size' <- traverse toExp group_size-  let (gtids, dims) = unzip $ unSegSpace space-  dims' <- mapM toExp dims-  required_groups <- dPrimVE "required_groups" $-                     product dims' `divUp` unCount group_size'+  let num_groups' = fmap toInt64Exp num_groups+      group_size' = fmap toInt64Exp group_size+      (gtids, dims) = unzip $ unSegSpace space+      dims' = map toInt64Exp dims+  required_groups <-+    dPrimVE "required_groups" $+      sExt32 $ product dims' `divUp` sExt64 (unCount group_size')    sKernelThread "scan_stage3" num_groups' group_size' (segFlat space) $     virtualiseGroups SegVirt required_groups $ \virt_group_id -> do-    constants <- kernelConstants <$> askEnv+      constants <- kernelConstants <$> askEnv -    -- Compute our logical index.-    flat_idx <- dPrimVE "flat_idx" $-                Imp.vi32 virt_group_id * unCount group_size' +-                kernelLocalThreadId constants-    zipWithM_ dPrimV_ gtids $ unflattenIndex dims' flat_idx+      -- Compute our logical index.+      flat_idx <-+        dPrimVE "flat_idx" $+          sExt64 virt_group_id * sExt64 (unCount group_size')+            + sExt64 (kernelLocalThreadId constants)+      zipWithM_ dPrimV_ gtids $ unflattenIndex dims' flat_idx -    -- Figure out which group this element was originally in.-    orig_group <- dPrimV "orig_group" $ flat_idx `quot` elems_per_group-    -- Then the index of the carry-in of the preceding group.-    carry_in_flat_idx <- dPrimV "carry_in_flat_idx" $-                         Imp.var orig_group int32 * elems_per_group - 1-    -- Figure out the logical index of the carry-in.-    let carry_in_idx = unflattenIndex dims' $ Imp.var carry_in_flat_idx int32+      -- Figure out which group this element was originally in.+      orig_group <- dPrimV "orig_group" $ flat_idx `quot` elems_per_group+      -- Then the index of the carry-in of the preceding group.+      carry_in_flat_idx <-+        dPrimV "carry_in_flat_idx" $+          tvExp orig_group * elems_per_group - 1+      -- Figure out the logical index of the carry-in.+      let carry_in_idx = unflattenIndex dims' $ tvExp carry_in_flat_idx -    -- Apply the carry if we are not in the scan results for the first-    -- group, and are not the last element in such a group (because-    -- then the carry was updated in stage 2), and we are not crossing-    -- a segment boundary.-    let in_bounds =-          foldl1 (.&&.) $ zipWith (.<.) (map Imp.vi32 gtids) dims'-        crosses_segment = fromMaybe false $-          crossesSegment <*>-            pure (Imp.var carry_in_flat_idx int32) <*>-            pure flat_idx-        is_a_carry = flat_idx .==.-                     (Imp.var orig_group int32 + 1) * elems_per_group - 1-        no_carry_in = Imp.var orig_group int32 .==. 0 .||. is_a_carry .||. crosses_segment+      -- Apply the carry if we are not in the scan results for the first+      -- group, and are not the last element in such a group (because+      -- then the carry was updated in stage 2), and we are not crossing+      -- a segment boundary.+      let in_bounds =+            foldl1 (.&&.) $ zipWith (.<.) (map Imp.vi64 gtids) dims'+          crosses_segment =+            fromMaybe false $+              crossesSegment+                <*> pure (tvExp carry_in_flat_idx)+                <*> pure flat_idx+          is_a_carry = flat_idx .==. (tvExp orig_group + 1) * elems_per_group - 1+          no_carry_in = tvExp orig_group .==. 0 .||. is_a_carry .||. crosses_segment -    let per_scan_pes = segBinOpChunks scans all_pes-    sWhen in_bounds $ sUnless no_carry_in $-      forM_ (zip per_scan_pes scans) $-      \(pes, SegBinOp _ scan_op nes vec_shape) -> do-        dScope Nothing $ scopeOfLParams $ lambdaParams scan_op-        let (scan_x_params, scan_y_params) =-              splitAt (length nes) $ lambdaParams scan_op+      let per_scan_pes = segBinOpChunks scans all_pes+      sWhen in_bounds $+        sUnless no_carry_in $+          forM_ (zip per_scan_pes scans) $+            \(pes, SegBinOp _ scan_op nes vec_shape) -> do+              dScope Nothing $ scopeOfLParams $ lambdaParams scan_op+              let (scan_x_params, scan_y_params) =+                    splitAt (length nes) $ lambdaParams scan_op -        sLoopNest vec_shape $ \vec_is -> do-          forM_ (zip scan_x_params pes) $ \(p, pe) ->-            copyDWIMFix (paramName p) []-            (Var $ patElemName pe) (carry_in_idx++vec_is)+              sLoopNest vec_shape $ \vec_is -> do+                forM_ (zip scan_x_params pes) $ \(p, pe) ->+                  copyDWIMFix+                    (paramName p)+                    []+                    (Var $ patElemName pe)+                    (carry_in_idx ++ vec_is) -          forM_ (zip scan_y_params pes) $ \(p, pe) ->-            copyDWIMFix (paramName p) []-            (Var $ patElemName pe) (map Imp.vi32 gtids++vec_is)+                forM_ (zip scan_y_params pes) $ \(p, pe) ->+                  copyDWIMFix+                    (paramName p)+                    []+                    (Var $ patElemName pe)+                    (map Imp.vi64 gtids ++ vec_is) -          compileBody' scan_x_params $ lambdaBody scan_op+                compileBody' scan_x_params $ lambdaBody scan_op -          forM_ (zip scan_x_params pes) $ \(p, pe) ->-            copyDWIMFix (patElemName pe) (map Imp.vi32 gtids++vec_is)-            (Var $ paramName p) []+                forM_ (zip scan_x_params pes) $ \(p, pe) ->+                  copyDWIMFix+                    (patElemName pe)+                    (map Imp.vi64 gtids ++ vec_is)+                    (Var $ paramName p)+                    []  -- | Compile 'SegScan' instance to host-level code with calls to -- various kernels.-compileSegScan :: Pattern KernelsMem-               -> SegLevel -> SegSpace-               -> [SegBinOp KernelsMem]-               -> KernelBody KernelsMem-               -> CallKernelGen ()+compileSegScan ::+  Pattern KernelsMem ->+  SegLevel ->+  SegSpace ->+  [SegBinOp KernelsMem] ->+  KernelBody KernelsMem ->+  CallKernelGen () compileSegScan pat lvl space scans kbody = sWhen (0 .<. n) $ do   emit $ Imp.DebugPrint "\n# SegScan" Nothing    -- Since stage 2 involves a group size equal to the number of groups   -- used for stage 1, we have to cap this number to the maximum group   -- size.-  stage1_max_num_groups <--    dPrim "stage1_max_num_groups" int32-  sOp $ Imp.GetSizeMax stage1_max_num_groups SizeGroup+  stage1_max_num_groups <- dPrim "stage1_max_num_groups" int32+  sOp $ Imp.GetSizeMax (tvVar stage1_max_num_groups) SizeGroup    stage1_num_groups <--    fmap (Imp.Count . Var) $ dPrimV "stage1_num_groups" $-    Imp.BinOpExp (SMin Int32) (Imp.vi32 stage1_max_num_groups) $-    toExp' int32 $ Imp.unCount $ segNumGroups lvl+    fmap (Imp.Count . tvSize) $+      dPrimV "stage1_num_groups" $+        sMin32 (tvExp stage1_max_num_groups) $+          toInt32Exp $ Imp.unCount $ segNumGroups lvl    (stage1_num_threads, elems_per_group, crossesSegment) <-     scanStage1 pat stage1_num_groups (segGroupSize lvl) space scans kbody -  emit $ Imp.DebugPrint "elems_per_group" $ Just elems_per_group+  emit $ Imp.DebugPrint "elems_per_group" $ Just $ untyped elems_per_group    scanStage2 pat stage1_num_threads elems_per_group stage1_num_groups crossesSegment space scans   scanStage3 pat (segNumGroups lvl) (segGroupSize lvl) elems_per_group crossesSegment space scans-  where n = product $ map (toExp' int32) $ segSpaceDims space+  where+    n = product $ map toInt32Exp $ segSpaceDims space
src/Futhark/CodeGen/ImpGen/Kernels/ToOpenCL.hs view
@@ -1,26 +1,22 @@ {-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE TupleSections #-} {-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TupleSections #-}+ -- | This module defines a translation from imperative code with -- kernels to imperative code with OpenCL calls. module Futhark.CodeGen.ImpGen.Kernels.ToOpenCL-  ( kernelsToOpenCL-  , kernelsToCUDA+  ( kernelsToOpenCL,+    kernelsToCUDA,   )-  where+where +import Control.Monad.Identity import Control.Monad.Reader import Control.Monad.State-import Control.Monad.Identity import Data.FileEmbed+import qualified Data.Map.Strict as M import Data.Maybe import qualified Data.Set as S-import qualified Data.Map.Strict as M--import qualified Language.C.Syntax as C-import qualified Language.C.Quote.OpenCL as C-import qualified Language.C.Quote.CUDA as CUDAC- import qualified Futhark.CodeGen.Backends.GenericC as GC import Futhark.CodeGen.Backends.SimpleRep import Futhark.CodeGen.ImpCode.Kernels hiding (Program)@@ -32,91 +28,105 @@ import Futhark.MonadFreshNames import Futhark.Util (zEncodeString) import Futhark.Util.Pretty (prettyOneLine)+import qualified Language.C.Quote.CUDA as CUDAC+import qualified Language.C.Quote.OpenCL as C+import qualified Language.C.Syntax as C  kernelsToCUDA, kernelsToOpenCL :: ImpKernels.Program -> ImpOpenCL.Program kernelsToCUDA = translateKernels TargetCUDA kernelsToOpenCL = translateKernels TargetOpenCL  -- | Translate a kernels-program to an OpenCL-program.-translateKernels :: KernelTarget-                 -> ImpKernels.Program-                 -> ImpOpenCL.Program+translateKernels ::+  KernelTarget ->+  ImpKernels.Program ->+  ImpOpenCL.Program translateKernels target prog =-  let (prog',-       ToOpenCL kernels device_funs used_types sizes failures) =--        (`runState` initialOpenCL) . (`runReaderT` defFuns prog) $ do-          let ImpKernels.Definitions-                (ImpKernels.Constants ps consts)-                (ImpKernels.Functions funs) = prog-          consts' <- traverse (onHostOp target) consts-          funs' <- forM funs $ \(fname, fun) ->-            (fname,) <$> traverse (onHostOp target) fun+  let ( prog',+        ToOpenCL kernels device_funs used_types sizes failures+        ) =+          (`runState` initialOpenCL) . (`runReaderT` defFuns prog) $ do+            let ImpKernels.Definitions+                  (ImpKernels.Constants ps consts)+                  (ImpKernels.Functions funs) = prog+            consts' <- traverse (onHostOp target) consts+            funs' <- forM funs $ \(fname, fun) ->+              (fname,) <$> traverse (onHostOp target) fun -          return $ ImpOpenCL.Definitions-            (ImpOpenCL.Constants ps consts')-            (ImpOpenCL.Functions funs')+            return $+              ImpOpenCL.Definitions+                (ImpOpenCL.Constants ps consts')+                (ImpOpenCL.Functions funs')        (device_prototypes, device_defs) = unzip $ M.elems device_funs       kernels' = M.map fst kernels       opencl_code = openClCode $ map snd $ M.elems kernels        opencl_prelude =-        unlines [pretty $ genPrelude target used_types,-                 unlines $ map pretty device_prototypes,-                 unlines $ map pretty device_defs]--  in ImpOpenCL.Program opencl_code opencl_prelude kernels'-     (S.toList used_types) (cleanSizes sizes) failures prog'--  where genPrelude TargetOpenCL = genOpenClPrelude-        genPrelude TargetCUDA = const genCUDAPrelude+        unlines+          [ pretty $ genPrelude target used_types,+            unlines $ map pretty device_prototypes,+            unlines $ map pretty device_defs+          ]+   in ImpOpenCL.Program+        opencl_code+        opencl_prelude+        kernels'+        (S.toList used_types)+        (cleanSizes sizes)+        failures+        prog'+  where+    genPrelude TargetOpenCL = genOpenClPrelude+    genPrelude TargetCUDA = const genCUDAPrelude  -- | Due to simplifications after kernel extraction, some threshold -- parameters may contain KernelPaths that reference threshold -- parameters that no longer exist.  We remove these here. cleanSizes :: M.Map Name SizeClass -> M.Map Name SizeClass cleanSizes m = M.map clean m-  where known = M.keys m-        clean (SizeThreshold path def) =-          SizeThreshold (filter ((`elem` known) . fst) path) def-        clean s = s+  where+    known = M.keys m+    clean (SizeThreshold path def) =+      SizeThreshold (filter ((`elem` known) . fst) path) def+    clean s = s -pointerQuals ::  Monad m => String -> m [C.TypeQual]-pointerQuals "global"     = return [C.ctyquals|__global|]-pointerQuals "local"      = return [C.ctyquals|__local|]-pointerQuals "private"    = return [C.ctyquals|__private|]-pointerQuals "constant"   = return [C.ctyquals|__constant|]+pointerQuals :: Monad m => String -> m [C.TypeQual]+pointerQuals "global" = return [C.ctyquals|__global|]+pointerQuals "local" = return [C.ctyquals|__local|]+pointerQuals "private" = return [C.ctyquals|__private|]+pointerQuals "constant" = return [C.ctyquals|__constant|] pointerQuals "write_only" = return [C.ctyquals|__write_only|]-pointerQuals "read_only"  = return [C.ctyquals|__read_only|]-pointerQuals "kernel"     = return [C.ctyquals|__kernel|]-pointerQuals s            = error $ "'" ++ s ++ "' is not an OpenCL kernel address space."+pointerQuals "read_only" = return [C.ctyquals|__read_only|]+pointerQuals "kernel" = return [C.ctyquals|__kernel|]+pointerQuals s = error $ "'" ++ s ++ "' is not an OpenCL kernel address space."  -- In-kernel name and per-workgroup size in bytes. type LocalMemoryUse = (VName, Count Bytes Exp) -data KernelState =-  KernelState { kernelLocalMemory :: [LocalMemoryUse]-              , kernelFailures :: [FailureMsg]-              , kernelNextSync :: Int-              , kernelSyncPending :: Bool-                -- ^ Has a potential failure occurred sine the last-                -- ErrorSync?-              , kernelHasBarriers :: Bool-              }+data KernelState = KernelState+  { kernelLocalMemory :: [LocalMemoryUse],+    kernelFailures :: [FailureMsg],+    kernelNextSync :: Int,+    -- | Has a potential failure occurred sine the last+    -- ErrorSync?+    kernelSyncPending :: Bool,+    kernelHasBarriers :: Bool+  }  newKernelState :: [FailureMsg] -> KernelState newKernelState failures = KernelState mempty failures 0 False False  errorLabel :: KernelState -> String-errorLabel = ("error_"++) . show . kernelNextSync+errorLabel = ("error_" ++) . show . kernelNextSync -data ToOpenCL = ToOpenCL { clKernels :: M.Map KernelName (KernelSafety, C.Func)-                         , clDevFuns :: M.Map Name (C.Definition, C.Func)-                         , clUsedTypes :: S.Set PrimType-                         , clSizes :: M.Map Name SizeClass-                         , clFailures :: [FailureMsg]-                         }+data ToOpenCL = ToOpenCL+  { clKernels :: M.Map KernelName (KernelSafety, C.Func),+    clDevFuns :: M.Map Name (C.Definition, C.Func),+    clUsedTypes :: S.Set PrimType,+    clSizes :: M.Map Name SizeClass,+    clFailures :: [FailureMsg]+  }  initialOpenCL :: ToOpenCL initialOpenCL = ToOpenCL mempty mempty mempty mempty mempty@@ -130,7 +140,7 @@  addSize :: Name -> SizeClass -> OnKernelM () addSize key sclass =-  modify $ \s -> s { clSizes = M.insert key sclass $ clSizes s }+  modify $ \s -> s {clSizes = M.insert key sclass $ clSizes s}  onHostOp :: KernelTarget -> HostOp -> OnKernelM OpenCL onHostOp target (CallKernel k) = onKernel target k@@ -143,12 +153,17 @@ onHostOp _ (ImpKernels.GetSizeMax v size_class) =   return $ ImpOpenCL.GetSizeMax v size_class -genGPUCode :: OpsMode -> KernelCode -> [FailureMsg]-           -> GC.CompilerM KernelOp KernelState a-           -> (a, GC.CompilerState KernelState)+genGPUCode ::+  OpsMode ->+  KernelCode ->+  [FailureMsg] ->+  GC.CompilerM KernelOp KernelState a ->+  (a, GC.CompilerState KernelState) genGPUCode mode body failures =-  GC.runCompilerM (inKernelOperations mode body)-  blankNameSource (newKernelState failures)+  GC.runCompilerM+    (inKernelOperations mode body)+    blankNameSource+    (newKernelState failures)  -- Compilation of a device function that is not not invoked from the -- host, but is invoked by (perhaps multiple) kernels.@@ -164,30 +179,32 @@   failures <- gets clFailures    let params =-        [[C.cparam|__global int *global_failure|],-         [C.cparam|__global int *global_failure_args|]]+        [ [C.cparam|__global int *global_failure|],+          [C.cparam|__global typename int64_t *global_failure_args|]+        ]       (func, cstate) =         genGPUCode FunMode (functionBody device_func) failures $-        GC.compileFun mempty params (fname, device_func)+          GC.compileFun mempty params (fname, device_func)       kstate = GC.compUserState cstate -  modify $ \s -> s-           { clUsedTypes = typesInCode (functionBody device_func) <> clUsedTypes s-           , clDevFuns = M.insert fname func $ clDevFuns s-           , clFailures = kernelFailures kstate-           }+  modify $ \s ->+    s+      { clUsedTypes = typesInCode (functionBody device_func) <> clUsedTypes s,+        clDevFuns = M.insert fname func $ clDevFuns s,+        clFailures = kernelFailures kstate+      }    -- Important to do this after the 'modify' call, so we propagate the   -- right clFailures.   void $ ensureDeviceFuns $ functionBody device_func--  where toDevice :: HostOp -> KernelOp-        toDevice _ = bad+  where+    toDevice :: HostOp -> KernelOp+    toDevice _ = bad -        memParam MemParam{} = True-        memParam ScalarParam{} = False+    memParam MemParam {} = True+    memParam ScalarParam {} = False -        bad = compilerLimitationS "Cannot generate GPU functions that use arrays."+    bad = compilerLimitationS "Cannot generate GPU functions that use arrays."  -- Ensure that this device function is available, but don't regenerate -- it if it already exists.@@ -199,15 +216,16 @@ ensureDeviceFuns :: ImpKernels.KernelCode -> OnKernelM [Name] ensureDeviceFuns code = do   let called = calledFuncs code-  fmap catMaybes $ forM (S.toList called) $ \fname -> do-    def <- asks $ lookupFunction fname-    case def of-      Just func -> do ensureDeviceFun fname func-                      return $ Just fname-      Nothing -> return Nothing+  fmap catMaybes $+    forM (S.toList called) $ \fname -> do+      def <- asks $ lookupFunction fname+      case def of+        Just func -> do+          ensureDeviceFun fname func+          return $ Just fname+        Nothing -> return Nothing  onKernel :: KernelTarget -> Kernel -> OnKernelM OpenCL- onKernel target kernel = do   called <- ensureDeviceFuns $ kernelBody kernel @@ -217,15 +235,15 @@    let (kernel_body, cstate) =         genGPUCode KernelMode (kernelBody kernel) failures $-        GC.blockScope $ GC.compileCode $ kernelBody kernel+          GC.blockScope $ GC.compileCode $ kernelBody kernel       kstate = GC.compUserState cstate        use_params = mapMaybe useAsParam $ kernelUses kernel        (local_memory_args, local_memory_params, local_memory_init) =         unzip3 $-        flip evalState (blankNameSource :: VNameSource) $-        mapM (prepareLocalMemory target) $ kernelLocalMemory kstate+          flip evalState (blankNameSource :: VNameSource) $+            mapM (prepareLocalMemory target) $ kernelLocalMemory kstate        -- CUDA has very strict restrictions on the number of blocks       -- permitted along the 'y' and 'z' dimensions of the grid@@ -239,36 +257,45 @@       -- added automatically in CCUDA.hs.       (perm_params, block_dim_init) =         case (target, num_groups) of-          (TargetCUDA, [_, _, _]) -> ([[C.cparam|const int block_dim0|],-                                       [C.cparam|const int block_dim1|],-                                       [C.cparam|const int block_dim2|]],-                                      mempty)-          _ -> (mempty,-                [[C.citem|const int block_dim0 = 0;|],-                 [C.citem|const int block_dim1 = 1;|],-                 [C.citem|const int block_dim2 = 2;|]])+          (TargetCUDA, [_, _, _]) ->+            ( [ [C.cparam|const int block_dim0|],+                [C.cparam|const int block_dim1|],+                [C.cparam|const int block_dim2|]+              ],+              mempty+            )+          _ ->+            ( mempty,+              [ [C.citem|const int block_dim0 = 0;|],+                [C.citem|const int block_dim1 = 1;|],+                [C.citem|const int block_dim2 = 2;|]+              ]+            )        (const_defs, const_undefs) = unzip $ mapMaybe constDef $ kernelUses kernel    let (safety, error_init)         -- We conservatively assume that any called function can fail.         | not $ null called =-            (SafetyFull, [])-+          (SafetyFull, [])         | length (kernelFailures kstate) == length failures =-            if kernelFailureTolerant kernel+          if kernelFailureTolerant kernel             then (SafetyNone, [])             else -- No possible failures in this kernel, so if we make-                 -- it past an initial check, then we are good to go.-                 (SafetyCheap,-                  [C.citems|if (*global_failure >= 0) { return; }|])+            -- it past an initial check, then we are good to go. +              ( SafetyCheap,+                [C.citems|if (*global_failure >= 0) { return; }|]+              )         | otherwise =-            if not (kernelHasBarriers kstate)-            then (SafetyFull,-                  [C.citems|if (*global_failure >= 0) { return; }|])-            else (SafetyFull,-                  [C.citems|+          if not (kernelHasBarriers kstate)+            then+              ( SafetyFull,+                [C.citems|if (*global_failure >= 0) { return; }|]+              )+            else+              ( SafetyFull,+                [C.citems|                      volatile __local bool local_failure;                      if (failure_is_an_option) {                        int failed = *global_failure >= 0;@@ -279,17 +306,20 @@                      // All threads write this value - it looks like CUDA has a compiler bug otherwise.                      local_failure = false;                      barrier(CLK_LOCAL_MEM_FENCE);-                  |])+                  |]+              )        failure_params =-        [[C.cparam|__global int *global_failure|],-         [C.cparam|int failure_is_an_option|],-         [C.cparam|__global int *global_failure_args|]]+        [ [C.cparam|__global int *global_failure|],+          [C.cparam|int failure_is_an_option|],+          [C.cparam|__global typename int64_t *global_failure_args|]+        ] -      params = perm_params ++-               take (numFailureParams safety) failure_params ++-               catMaybes local_memory_params ++-               use_params+      params =+        perm_params+          ++ take (numFailureParams safety) failure_params+          ++ catMaybes local_memory_params+          ++ use_params        kernel_fun =         [C.cfun|__kernel void $id:name ($params:params) {@@ -303,62 +333,75 @@                    $items:const_undefs                 }|]-  modify $ \s -> s-    { clKernels = M.insert name (safety, kernel_fun) $ clKernels s-    , clUsedTypes = typesInKernel kernel <> clUsedTypes s-    , clFailures = kernelFailures kstate-    }+  modify $ \s ->+    s+      { clKernels = M.insert name (safety, kernel_fun) $ clKernels s,+        clUsedTypes = typesInKernel kernel <> clUsedTypes s,+        clFailures = kernelFailures kstate+      }    -- The argument corresponding to the global_failure parameters is   -- added automatically later.-  let args = catMaybes local_memory_args ++-             kernelArgs kernel+  let args =+        catMaybes local_memory_args+          ++ kernelArgs kernel    return $ LaunchKernel safety name args num_groups group_size-  where name = kernelName kernel-        num_groups = kernelNumGroups kernel-        group_size = kernelGroupSize kernel+  where+    name = kernelName kernel+    num_groups = kernelNumGroups kernel+    group_size = kernelGroupSize kernel -        prepareLocalMemory TargetOpenCL (mem, size) = do-          mem_aligned <- newVName $ baseString mem ++ "_aligned"-          return (Just $ SharedMemoryKArg size,-                  Just [C.cparam|__local volatile typename int64_t* $id:mem_aligned|],-                  [C.citem|__local volatile char* restrict $id:mem = (__local volatile char*)$id:mem_aligned;|])-        prepareLocalMemory TargetCUDA (mem, size) = do-          param <- newVName $ baseString mem ++ "_offset"-          return (Just $ SharedMemoryKArg size,-                  Just [C.cparam|uint $id:param|],-                  [C.citem|volatile char *$id:mem = &shared_mem[$id:param];|])+    prepareLocalMemory TargetOpenCL (mem, size) = do+      mem_aligned <- newVName $ baseString mem ++ "_aligned"+      return+        ( Just $ SharedMemoryKArg size,+          Just [C.cparam|__local volatile typename int64_t* $id:mem_aligned|],+          [C.citem|__local volatile char* restrict $id:mem = (__local volatile char*)$id:mem_aligned;|]+        )+    prepareLocalMemory TargetCUDA (mem, size) = do+      param <- newVName $ baseString mem ++ "_offset"+      return+        ( Just $ SharedMemoryKArg size,+          Just [C.cparam|uint $id:param|],+          [C.citem|volatile char *$id:mem = &shared_mem[$id:param];|]+        )  useAsParam :: KernelUse -> Maybe C.Param useAsParam (ScalarUse name bt) =   let ctp = case bt of         -- OpenCL does not permit bool as a kernel parameter type.         Bool -> [C.cty|unsigned char|]-        _    -> GC.primTypeToCType bt-  in Just [C.cparam|$ty:ctp $id:name|]+        _ -> GC.primTypeToCType bt+   in Just [C.cparam|$ty:ctp $id:name|] useAsParam (MemoryUse name) =   Just [C.cparam|__global unsigned char *$id:name|]-useAsParam ConstUse{} =+useAsParam ConstUse {} =   Nothing  -- Constants are #defined as macros.  Since a constant name in one -- kernel might potentially (although unlikely) also be used for -- something else in another kernel, we #undef them after the kernel. constDef :: KernelUse -> Maybe (C.BlockItem, C.BlockItem)-constDef (ConstUse v e) = Just ([C.citem|$escstm:def|],-                                [C.citem|$escstm:undef|])-  where e' = compilePrimExp e-        def = "#define " ++ pretty (C.toIdent v mempty) ++ " (" ++ prettyOneLine e' ++ ")"-        undef = "#undef " ++ pretty (C.toIdent v mempty)+constDef (ConstUse v e) =+  Just+    ( [C.citem|$escstm:def|],+      [C.citem|$escstm:undef|]+    )+  where+    e' = compilePrimExp e+    def = "#define " ++ pretty (C.toIdent v mempty) ++ " (" ++ prettyOneLine e' ++ ")"+    undef = "#undef " ++ pretty (C.toIdent v mempty) constDef _ = Nothing  openClCode :: [C.Func] -> String openClCode kernels =   pretty [C.cunit|$edecls:funcs|]-  where funcs =-          [[C.cedecl|$func:kernel_func|] |-           kernel_func <- kernels ]+  where+    funcs =+      [ [C.cedecl|$func:kernel_func|]+        | kernel_func <- kernels+      ]  atomicsDefs :: String atomicsDefs = $(embedStringFile "rts/c/atomics.h")@@ -366,13 +409,13 @@ genOpenClPrelude :: S.Set PrimType -> [C.Definition] genOpenClPrelude ts =   -- Clang-based OpenCL implementations need this for 'static' to work.-  [ [C.cedecl|$esc:("#ifdef cl_clang_storage_class_specifiers")|]-  , [C.cedecl|$esc:("#pragma OPENCL EXTENSION cl_clang_storage_class_specifiers : enable")|]-  , [C.cedecl|$esc:("#endif")|]-  , [C.cedecl|$esc:("#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : enable")|]]-  ++-  [[C.cedecl|$esc:("#pragma OPENCL EXTENSION cl_khr_fp64 : enable")|] | uses_float64] ++-  [C.cunit|+  [ [C.cedecl|$esc:("#ifdef cl_clang_storage_class_specifiers")|],+    [C.cedecl|$esc:("#pragma OPENCL EXTENSION cl_clang_storage_class_specifiers : enable")|],+    [C.cedecl|$esc:("#endif")|],+    [C.cedecl|$esc:("#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : enable")|]+  ]+    ++ [[C.cedecl|$esc:("#pragma OPENCL EXTENSION cl_khr_fp64 : enable")|] | uses_float64]+    ++ [C.cunit| /* Some OpenCL programs dislike empty progams, or programs with no kernels.  * Declare a dummy kernel to ensure they remain our friends. */ __kernel void dummy_kernel(__global unsigned char *dummy, int n)@@ -405,19 +448,26 @@ static inline void mem_fence_local() {   mem_fence(CLK_LOCAL_MEM_FENCE); }-|] ++-  cIntOps ++ cFloat32Ops ++ cFloat32Funs ++-  (if uses_float64 then cFloat64Ops ++ cFloat64Funs ++ cFloatConvOps else [])-  ++ [[C.cedecl|$esc:atomicsDefs|]]-  where uses_float64 = FloatType Float64 `S.member` ts+|]+    ++ cIntOps+    ++ cFloat32Ops+    ++ cFloat32Funs+    ++ (if uses_float64 then cFloat64Ops ++ cFloat64Funs ++ cFloatConvOps else [])+    ++ [[C.cedecl|$esc:atomicsDefs|]]+  where+    uses_float64 = FloatType Float64 `S.member` ts  genCUDAPrelude :: [C.Definition] genCUDAPrelude =   cudafy ++ ops-  where ops = cIntOps ++ cFloat32Ops ++ cFloat32Funs ++ cFloat64Ops-              ++ cFloat64Funs ++ cFloatConvOps-              ++ [[C.cedecl|$esc:atomicsDefs|]]-        cudafy = [CUDAC.cunit|+  where+    ops =+      cIntOps ++ cFloat32Ops ++ cFloat32Funs ++ cFloat64Ops+        ++ cFloat64Funs+        ++ cFloatConvOps+        ++ [[C.cedecl|$esc:atomicsDefs|]]+    cudafy =+      [CUDAC.cunit| $esc:("#define FUTHARK_CUDA")  typedef char int8_t;@@ -523,14 +573,16 @@  compilePrimExp :: PrimExp KernelConst -> C.Exp compilePrimExp e = runIdentity $ GC.compilePrimExp compileKernelConst e-  where compileKernelConst (SizeConst key) =-          return [C.cexp|$id:(zEncodeString (pretty key))|]+  where+    compileKernelConst (SizeConst key) =+      return [C.cexp|$id:(zEncodeString (pretty key))|]  kernelArgs :: Kernel -> [KernelArg] kernelArgs = mapMaybe useToArg . kernelUses-  where useToArg (MemoryUse mem)  = Just $ MemKArg mem-        useToArg (ScalarUse v bt) = Just $ ValueKArg (LeafExp (ScalarVar v) bt) bt-        useToArg ConstUse{}       = Nothing+  where+    useToArg (MemoryUse mem) = Just $ MemKArg mem+    useToArg (ScalarUse v bt) = Just $ ValueKArg (LeafExp (ScalarVar v) bt) bt+    useToArg ConstUse {} = Nothing  nextErrorLabel :: GC.CompilerM KernelOp KernelState String nextErrorLabel =@@ -538,202 +590,209 @@  incErrorLabel :: GC.CompilerM KernelOp KernelState () incErrorLabel =-  GC.modifyUserState $ \s -> s { kernelNextSync = kernelNextSync s + 1 }+  GC.modifyUserState $ \s -> s {kernelNextSync = kernelNextSync s + 1}  pendingError :: Bool -> GC.CompilerM KernelOp KernelState () pendingError b =-  GC.modifyUserState $ \s -> s { kernelSyncPending = b }+  GC.modifyUserState $ \s -> s {kernelSyncPending = b}  hasCommunication :: ImpKernels.KernelCode -> Bool hasCommunication = any communicates-  where communicates ErrorSync{} = True-        communicates Barrier{} = True-        communicates _ = False+  where+    communicates ErrorSync {} = True+    communicates Barrier {} = True+    communicates _ = False  -- Whether we are generating code for a kernel or a device function. -- This has minor effects, such as exactly how failures are -- propagated. data OpsMode = KernelMode | FunMode deriving (Eq) -inKernelOperations :: OpsMode -> ImpKernels.KernelCode-                   -> GC.Operations KernelOp KernelState+inKernelOperations ::+  OpsMode ->+  ImpKernels.KernelCode ->+  GC.Operations KernelOp KernelState inKernelOperations mode body =   GC.Operations-  { GC.opsCompiler = kernelOps-  , GC.opsMemoryType = kernelMemoryType-  , GC.opsWriteScalar = kernelWriteScalar-  , GC.opsReadScalar = kernelReadScalar-  , GC.opsAllocate = cannotAllocate-  , GC.opsDeallocate = cannotDeallocate-  , GC.opsCopy = copyInKernel-  , GC.opsStaticArray = noStaticArrays-  , GC.opsFatMemory = False-  , GC.opsError = errorInKernel-  , GC.opsCall = callInKernel-  }-  where has_communication = hasCommunication body--        fence FenceLocal = [C.cexp|CLK_LOCAL_MEM_FENCE|]-        fence FenceGlobal = [C.cexp|CLK_GLOBAL_MEM_FENCE | CLK_LOCAL_MEM_FENCE|]--        kernelOps :: GC.OpCompiler KernelOp KernelState-        kernelOps (GetGroupId v i) =-          GC.stm [C.cstm|$id:v = get_group_id($int:i);|]-        kernelOps (GetLocalId v i) =-          GC.stm [C.cstm|$id:v = get_local_id($int:i);|]-        kernelOps (GetLocalSize v i) =-          GC.stm [C.cstm|$id:v = get_local_size($int:i);|]-        kernelOps (GetGlobalId v i) =-          GC.stm [C.cstm|$id:v = get_global_id($int:i);|]-        kernelOps (GetGlobalSize v i) =-          GC.stm [C.cstm|$id:v = get_global_size($int:i);|]-        kernelOps (GetLockstepWidth v) =-          GC.stm [C.cstm|$id:v = LOCKSTEP_WIDTH;|]-        kernelOps (Barrier f) = do-          GC.stm [C.cstm|barrier($exp:(fence f));|]-          GC.modifyUserState $ \s -> s { kernelHasBarriers = True }-        kernelOps (MemFence FenceLocal) =-          GC.stm [C.cstm|mem_fence_local();|]-        kernelOps (MemFence FenceGlobal) =-          GC.stm [C.cstm|mem_fence_global();|]-        kernelOps (LocalAlloc name size) = do-          name' <- newVName $ pretty name ++ "_backing"-          GC.modifyUserState $ \s ->-            s { kernelLocalMemory = (name', size) : kernelLocalMemory s }-          GC.stm [C.cstm|$id:name = (__local char*) $id:name';|]-        kernelOps (ErrorSync f) = do-          label <- nextErrorLabel-          pending <- kernelSyncPending <$> GC.getUserState-          when pending $ do-            pendingError False-            GC.stm [C.cstm|$id:label: barrier($exp:(fence f));|]-            GC.stm [C.cstm|if (local_failure) { return; }|]-          GC.stm [C.cstm|barrier(CLK_LOCAL_MEM_FENCE);|] -- intentional-          GC.modifyUserState $ \s -> s { kernelHasBarriers = True }-          incErrorLabel-        kernelOps (Atomic space aop) = atomicOps space aop--        atomicCast s t = do-          let volatile = [C.ctyquals|volatile|]-          quals <- case s of Space sid    -> pointerQuals sid-                             _            -> pointerQuals "global"-          return [C.cty|$tyquals:(volatile++quals) $ty:t|]--        atomicSpace (Space sid) = sid-        atomicSpace _           = "global"--        doAtomic s t old arr ind val op ty = do-          ind' <- GC.compileExp $ unCount ind-          val' <- GC.compileExp val-          cast <- atomicCast s ty-          GC.stm [C.cstm|$id:old = $id:op'(&(($ty:cast *)$id:arr)[$exp:ind'], ($ty:ty) $exp:val');|]-          where op' = op ++ "_" ++ pretty t ++ "_" ++ atomicSpace s--        atomicOps s (AtomicAdd t old arr ind val) =-          doAtomic s t old arr ind val "atomic_add" [C.cty|int|]--        atomicOps s (AtomicFAdd t old arr ind val) =-          doAtomic s t old arr ind val "atomic_fadd" [C.cty|float|]--        atomicOps s (AtomicSMax t old arr ind val) =-          doAtomic s t old arr ind val "atomic_smax" [C.cty|int|]--        atomicOps s (AtomicSMin t old arr ind val) =-          doAtomic s t old arr ind val "atomic_smin" [C.cty|int|]--        atomicOps s (AtomicUMax t old arr ind val) =-          doAtomic s t old arr ind val "atomic_umax" [C.cty|unsigned int|]--        atomicOps s (AtomicUMin t old arr ind val) =-          doAtomic s t old arr ind val "atomic_umin" [C.cty|unsigned int|]+    { GC.opsCompiler = kernelOps,+      GC.opsMemoryType = kernelMemoryType,+      GC.opsWriteScalar = kernelWriteScalar,+      GC.opsReadScalar = kernelReadScalar,+      GC.opsAllocate = cannotAllocate,+      GC.opsDeallocate = cannotDeallocate,+      GC.opsCopy = copyInKernel,+      GC.opsStaticArray = noStaticArrays,+      GC.opsFatMemory = False,+      GC.opsError = errorInKernel,+      GC.opsCall = callInKernel,+      GC.opsCritical = mempty+    }+  where+    has_communication = hasCommunication body -        atomicOps s (AtomicAnd t old arr ind val) =-          doAtomic s t old arr ind val "atomic_and" [C.cty|int|]+    fence FenceLocal = [C.cexp|CLK_LOCAL_MEM_FENCE|]+    fence FenceGlobal = [C.cexp|CLK_GLOBAL_MEM_FENCE | CLK_LOCAL_MEM_FENCE|] -        atomicOps s (AtomicOr t old arr ind val) =-          doAtomic s t old arr ind val "atomic_or" [C.cty|int|]+    kernelOps :: GC.OpCompiler KernelOp KernelState+    kernelOps (GetGroupId v i) =+      GC.stm [C.cstm|$id:v = get_group_id($int:i);|]+    kernelOps (GetLocalId v i) =+      GC.stm [C.cstm|$id:v = get_local_id($int:i);|]+    kernelOps (GetLocalSize v i) =+      GC.stm [C.cstm|$id:v = get_local_size($int:i);|]+    kernelOps (GetGlobalId v i) =+      GC.stm [C.cstm|$id:v = get_global_id($int:i);|]+    kernelOps (GetGlobalSize v i) =+      GC.stm [C.cstm|$id:v = get_global_size($int:i);|]+    kernelOps (GetLockstepWidth v) =+      GC.stm [C.cstm|$id:v = LOCKSTEP_WIDTH;|]+    kernelOps (Barrier f) = do+      GC.stm [C.cstm|barrier($exp:(fence f));|]+      GC.modifyUserState $ \s -> s {kernelHasBarriers = True}+    kernelOps (MemFence FenceLocal) =+      GC.stm [C.cstm|mem_fence_local();|]+    kernelOps (MemFence FenceGlobal) =+      GC.stm [C.cstm|mem_fence_global();|]+    kernelOps (LocalAlloc name size) = do+      name' <- newVName $ pretty name ++ "_backing"+      GC.modifyUserState $ \s ->+        s {kernelLocalMemory = (name', fmap untyped size) : kernelLocalMemory s}+      GC.stm [C.cstm|$id:name = (__local char*) $id:name';|]+    kernelOps (ErrorSync f) = do+      label <- nextErrorLabel+      pending <- kernelSyncPending <$> GC.getUserState+      when pending $ do+        pendingError False+        GC.stm [C.cstm|$id:label: barrier($exp:(fence f));|]+        GC.stm [C.cstm|if (local_failure) { return; }|]+      GC.stm [C.cstm|barrier(CLK_LOCAL_MEM_FENCE);|] -- intentional+      GC.modifyUserState $ \s -> s {kernelHasBarriers = True}+      incErrorLabel+    kernelOps (Atomic space aop) = atomicOps space aop -        atomicOps s (AtomicXor t old arr ind val) =-          doAtomic s t old arr ind val "atomic_xor" [C.cty|int|]+    atomicCast s t = do+      let volatile = [C.ctyquals|volatile|]+      quals <- case s of+        Space sid -> pointerQuals sid+        _ -> pointerQuals "global"+      return [C.cty|$tyquals:(volatile++quals) $ty:t|] -        atomicOps s (AtomicCmpXchg t old arr ind cmp val) = do-          ind' <- GC.compileExp $ unCount ind-          cmp' <- GC.compileExp cmp-          val' <- GC.compileExp val-          cast <- atomicCast s [C.cty|int|]-          GC.stm [C.cstm|$id:old = $id:op(&(($ty:cast *)$id:arr)[$exp:ind'], $exp:cmp', $exp:val');|]-          where op = "atomic_cmpxchg_" ++ pretty t ++ "_" ++ atomicSpace s+    atomicSpace (Space sid) = sid+    atomicSpace _ = "global" -        atomicOps s (AtomicXchg t old arr ind val) = do-          ind' <- GC.compileExp $ unCount ind-          val' <- GC.compileExp val-          cast <- atomicCast s [C.cty|int|]-          GC.stm [C.cstm|$id:old = $id:op(&(($ty:cast *)$id:arr)[$exp:ind'], $exp:val');|]-          where op = "atomic_cmpxchg_" ++ pretty t ++ "_" ++ atomicSpace s+    doAtomic s t old arr ind val op ty = do+      ind' <- GC.compileExp $ untyped $ unCount ind+      val' <- GC.compileExp val+      cast <- atomicCast s ty+      GC.stm [C.cstm|$id:old = $id:op'(&(($ty:cast *)$id:arr)[$exp:ind'], ($ty:ty) $exp:val');|]+      where+        op' = op ++ "_" ++ pretty t ++ "_" ++ atomicSpace s -        cannotAllocate :: GC.Allocate KernelOp KernelState-        cannotAllocate _ =-          error "Cannot allocate memory in kernel"+    atomicOps s (AtomicAdd t old arr ind val) =+      doAtomic s t old arr ind val "atomic_add" [C.cty|int|]+    atomicOps s (AtomicFAdd t old arr ind val) =+      doAtomic s t old arr ind val "atomic_fadd" [C.cty|float|]+    atomicOps s (AtomicSMax t old arr ind val) =+      doAtomic s t old arr ind val "atomic_smax" [C.cty|int|]+    atomicOps s (AtomicSMin t old arr ind val) =+      doAtomic s t old arr ind val "atomic_smin" [C.cty|int|]+    atomicOps s (AtomicUMax t old arr ind val) =+      doAtomic s t old arr ind val "atomic_umax" [C.cty|unsigned int|]+    atomicOps s (AtomicUMin t old arr ind val) =+      doAtomic s t old arr ind val "atomic_umin" [C.cty|unsigned int|]+    atomicOps s (AtomicAnd t old arr ind val) =+      doAtomic s t old arr ind val "atomic_and" [C.cty|int|]+    atomicOps s (AtomicOr t old arr ind val) =+      doAtomic s t old arr ind val "atomic_or" [C.cty|int|]+    atomicOps s (AtomicXor t old arr ind val) =+      doAtomic s t old arr ind val "atomic_xor" [C.cty|int|]+    atomicOps s (AtomicCmpXchg t old arr ind cmp val) = do+      ind' <- GC.compileExp $ untyped $ unCount ind+      cmp' <- GC.compileExp cmp+      val' <- GC.compileExp val+      cast <- atomicCast s [C.cty|int|]+      GC.stm [C.cstm|$id:old = $id:op(&(($ty:cast *)$id:arr)[$exp:ind'], $exp:cmp', $exp:val');|]+      where+        op = "atomic_cmpxchg_" ++ pretty t ++ "_" ++ atomicSpace s+    atomicOps s (AtomicXchg t old arr ind val) = do+      ind' <- GC.compileExp $ untyped $ unCount ind+      val' <- GC.compileExp val+      cast <- atomicCast s [C.cty|int|]+      GC.stm [C.cstm|$id:old = $id:op(&(($ty:cast *)$id:arr)[$exp:ind'], $exp:val');|]+      where+        op = "atomic_cmpxchg_" ++ pretty t ++ "_" ++ atomicSpace s -        cannotDeallocate :: GC.Deallocate KernelOp KernelState-        cannotDeallocate _ _ =-          error "Cannot deallocate memory in kernel"+    cannotAllocate :: GC.Allocate KernelOp KernelState+    cannotAllocate _ =+      error "Cannot allocate memory in kernel" -        copyInKernel :: GC.Copy KernelOp KernelState-        copyInKernel _ _ _ _ _ _ _ =-          error "Cannot bulk copy in kernel."+    cannotDeallocate :: GC.Deallocate KernelOp KernelState+    cannotDeallocate _ _ =+      error "Cannot deallocate memory in kernel" -        noStaticArrays :: GC.StaticArray KernelOp KernelState-        noStaticArrays _ _ _ _ =-          error "Cannot create static array in kernel."+    copyInKernel :: GC.Copy KernelOp KernelState+    copyInKernel _ _ _ _ _ _ _ =+      error "Cannot bulk copy in kernel." -        kernelMemoryType space = do-          quals <- pointerQuals space-          return [C.cty|$tyquals:quals $ty:defaultMemBlockType|]+    noStaticArrays :: GC.StaticArray KernelOp KernelState+    noStaticArrays _ _ _ _ =+      error "Cannot create static array in kernel." -        kernelWriteScalar =-          GC.writeScalarPointerWithQuals pointerQuals+    kernelMemoryType space = do+      quals <- pointerQuals space+      return [C.cty|$tyquals:quals $ty:defaultMemBlockType|] -        kernelReadScalar =-          GC.readScalarPointerWithQuals pointerQuals+    kernelWriteScalar =+      GC.writeScalarPointerWithQuals pointerQuals -        whatNext = do-           label <- nextErrorLabel-           pendingError True-           return $ if has_communication-                    then [C.citems|local_failure = true; goto $id:label;|]-                    else if mode == FunMode-                         then [C.citems|return 1;|]-                         else [C.citems|return;|]+    kernelReadScalar =+      GC.readScalarPointerWithQuals pointerQuals -        callInKernel dests fname args-          | isBuiltInFunction fname =-              GC.opsCall GC.defaultOperations dests fname args+    whatNext = do+      label <- nextErrorLabel+      pendingError True+      return $+        if has_communication+          then [C.citems|local_failure = true; goto $id:label;|]+          else+            if mode == FunMode+              then [C.citems|return 1;|]+              else [C.citems|return;|] -          | otherwise = do-              let out_args = [ [C.cexp|&$id:d|] | d <- dests ]-                  args' = [C.cexp|global_failure|] : [C.cexp|global_failure_args|] :-                          out_args ++ args+    callInKernel dests fname args+      | isBuiltInFunction fname =+        GC.opsCall GC.defaultOperations dests fname args+      | otherwise = do+        let out_args = [[C.cexp|&$id:d|] | d <- dests]+            args' =+              [C.cexp|global_failure|] :+              [C.cexp|global_failure_args|] :+              out_args ++ args -              what_next <- whatNext+        what_next <- whatNext -              GC.item [C.citem|if ($id:(funName fname)($args:args') != 0) { $items:what_next; }|]+        GC.item [C.citem|if ($id:(funName fname)($args:args') != 0) { $items:what_next; }|] -        errorInKernel msg@(ErrorMsg parts) backtrace = do-          n <- length . kernelFailures <$> GC.getUserState-          GC.modifyUserState $ \s ->-            s { kernelFailures = kernelFailures s ++ [FailureMsg msg backtrace] }-          let setArgs _ [] = return []-              setArgs i (ErrorString{} : parts') = setArgs i parts'-              setArgs i (ErrorInt32 x : parts') = do-                x' <- GC.compileExp x-                stms <- setArgs (i+1) parts'-                return $ [C.cstm|global_failure_args[$int:i] = $exp:x';|] : stms-          argstms <- setArgs (0::Int) parts+    errorInKernel msg@(ErrorMsg parts) backtrace = do+      n <- length . kernelFailures <$> GC.getUserState+      GC.modifyUserState $ \s ->+        s {kernelFailures = kernelFailures s ++ [FailureMsg msg backtrace]}+      let setArgs _ [] = return []+          setArgs i (ErrorString {} : parts') = setArgs i parts'+          setArgs i (ErrorInt32 x : parts') = do+            x' <- GC.compileExp x+            stms <- setArgs (i + 1) parts'+            return $ [C.cstm|global_failure_args[$int:i] = (typename int64_t)$exp:x';|] : stms+          setArgs i (ErrorInt64 x : parts') = do+            x' <- GC.compileExp x+            stms <- setArgs (i + 1) parts'+            return $ [C.cstm|global_failure_args[$int:i] = $exp:x';|] : stms+      argstms <- setArgs (0 :: Int) parts -          what_next <- whatNext+      what_next <- whatNext -          GC.stm [C.cstm|{ if (atomic_cmpxchg_i32_global(global_failure, -1, $int:n) == -1)+      GC.stm+        [C.cstm|{ if (atomic_cmpxchg_i32_global(global_failure, -1, $int:n) == -1)                                  { $stms:argstms; }                                  $items:what_next                                }|]@@ -746,37 +805,48 @@ typesInCode :: ImpKernels.KernelCode -> S.Set PrimType typesInCode Skip = mempty typesInCode (c1 :>>: c2) = typesInCode c1 <> typesInCode c2-typesInCode (For _ it e c) = IntType it `S.insert` typesInExp e <> typesInCode c-typesInCode (While e c) = typesInExp e <> typesInCode c-typesInCode DeclareMem{} = mempty+typesInCode (For _ e c) = typesInExp e <> typesInCode c+typesInCode (While (TPrimExp e) c) = typesInExp e <> typesInCode c+typesInCode DeclareMem {} = mempty typesInCode (DeclareScalar _ _ t) = S.singleton t typesInCode (DeclareArray _ _ t _) = S.singleton t-typesInCode (Allocate _ (Count e) _) = typesInExp e-typesInCode Free{} = mempty-typesInCode (Copy _ (Count e1) _ _ (Count e2) _ (Count e3)) =-  typesInExp e1 <> typesInExp e2 <> typesInExp e3-typesInCode (Write _ (Count e1) t _ _ e2) =+typesInCode (Allocate _ (Count (TPrimExp e)) _) = typesInExp e+typesInCode Free {} = mempty+typesInCode+  ( Copy+      _+      (Count (TPrimExp e1))+      _+      _+      (Count (TPrimExp e2))+      _+      (Count (TPrimExp e3))+    ) =+    typesInExp e1 <> typesInExp e2 <> typesInExp e3+typesInCode (Write _ (Count (TPrimExp e1)) t _ _ e2) =   typesInExp e1 <> S.singleton t <> typesInExp e2 typesInCode (SetScalar _ e) = typesInExp e-typesInCode SetMem{} = mempty+typesInCode SetMem {} = mempty typesInCode (Call _ _ es) = mconcat $ map typesInArg es-  where typesInArg MemArg{} = mempty-        typesInArg (ExpArg e) = typesInExp e-typesInCode (If e c1 c2) =+  where+    typesInArg MemArg {} = mempty+    typesInArg (ExpArg e) = typesInExp e+typesInCode (If (TPrimExp e) c1 c2) =   typesInExp e <> typesInCode c1 <> typesInCode c2 typesInCode (Assert e _ _) = typesInExp e typesInCode (Comment _ c) = typesInCode c typesInCode (DebugPrint _ v) = maybe mempty typesInExp v-typesInCode Op{} = mempty+typesInCode Op {} = mempty  typesInExp :: Exp -> S.Set PrimType typesInExp (ValueExp v) = S.singleton $ primValueType v typesInExp (BinOpExp _ e1 e2) = typesInExp e1 <> typesInExp e2 typesInExp (CmpOpExp _ e1 e2) = typesInExp e1 <> typesInExp e2 typesInExp (ConvOpExp op e) = S.fromList [from, to] <> typesInExp e-  where (from, to) = convOpType op+  where+    (from, to) = convOpType op typesInExp (UnOpExp _ e) = typesInExp e typesInExp (FunExp _ args t) = S.singleton t <> mconcat (map typesInExp args)-typesInExp (LeafExp (Index _ (Count e) t _ _) _) = S.singleton t <> typesInExp e-typesInExp (LeafExp ScalarVar{} _) = mempty+typesInExp (LeafExp (Index _ (Count (TPrimExp e)) t _ _) _) = S.singleton t <> typesInExp e+typesInExp (LeafExp ScalarVar {} _) = mempty typesInExp (LeafExp (SizeOf t) _) = S.singleton t
src/Futhark/CodeGen/ImpGen/Kernels/Transpose.hs view
@@ -1,186 +1,263 @@ -- | Carefully optimised implementations of GPU transpositions. -- Written in ImpCode so we can compile it to both CUDA and OpenCL. module Futhark.CodeGen.ImpGen.Kernels.Transpose-  ( TransposeType(..)-  , TransposeArgs-  , mapTransposeKernel+  ( TransposeType (..),+    TransposeArgs,+    mapTransposeKernel,   )-  where--import Prelude hiding (quot, rem)+where  import Futhark.CodeGen.ImpCode.Kernels import Futhark.IR.Prop.Types-import Futhark.Util.IntegralExp (IntegralExp, divUp, quot, rem)+import Futhark.Util.IntegralExp (divUp, quot, rem)+import Prelude hiding (quot, rem)  -- | Which form of transposition to generate code for.-data TransposeType = TransposeNormal-                   | TransposeLowWidth-                   | TransposeLowHeight-                   | TransposeSmall -- ^ For small arrays that do not-                                    -- benefit from coalescing.-                   deriving (Eq, Ord, Show)+data TransposeType+  = TransposeNormal+  | TransposeLowWidth+  | TransposeLowHeight+  | -- | For small arrays that do not+    -- benefit from coalescing.+    TransposeSmall+  deriving (Eq, Ord, Show)  -- | The types of the arguments accepted by a transposition function.-type TransposeArgs = (VName, Exp,-                      VName, Exp,-                      Exp, Exp,-                      Exp, Exp, Exp,-                      VName)+type TransposeArgs =+  ( VName,+    TExp Int32,+    VName,+    TExp Int32,+    TExp Int32,+    TExp Int32,+    TExp Int32,+    TExp Int32,+    TExp Int32,+    VName+  ) -elemsPerThread :: IntegralExp a => a+elemsPerThread :: TExp Int32 elemsPerThread = 4 -mapTranspose :: Exp -> TransposeArgs -> PrimType -> TransposeType -> KernelCode+mapTranspose :: TExp Int32 -> TransposeArgs -> PrimType -> TransposeType -> KernelCode mapTranspose block_dim args t kind =   case kind of     TransposeSmall ->       mconcat-      [ get_ids--      , dec our_array_offset $ v32 get_global_id_0 `quot` (height*width) * (height*width)--      , dec x_index $ (v32 get_global_id_0 `rem` (height*width)) `quot` height-      , dec y_index $ v32 get_global_id_0 `rem` height--      , dec odata_offset $-        (basic_odata_offset `quot` primByteSize t) + v32 our_array_offset-      , dec idata_offset $-        (basic_idata_offset `quot` primByteSize t) + v32 our_array_offset--      , dec index_in $ v32 y_index * width + v32 x_index-      , dec index_out $ v32 x_index * height + v32 y_index--      , If (v32 get_global_id_0 .<. width * height * num_arrays)-        (Write odata (elements $ v32 odata_offset + v32 index_out) t (Space "global") Nonvolatile $-         index idata (elements $ v32 idata_offset + v32 index_in) t (Space "global") Nonvolatile)-        mempty-      ]-+        [ get_ids,+          dec our_array_offset $ vi32 get_global_id_0 `quot` (height * width) * (height * width),+          dec x_index $ (vi32 get_global_id_0 `rem` (height * width)) `quot` height,+          dec y_index $ vi32 get_global_id_0 `rem` height,+          dec odata_offset $+            (basic_odata_offset `quot` primByteSize t) + vi32 our_array_offset,+          dec idata_offset $+            (basic_idata_offset `quot` primByteSize t) + vi32 our_array_offset,+          dec index_in $ vi32 y_index * width + vi32 x_index,+          dec index_out $ vi32 x_index * height + vi32 y_index,+          when+            (vi32 get_global_id_0 .<. width * height * num_arrays)+            ( Write odata (elements $ sExt64 $ vi32 odata_offset + vi32 index_out) t (Space "global") Nonvolatile $+                index idata (elements $ sExt64 $ vi32 idata_offset + vi32 index_in) t (Space "global") Nonvolatile+            )+        ]     TransposeLowWidth ->-      mkTranspose $ lowDimBody-      (v32 get_group_id_0 * block_dim + (v32 get_local_id_0 `quot` muly))-      (v32 get_group_id_1 * block_dim * muly + v32 get_local_id_1 +-       (v32 get_local_id_0 `rem` muly) * block_dim)-      (v32 get_group_id_1* block_dim * muly + v32 get_local_id_0 +-       (v32 get_local_id_1 `rem` muly) * block_dim)-      (v32 get_group_id_0 * block_dim + (v32 get_local_id_1 `quot` muly))-+      mkTranspose $+        lowDimBody+          (vi32 get_group_id_0 * block_dim + (vi32 get_local_id_0 `quot` muly))+          ( vi32 get_group_id_1 * block_dim * muly + vi32 get_local_id_1+              + (vi32 get_local_id_0 `rem` muly) * block_dim+          )+          ( vi32 get_group_id_1 * block_dim * muly + vi32 get_local_id_0+              + (vi32 get_local_id_1 `rem` muly) * block_dim+          )+          (vi32 get_group_id_0 * block_dim + (vi32 get_local_id_1 `quot` muly))     TransposeLowHeight ->-      mkTranspose $ lowDimBody-      (v32 get_group_id_0 * block_dim * mulx + v32 get_local_id_0 +-       (v32 get_local_id_1 `rem` mulx) * block_dim)-      (v32 get_group_id_1 * block_dim + (v32 get_local_id_1 `quot` mulx))-      (v32 get_group_id_1 * block_dim + (v32 get_local_id_0 `quot` mulx))-      (v32 get_group_id_0 * block_dim * mulx + v32 get_local_id_1 +-       (v32 get_local_id_0 `rem` mulx) * block_dim)-+      mkTranspose $+        lowDimBody+          ( vi32 get_group_id_0 * block_dim * mulx + vi32 get_local_id_0+              + (vi32 get_local_id_1 `rem` mulx) * block_dim+          )+          (vi32 get_group_id_1 * block_dim + (vi32 get_local_id_1 `quot` mulx))+          (vi32 get_group_id_1 * block_dim + (vi32 get_local_id_0 `quot` mulx))+          ( vi32 get_group_id_0 * block_dim * mulx + vi32 get_local_id_1+              + (vi32 get_local_id_0 `rem` mulx) * block_dim+          )     TransposeNormal ->-      mkTranspose $ mconcat-      [ dec x_index $ v32 get_global_id_0-      , dec y_index $ v32 get_group_id_1 * tile_dim + v32 get_local_id_1-      , when (v32 x_index .<. width) $-        For j Int32 elemsPerThread $-        let i = v32 j * (tile_dim `quot` elemsPerThread)-        in mconcat [ dec index_in $ (v32 y_index + i) * width + v32 x_index-                   , when (v32 y_index + i .<. height) $-                     Write block (elements $ (v32 get_local_id_1 + i) * (tile_dim+1)-                                             + v32 get_local_id_0)-                     t (Space "local") Nonvolatile $-                     index idata (elements $ v32 idata_offset + v32 index_in)-                     t (Space "global") Nonvolatile]-      , Op $ Barrier FenceLocal-      , SetScalar x_index $ v32 get_group_id_1 * tile_dim + v32 get_local_id_0-      , SetScalar y_index $ v32 get_group_id_0 * tile_dim + v32 get_local_id_1-      , when (v32 x_index .<. height) $-        For j Int32 elemsPerThread $-        let i = v32 j * (tile_dim `quot` elemsPerThread)-        in mconcat [ dec index_out $ (v32 y_index + i) * height + v32 x_index-                   , when (v32 y_index + i .<. width) $-                     Write odata (elements $ v32 odata_offset + v32 index_out)-                     t (Space "global") Nonvolatile $-                     index block (elements $ v32 get_local_id_0 * (tile_dim+1)-                                             + v32 get_local_id_1+i)-                     t (Space "local") Nonvolatile-                   ]-      ]--  where dec v e = DeclareScalar v Nonvolatile int32 <> SetScalar v e-        v32 = flip var int32-        tile_dim = 2 * block_dim+      mkTranspose $+        mconcat+          [ dec x_index $ vi32 get_global_id_0,+            dec y_index $ vi32 get_group_id_1 * tile_dim + vi32 get_local_id_1,+            when (vi32 x_index .<. width) $+              For j (untyped elemsPerThread) $+                let i = vi32 j * (tile_dim `quot` elemsPerThread)+                 in mconcat+                      [ dec index_in $ (vi32 y_index + i) * width + vi32 x_index,+                        when (vi32 y_index + i .<. height) $+                          Write+                            block+                            ( elements $+                                sExt64 $+                                  (vi32 get_local_id_1 + i) * (tile_dim + 1)+                                    + vi32 get_local_id_0+                            )+                            t+                            (Space "local")+                            Nonvolatile+                            $ index+                              idata+                              (elements $ sExt64 $ vi32 idata_offset + vi32 index_in)+                              t+                              (Space "global")+                              Nonvolatile+                      ],+            Op $ Barrier FenceLocal,+            SetScalar x_index $ untyped $ vi32 get_group_id_1 * tile_dim + vi32 get_local_id_0,+            SetScalar y_index $ untyped $ vi32 get_group_id_0 * tile_dim + vi32 get_local_id_1,+            when (vi32 x_index .<. height) $+              For j (untyped elemsPerThread) $+                let i = vi32 j * (tile_dim `quot` elemsPerThread)+                 in mconcat+                      [ dec index_out $ (vi32 y_index + i) * height + vi32 x_index,+                        when (vi32 y_index + i .<. width) $+                          Write+                            odata+                            (elements $ sExt64 $ vi32 odata_offset + vi32 index_out)+                            t+                            (Space "global")+                            Nonvolatile+                            $ index+                              block+                              ( elements $+                                  sExt64 $+                                    vi32 get_local_id_0 * (tile_dim + 1) + vi32 get_local_id_1 + i+                              )+                              t+                              (Space "local")+                              Nonvolatile+                      ]+          ]+  where+    dec v (TPrimExp e) =+      DeclareScalar v Nonvolatile (primExpType e) <> SetScalar v e+    tile_dim = 2 * block_dim -        when a b = If a b mempty+    when a b = If a b mempty -        (odata, basic_odata_offset, idata, basic_idata_offset,-         width, height,-         mulx, muly, num_arrays, block) = args+    ( odata,+      basic_odata_offset,+      idata,+      basic_idata_offset,+      width,+      height,+      mulx,+      muly,+      num_arrays,+      block+      ) = args -        -- Be extremely careful when editing this list to ensure that-        -- the names match up.  Also, be careful that the tags on-        -- these names do not conflict with the tags of the-        -- surrounding code.  We accomplish the latter by using very-        -- low tags (normal variables start at least in the low-        -- hundreds).-        [   our_array_offset , x_index , y_index-          , odata_offset, idata_offset, index_in, index_out-          , get_global_id_0-          , get_local_id_0, get_local_id_1-          , get_group_id_0, get_group_id_1, get_group_id_2-          , j] =-          zipWith (flip VName) [30..] $ map nameFromString-          [ "our_array_offset" , "x_index" , "y_index"-          , "odata_offset", "idata_offset", "index_in", "index_out"-          , "get_global_id_0"-          , "get_local_id_0", "get_local_id_1"-          , "get_group_id_0", "get_group_id_1", "get_group_id_2"-          , "j"]+    -- Be extremely careful when editing this list to ensure that+    -- the names match up.  Also, be careful that the tags on+    -- these names do not conflict with the tags of the+    -- surrounding code.  We accomplish the latter by using very+    -- low tags (normal variables start at least in the low+    -- hundreds).+    [ our_array_offset,+      x_index,+      y_index,+      odata_offset,+      idata_offset,+      index_in,+      index_out,+      get_global_id_0,+      get_local_id_0,+      get_local_id_1,+      get_group_id_0,+      get_group_id_1,+      get_group_id_2,+      j+      ] =+        zipWith (flip VName) [30 ..] $+          map+            nameFromString+            [ "our_array_offset",+              "x_index",+              "y_index",+              "odata_offset",+              "idata_offset",+              "index_in",+              "index_out",+              "get_global_id_0",+              "get_local_id_0",+              "get_local_id_1",+              "get_group_id_0",+              "get_group_id_1",+              "get_group_id_2",+              "j"+            ] -        get_ids =-          mconcat [ DeclareScalar get_global_id_0 Nonvolatile int32-                  , Op $ GetGlobalId get_global_id_0 0-                  , DeclareScalar get_local_id_0 Nonvolatile int32-                  , Op $ GetLocalId get_local_id_0 0-                  , DeclareScalar get_local_id_1 Nonvolatile int32-                  , Op $ GetLocalId get_local_id_1 1-                  , DeclareScalar get_group_id_0 Nonvolatile int32-                  , Op $ GetGroupId get_group_id_0 0-                  , DeclareScalar get_group_id_1 Nonvolatile int32-                  , Op $ GetGroupId get_group_id_1 1-                  , DeclareScalar get_group_id_2 Nonvolatile int32-                  , Op $ GetGroupId get_group_id_2 2-                  ]+    get_ids =+      mconcat+        [ DeclareScalar get_global_id_0 Nonvolatile int32,+          Op $ GetGlobalId get_global_id_0 0,+          DeclareScalar get_local_id_0 Nonvolatile int32,+          Op $ GetLocalId get_local_id_0 0,+          DeclareScalar get_local_id_1 Nonvolatile int32,+          Op $ GetLocalId get_local_id_1 1,+          DeclareScalar get_group_id_0 Nonvolatile int32,+          Op $ GetGroupId get_group_id_0 0,+          DeclareScalar get_group_id_1 Nonvolatile int32,+          Op $ GetGroupId get_group_id_1 1,+          DeclareScalar get_group_id_2 Nonvolatile int32,+          Op $ GetGroupId get_group_id_2 2+        ] -        mkTranspose body =-          mconcat-          [ get_ids-          , dec our_array_offset $ v32 get_group_id_2 * width * height-          , dec odata_offset $-            (basic_odata_offset `quot` primByteSize t) + v32 our_array_offset-          , dec idata_offset $-            (basic_idata_offset `quot` primByteSize t) + v32 our_array_offset-          , body-          ]+    mkTranspose body =+      mconcat+        [ get_ids,+          dec our_array_offset $ vi32 get_group_id_2 * width * height,+          dec odata_offset $+            (basic_odata_offset `quot` primByteSize t) + vi32 our_array_offset,+          dec idata_offset $+            (basic_idata_offset `quot` primByteSize t) + vi32 our_array_offset,+          body+        ] -        lowDimBody x_in_index y_in_index x_out_index y_out_index =-          mconcat-          [ dec x_index x_in_index-          , dec y_index y_in_index-          , dec index_in $ v32 y_index * width + v32 x_index-          , when (v32 x_index .<. width .&&. v32 y_index .<. height) $-            Write block (elements $ v32 get_local_id_1 * (block_dim+1) + v32 get_local_id_0)-            t (Space "local") Nonvolatile $-            index idata (elements $ v32 idata_offset + v32 index_in)-            t (Space "global") Nonvolatile-          , Op $ Barrier FenceLocal-          , SetScalar x_index x_out_index-          , SetScalar y_index y_out_index-          , dec index_out $ v32 y_index * height + v32 x_index-          , when (v32 x_index .<. height .&&. v32 y_index .<. width) $-            Write odata (elements $ v32 odata_offset + v32 index_out)-            t (Space "global") Nonvolatile $-            index block (elements $ v32 get_local_id_0 * (block_dim+1) + v32 get_local_id_1)-            t (Space "local") Nonvolatile-          ]+    lowDimBody x_in_index y_in_index x_out_index y_out_index =+      mconcat+        [ dec x_index x_in_index,+          dec y_index y_in_index,+          dec index_in $ vi32 y_index * width + vi32 x_index,+          when (vi32 x_index .<. width .&&. vi32 y_index .<. height) $+            Write+              block+              (elements $ sExt64 $ vi32 get_local_id_1 * (block_dim + 1) + vi32 get_local_id_0)+              t+              (Space "local")+              Nonvolatile+              $ index+                idata+                (elements $ sExt64 $ vi32 idata_offset + vi32 index_in)+                t+                (Space "global")+                Nonvolatile,+          Op $ Barrier FenceLocal,+          SetScalar x_index $ untyped x_out_index,+          SetScalar y_index $ untyped y_out_index,+          dec index_out $ vi32 y_index * height + vi32 x_index,+          when (vi32 x_index .<. height .&&. vi32 y_index .<. width) $+            Write+              odata+              (elements $ sExt64 (vi32 odata_offset + vi32 index_out))+              t+              (Space "global")+              Nonvolatile+              $ index+                block+                (elements $ sExt64 $ vi32 get_local_id_0 * (block_dim + 1) + vi32 get_local_id_1)+                t+                (Space "local")+                Nonvolatile+        ]  -- | Generate a transpose kernel.  There is special support to handle -- input arrays with low width, low height, or both.@@ -205,65 +282,105 @@ -- of block_dim*2 by block_dim*2+1 elements. Padding each row with -- an additional element prevents bank conflicts from occuring when -- the tile is accessed column-wise.-mapTransposeKernel :: String -> Integer -> TransposeArgs -> PrimType -> TransposeType-                   -> Kernel+mapTransposeKernel ::+  String ->+  Integer ->+  TransposeArgs ->+  PrimType ->+  TransposeType ->+  Kernel mapTransposeKernel desc block_dim_int args t kind =   Kernel-  { kernelBody = DeclareMem block (Space "local") <>-                 Op (LocalAlloc block block_size) <>-                 mapTranspose block_dim args t kind-  , kernelUses = uses-  , kernelNumGroups = num_groups-  , kernelGroupSize = group_size-  , kernelName = nameFromString name-  , kernelFailureTolerant = True-  }-  where pad2DBytes k = k * (k + 1) * primByteSize t-        block_size =-          case kind of TransposeSmall -> 1 -- Not used, but AMD's-                                           -- OpenCL does not like-                                           -- zero-size local memory.-                       TransposeNormal -> fromInteger $ pad2DBytes $ 2*block_dim_int-                       TransposeLowWidth -> fromInteger $ pad2DBytes block_dim_int-                       TransposeLowHeight -> fromInteger $ pad2DBytes block_dim_int-        block_dim = fromInteger block_dim_int+    { kernelBody =+        DeclareMem block (Space "local")+          <> Op (LocalAlloc block block_size)+          <> mapTranspose block_dim args t kind,+      kernelUses = uses,+      kernelNumGroups = map untyped num_groups,+      kernelGroupSize = map untyped group_size,+      kernelName = nameFromString name,+      kernelFailureTolerant = True+    }+  where+    pad2DBytes k = k * (k + 1) * primByteSize t+    block_size =+      bytes $+        case kind of+          TransposeSmall -> 1 :: TExp Int64+          -- Not used, but AMD's OpenCL+          -- does not like zero-size+          -- local memory.+          TransposeNormal -> fromInteger $ pad2DBytes $ 2 * block_dim_int+          TransposeLowWidth -> fromInteger $ pad2DBytes block_dim_int+          TransposeLowHeight -> fromInteger $ pad2DBytes block_dim_int+    block_dim = fromInteger block_dim_int :: TExp Int32 -        (odata, basic_odata_offset, idata, basic_idata_offset,-         width, height,-         mulx, muly, num_arrays,-         block) = args+    ( odata,+      basic_odata_offset,+      idata,+      basic_idata_offset,+      width,+      height,+      mulx,+      muly,+      num_arrays,+      block+      ) = args -        (num_groups, group_size) =-          case kind of-            TransposeSmall ->-              ([(num_arrays * width * height) `divUp` (block_dim * block_dim)],-               [block_dim * block_dim])-            TransposeLowWidth ->-              lowDimKernelAndGroupSize block_dim num_arrays width $ height `divUp` muly-            TransposeLowHeight ->-              lowDimKernelAndGroupSize block_dim num_arrays (width `divUp` mulx) height-            TransposeNormal ->-              let actual_dim = block_dim*2-              in ( [ width `divUp` actual_dim-                   , height `divUp` actual_dim-                   , num_arrays]-                 , [actual_dim, actual_dim `quot` elemsPerThread, 1])+    (num_groups, group_size) =+      case kind of+        TransposeSmall ->+          ( [(num_arrays * width * height) `divUp` (block_dim * block_dim)],+            [block_dim * block_dim]+          )+        TransposeLowWidth ->+          lowDimKernelAndGroupSize block_dim num_arrays width $ height `divUp` muly+        TransposeLowHeight ->+          lowDimKernelAndGroupSize block_dim num_arrays (width `divUp` mulx) height+        TransposeNormal ->+          let actual_dim = block_dim * 2+           in ( [ width `divUp` actual_dim,+                  height `divUp` actual_dim,+                  num_arrays+                ],+                [actual_dim, actual_dim `quot` elemsPerThread, 1]+              ) -        uses = map (`ScalarUse` int32)-               (namesToList $ mconcat $ map freeIn-                [basic_odata_offset, basic_idata_offset, num_arrays,-                 width, height, mulx, muly]) ++-               map MemoryUse [odata, idata]+    uses =+      map+        (`ScalarUse` int32)+        ( namesToList $+            mconcat $+              map+                freeIn+                [ basic_odata_offset,+                  basic_idata_offset,+                  num_arrays,+                  width,+                  height,+                  mulx,+                  muly+                ]+        )+        ++ map MemoryUse [odata, idata] -        name =-          case kind of TransposeSmall -> desc ++ "_small"-                       TransposeLowHeight -> desc ++ "_low_height"-                       TransposeLowWidth -> desc ++ "_low_width"-                       TransposeNormal -> desc+    name =+      case kind of+        TransposeSmall -> desc ++ "_small"+        TransposeLowHeight -> desc ++ "_low_height"+        TransposeLowWidth -> desc ++ "_low_width"+        TransposeNormal -> desc -lowDimKernelAndGroupSize :: Exp -> Exp -> Exp -> Exp -> ([Exp], [Exp])+lowDimKernelAndGroupSize ::+  TExp Int32 ->+  TExp Int32 ->+  TExp Int32 ->+  TExp Int32 ->+  ([TExp Int32], [TExp Int32]) lowDimKernelAndGroupSize block_dim num_arrays x_elems y_elems =-  ([x_elems `divUp` block_dim,-    y_elems `divUp` block_dim,-    num_arrays],-   [block_dim, block_dim, 1])+  ( [ x_elems `divUp` block_dim,+      y_elems `divUp` block_dim,+      num_arrays+    ],+    [block_dim, block_dim, 1]+  )
src/Futhark/CodeGen/ImpGen/OpenCL.hs view
@@ -1,14 +1,14 @@ module Futhark.CodeGen.ImpGen.OpenCL-  ( compileProg-  , Warnings-  ) where+  ( compileProg,+    Warnings,+  )+where  import Data.Bifunctor (second)--import Futhark.IR.KernelsMem import qualified Futhark.CodeGen.ImpCode.OpenCL as OpenCL import Futhark.CodeGen.ImpGen.Kernels import Futhark.CodeGen.ImpGen.Kernels.ToOpenCL+import Futhark.IR.KernelsMem import Futhark.MonadFreshNames  compileProg :: MonadFreshNames m => Prog KernelsMem -> m (Warnings, OpenCL.Program)
src/Futhark/CodeGen/ImpGen/Sequential.hs view
@@ -1,11 +1,12 @@-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+ -- | Compile Futhark to sequential imperative code. module Futhark.CodeGen.ImpGen.Sequential-  ( compileProg-  , ImpGen.Warnings+  ( compileProg,+    ImpGen.Warnings,   )-  where+where  import qualified Futhark.CodeGen.ImpCode.Sequential as Imp import qualified Futhark.CodeGen.ImpGen as ImpGen@@ -15,7 +16,8 @@ -- | Compile a 'SeqMem' program to sequential imperative code. compileProg :: MonadFreshNames m => Prog SeqMem -> m (ImpGen.Warnings, Imp.Program) compileProg = ImpGen.compileProg () ops Imp.DefaultSpace-  where ops = ImpGen.defaultOperations opCompiler-        opCompiler dest (Alloc e space) =-          ImpGen.compileAlloc dest e space-        opCompiler _ (Inner ()) = pure ()+  where+    ops = ImpGen.defaultOperations opCompiler+    opCompiler dest (Alloc e space) =+      ImpGen.compileAlloc dest e space+    opCompiler _ (Inner ()) = pure ()
+ src/Futhark/CodeGen/ImpGen/Transpose.hs view
@@ -0,0 +1,214 @@+-- | A cache-oblivious sequential transposition for CPU execution.+-- Generates a recursive function.+module Futhark.CodeGen.ImpGen.Transpose+  ( mapTransposeFunction,+    transposeArgs,+  )+where++import Futhark.CodeGen.ImpCode+import Futhark.IR.Prop.Types+import Futhark.Util.IntegralExp+import Prelude hiding (quot)++-- | Take well-typed arguments to the transpose function and produce+-- the actual argument list.+transposeArgs ::+  PrimType ->+  VName ->+  Count Bytes (TExp Int64) ->+  VName ->+  Count Bytes (TExp Int64) ->+  TExp Int64 ->+  TExp Int64 ->+  TExp Int64 ->+  [Arg]+transposeArgs pt destmem destoffset srcmem srcoffset num_arrays m n =+  [ MemArg destmem,+    ExpArg $ untyped $ unCount destoffset `quot` primByteSize pt,+    MemArg srcmem,+    ExpArg $ untyped $ unCount srcoffset `quot` primByteSize pt,+    ExpArg $ untyped num_arrays,+    ExpArg $ untyped m,+    ExpArg $ untyped n,+    ExpArg $ untyped (0 :: TExp Int64),+    ExpArg $ untyped m,+    ExpArg $ untyped (0 :: TExp Int64),+    ExpArg $ untyped n+  ]++-- | We need to know the name of the function we are generating, as+-- this function is recursive.+mapTransposeFunction :: Name -> PrimType -> Function op+mapTransposeFunction fname pt =+  Function+    False+    []+    params+    ( mconcat+        [ dec r $ vi64 re - vi64 rb,+          dec c $ vi64 ce - vi64 cb,+          If (vi64 num_arrays .==. 1) doTranspose doMapTranspose+        ]+    )+    []+    []+  where+    params =+      [ memparam destmem,+        intparam destoffset,+        memparam srcmem,+        intparam srcoffset,+        intparam num_arrays,+        intparam m,+        intparam n,+        intparam cb,+        intparam ce,+        intparam rb,+        intparam re+      ]++    memparam v = MemParam v DefaultSpace+    intparam v = ScalarParam v int64++    [ destmem,+      destoffset,+      srcmem,+      srcoffset,+      num_arrays,+      n,+      m,+      rb,+      re,+      cb,+      ce,+      r,+      c,+      i,+      j+      ] =+        zipWith+          (VName . nameFromString)+          [ "destmem",+            "destoffset",+            "srcmem",+            "srcoffset",+            "num_arrays",+            "n",+            "m",+            "rb",+            "re",+            "cb",+            "ce",+            "r",+            "c",+            "i",+            "j" -- local+          ]+          [0 ..]++    dec v e = DeclareScalar v Nonvolatile int32 <> SetScalar v (untyped e)++    naiveTranspose =+      For j (untyped $ vi64 c) $+        For i (untyped $ vi64 r) $+          let i' = vi64 i + vi64 rb+              j' = vi64 j + vi64 cb+           in Write+                destmem+                (elements $ vi64 destoffset + j' * vi64 n + i')+                pt+                DefaultSpace+                Nonvolatile+                $ index+                  srcmem+                  (elements $ vi64 srcoffset + i' * vi64 m + j')+                  pt+                  DefaultSpace+                  Nonvolatile++    recArgs (cb', ce', rb', re') =+      [ MemArg destmem,+        ExpArg $ untyped $ vi64 destoffset,+        MemArg srcmem,+        ExpArg $ untyped $ vi64 srcoffset,+        ExpArg $ untyped $ vi64 num_arrays,+        ExpArg $ untyped $ vi64 m,+        ExpArg $ untyped $ vi64 n,+        ExpArg $ untyped cb',+        ExpArg $ untyped ce',+        ExpArg $ untyped rb',+        ExpArg $ untyped re'+      ]++    cutoff = 64 -- arbitrary+    doTranspose =+      mconcat+        [ If+            (vi64 r .<=. cutoff .&&. vi64 c .<=. cutoff)+            naiveTranspose+            $ If+              (vi64 r .>=. vi64 c)+              ( Call+                  []+                  fname+                  ( recArgs+                      ( vi64 cb,+                        vi64 ce,+                        vi64 rb,+                        vi64 rb + (vi64 r `quot` 2)+                      )+                  )+                  <> Call+                    []+                    fname+                    ( recArgs+                        ( vi64 cb,+                          vi64 ce,+                          vi64 rb + vi64 r `quot` 2,+                          vi64 re+                        )+                    )+              )+              ( Call+                  []+                  fname+                  ( recArgs+                      ( vi64 cb,+                        vi64 cb + (vi64 c `quot` 2),+                        vi64 rb,+                        vi64 re+                      )+                  )+                  <> Call+                    []+                    fname+                    ( recArgs+                        ( vi64 cb + vi64 c `quot` 2,+                          vi64 ce,+                          vi64 rb,+                          vi64 re+                        )+                    )+              )+        ]++    doMapTranspose =+      -- In the map-transpose case, we assume that cb==rb==0, ce==m,+      -- re==n.+      For i (untyped $ vi64 num_arrays) $+        Call+          []+          fname+          [ MemArg destmem,+            ExpArg $ untyped $ vi64 destoffset + vi64 i * vi64 m * vi64 n,+            MemArg srcmem,+            ExpArg $ untyped $ vi64 srcoffset + vi64 i * vi64 m * vi64 n,+            ExpArg $ untyped (1 :: TExp Int64),+            ExpArg $ untyped $ vi64 m,+            ExpArg $ untyped $ vi64 n,+            ExpArg $ untyped $ vi64 cb,+            ExpArg $ untyped $ vi64 ce,+            ExpArg $ untyped $ vi64 rb,+            ExpArg $ untyped $ vi64 re+          ]
src/Futhark/CodeGen/OpenCL/Heuristics.hs view
@@ -10,13 +10,13 @@ -- We also use this to select reasonable default group sizes and group -- counts. module Futhark.CodeGen.OpenCL.Heuristics-       ( SizeHeuristic (..)-       , DeviceType (..)-       , WhichSize (..)-       , DeviceInfo (..)-       , sizeHeuristicsTable-       )-       where+  ( SizeHeuristic (..),+    DeviceType (..),+    WhichSize (..),+    DeviceInfo (..),+    sizeHeuristicsTable,+  )+where  import Futhark.Analysis.PrimExp import Futhark.Util.Pretty@@ -37,33 +37,32 @@ data WhichSize = LockstepWidth | NumGroups | GroupSize | TileSize | Threshold  -- | A heuristic for setting the default value for something.-data SizeHeuristic =-    SizeHeuristic { platformName :: String-                  , deviceType :: DeviceType-                  , heuristicSize :: WhichSize-                  , heuristicValue :: PrimExp DeviceInfo-                  }+data SizeHeuristic = SizeHeuristic+  { platformName :: String,+    deviceType :: DeviceType,+    heuristicSize :: WhichSize,+    heuristicValue :: TPrimExp Int32 DeviceInfo+  }  -- | All of our heuristics. sizeHeuristicsTable :: [SizeHeuristic] sizeHeuristicsTable =-  [ SizeHeuristic "NVIDIA CUDA" DeviceGPU LockstepWidth $ constant 32-  , SizeHeuristic "AMD Accelerated Parallel Processing" DeviceGPU LockstepWidth $ constant 32-  , SizeHeuristic "" DeviceGPU LockstepWidth $ constant 1-  -- We calculate the number of groups to aim for 1024 threads per-  -- compute unit if we also use the default group size.  This seems-  -- to perform well in practice.-  , SizeHeuristic "" DeviceGPU NumGroups $ 4 * max_compute_units-  , SizeHeuristic "" DeviceGPU GroupSize $ constant 256-  , SizeHeuristic "" DeviceGPU TileSize $ constant 32-  , SizeHeuristic "" DeviceGPU Threshold $ constant $ 32*1024--  , SizeHeuristic "" DeviceCPU LockstepWidth $ constant 1-  , SizeHeuristic "" DeviceCPU NumGroups max_compute_units-  , SizeHeuristic "" DeviceCPU GroupSize $ constant 32-  , SizeHeuristic "" DeviceCPU TileSize $ constant 4-  , SizeHeuristic "" DeviceCPU Threshold max_compute_units+  [ SizeHeuristic "NVIDIA CUDA" DeviceGPU LockstepWidth 32,+    SizeHeuristic "AMD Accelerated Parallel Processing" DeviceGPU LockstepWidth 32,+    SizeHeuristic "" DeviceGPU LockstepWidth 1,+    -- We calculate the number of groups to aim for 1024 threads per+    -- compute unit if we also use the default group size.  This seems+    -- to perform well in practice.+    SizeHeuristic "" DeviceGPU NumGroups $ 4 * max_compute_units,+    SizeHeuristic "" DeviceGPU GroupSize 256,+    SizeHeuristic "" DeviceGPU TileSize 32,+    SizeHeuristic "" DeviceGPU Threshold $ 32 * 1024,+    SizeHeuristic "" DeviceCPU LockstepWidth 1,+    SizeHeuristic "" DeviceCPU NumGroups max_compute_units,+    SizeHeuristic "" DeviceCPU GroupSize 32,+    SizeHeuristic "" DeviceCPU TileSize 4,+    SizeHeuristic "" DeviceCPU Threshold max_compute_units   ]-  where constant = ValueExp . IntValue . Int32Value-        max_compute_units =-          LeafExp (DeviceInfo "MAX_COMPUTE_UNITS") $ IntType Int32+  where+    max_compute_units =+      TPrimExp $ LeafExp (DeviceInfo "MAX_COMPUTE_UNITS") $ IntType Int32
src/Futhark/CodeGen/SetDefaultSpace.hs view
@@ -3,9 +3,9 @@ -- to GPU memory for most of the pipeline, but final code generation -- assumes that 'DefaultSpace' is CPU memory. module Futhark.CodeGen.SetDefaultSpace-       ( setDefaultSpace-       )-       where+  ( setDefaultSpace,+  )+where  import Futhark.CodeGen.ImpCode @@ -14,18 +14,22 @@ setDefaultSpace :: Space -> Definitions op -> Definitions op setDefaultSpace space (Definitions (Constants ps consts) (Functions fundecs)) =   Definitions-  (Constants (map (setParamSpace space) ps) (setBodySpace space consts))-  (Functions [ (fname, setFunctionSpace space func)-             | (fname, func) <- fundecs ])+    (Constants (map (setParamSpace space) ps) (setBodySpace space consts))+    ( Functions+        [ (fname, setFunctionSpace space func)+          | (fname, func) <- fundecs+        ]+    )  setFunctionSpace :: Space -> Function op -> Function op setFunctionSpace space (Function entry outputs inputs body results args) =-  Function entry-  (map (setParamSpace space) outputs)-  (map (setParamSpace space) inputs)-  (setBodySpace space body)-  (map (setExtValueSpace space) results)-  (map (setExtValueSpace space) args)+  Function+    entry+    (map (setParamSpace space) outputs)+    (map (setParamSpace space) inputs)+    (setBodySpace space body)+    (map (setExtValueSpace space) results)+    (map (setExtValueSpace space) args)  setParamSpace :: Space -> Param -> Param setParamSpace space (MemParam name DefaultSpace) =@@ -47,7 +51,7 @@  setBodySpace :: Space -> Code op -> Code op setBodySpace space (Allocate v e old_space) =-  Allocate v (fmap (setExpSpace space) e) $ setSpace space old_space+  Allocate v (fmap (setTExpSpace space) e) $ setSpace space old_space setBodySpace space (Free v old_space) =   Free v $ setSpace space old_space setBodySpace space (DeclareMem name old_space) =@@ -56,22 +60,32 @@   DeclareArray name space t vs setBodySpace space (Copy dest dest_offset dest_space src src_offset src_space n) =   Copy-  dest (fmap (setExpSpace space) dest_offset) dest_space'-  src (fmap (setExpSpace space) src_offset) src_space' $-  fmap (setExpSpace space) n-  where dest_space' = setSpace space dest_space-        src_space' = setSpace space src_space+    dest+    (fmap (setTExpSpace space) dest_offset)+    dest_space'+    src+    (fmap (setTExpSpace space) src_offset)+    src_space'+    $ fmap (setTExpSpace space) n+  where+    dest_space' = setSpace space dest_space+    src_space' = setSpace space src_space setBodySpace space (Write dest dest_offset bt dest_space vol e) =-  Write dest (fmap (setExpSpace space) dest_offset) bt (setSpace space dest_space)-  vol (setExpSpace space e)+  Write+    dest+    (fmap (setTExpSpace space) dest_offset)+    bt+    (setSpace space dest_space)+    vol+    (setExpSpace space e) setBodySpace space (c1 :>>: c2) =   setBodySpace space c1 :>>: setBodySpace space c2-setBodySpace space (For i it e body) =-  For i it (setExpSpace space e) $ setBodySpace space body+setBodySpace space (For i e body) =+  For i (setExpSpace space e) $ setBodySpace space body setBodySpace space (While e body) =-  While (setExpSpace space e) $ setBodySpace space body+  While (setTExpSpace space e) $ setBodySpace space body setBodySpace space (If e c1 c2) =-  If (setExpSpace space e) (setBodySpace space c1) (setBodySpace space c2)+  If (setTExpSpace space e) (setBodySpace space c1) (setBodySpace space c2) setBodySpace space (Comment s c) =   Comment s $ setBodySpace space c setBodySpace _ Skip =@@ -84,8 +98,9 @@   SetMem to from $ setSpace space old_space setBodySpace space (Call dests fname args) =   Call dests fname $ map setArgSpace args-  where setArgSpace (MemArg m) = MemArg m-        setArgSpace (ExpArg e) = ExpArg $ setExpSpace space e+  where+    setArgSpace (MemArg m) = MemArg m+    setArgSpace (ExpArg e) = ExpArg $ setExpSpace space e setBodySpace space (Assert e msg loc) =   Assert (setExpSpace space e) msg loc setBodySpace space (DebugPrint s v) =@@ -95,10 +110,14 @@  setExpSpace :: Space -> Exp -> Exp setExpSpace space = fmap setLeafSpace-  where setLeafSpace (Index mem i bt DefaultSpace vol) =-          Index mem i bt space vol-        setLeafSpace e = e+  where+    setLeafSpace (Index mem i bt DefaultSpace vol) =+      Index mem i bt space vol+    setLeafSpace e = e +setTExpSpace :: Space -> TExp t -> TExp t+setTExpSpace space = TPrimExp . setExpSpace space . untyped+ setSpace :: Space -> Space -> Space setSpace space DefaultSpace = space-setSpace _     space        = space+setSpace _ space = space
src/Futhark/Compiler.hs view
@@ -1,61 +1,62 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Safe #-} {-# LANGUAGE Strict #-}+ -- | High-level API for invoking the Futhark compiler. module Futhark.Compiler-       (-         runPipelineOnProgram-       , runCompilerOnProgram--       , FutharkConfig (..)-       , newFutharkConfig-       , dumpError-       , handleWarnings--       , module Futhark.Compiler.Program-       , readProgram-       , readLibrary-       , readProgramOrDie-       )+  ( runPipelineOnProgram,+    runCompilerOnProgram,+    FutharkConfig (..),+    newFutharkConfig,+    dumpError,+    handleWarnings,+    module Futhark.Compiler.Program,+    readProgram,+    readLibrary,+    readProgramOrDie,+  ) where  import Control.Monad-import Control.Monad.Reader import Control.Monad.Except-import System.Exit (exitWith, ExitCode(..))-import System.IO import qualified Data.Text.IO as T- import qualified Futhark.Analysis.Alias as Alias-import Futhark.Internalise-import Futhark.Pipeline-import Futhark.MonadFreshNames+import Futhark.Compiler.Program import Futhark.IR import qualified Futhark.IR.SOACS as I+import Futhark.Internalise+import Futhark.MonadFreshNames+import Futhark.Pipeline import qualified Futhark.TypeCheck as I-import Futhark.Compiler.Program import Futhark.Util.Log-import Futhark.Util.Pretty (prettyText, ppr)+import Futhark.Util.Pretty (ppr, prettyText)+import System.Exit (ExitCode (..), exitWith)+import System.IO  -- | The compiler configuration.  This only contains options related -- to core compiler functionality, such as reading the initial program -- and running passes.  Options related to code generation are handled -- elsewhere. data FutharkConfig = FutharkConfig-                     { futharkVerbose :: (Verbosity, Maybe FilePath)-                     , futharkWarn :: Bool -- ^ Warn if True.-                     , futharkWerror :: Bool -- ^ If true, error on any warnings.-                     , futharkSafe :: Bool -- ^ If True, ignore @unsafe@.-                     }+  { futharkVerbose :: (Verbosity, Maybe FilePath),+    -- | Warn if True.+    futharkWarn :: Bool,+    -- | If true, error on any warnings.+    futharkWerror :: Bool,+    -- | If True, ignore @unsafe@.+    futharkSafe :: Bool+  }  -- | The default compiler configuration. newFutharkConfig :: FutharkConfig-newFutharkConfig = FutharkConfig { futharkVerbose = (NotVerbose, Nothing)-                                 , futharkWarn = True-                                 , futharkWerror = False-                                 , futharkSafe = False-                                 }+newFutharkConfig =+  FutharkConfig+    { futharkVerbose = (NotVerbose, Nothing),+      futharkWarn = True,+      futharkWerror = False,+      futharkSafe = False+    }  -- | Print a compiler error to stdout.  The 'FutharkConfig' controls -- to which degree auxiliary information (e.g. the failing program) is@@ -75,19 +76,24 @@       T.hPutStrLn stderr "Known compiler limitation encountered.  Sorry."       T.hPutStrLn stderr "Revise your program or try a different Futhark compiler."       report s info-  where report s info = do-          T.hPutStrLn stderr s-          when (fst (futharkVerbose config) > NotVerbose) $-            maybe (T.hPutStr stderr) T.writeFile-            (snd (futharkVerbose config)) $ info <> "\n"+  where+    report s info = do+      T.hPutStrLn stderr s+      when (fst (futharkVerbose config) > NotVerbose) $+        maybe+          (T.hPutStr stderr)+          T.writeFile+          (snd (futharkVerbose config))+          $ info <> "\n"  -- | Read a program from the given 'FilePath', run the given -- 'Pipeline', and finish up with the given 'Action'.-runCompilerOnProgram :: FutharkConfig-                     -> Pipeline I.SOACS lore-                     -> Action lore-                     -> FilePath-                     -> IO ()+runCompilerOnProgram ::+  FutharkConfig ->+  Pipeline I.SOACS lore ->+  Action lore ->+  FilePath ->+  IO () runCompilerOnProgram config pipeline action file = do   res <- runFutharkM compile $ fst $ futharkVerbose config   case res of@@ -96,25 +102,27 @@       exitWith $ ExitFailure 2     Right () ->       return ()-  where compile = do-          prog <- runPipelineOnProgram config pipeline file-          when ((>NotVerbose) . fst $ futharkVerbose config) $-            logMsg $ "Running action " ++ actionName action-          actionProcedure action prog-          when ((>NotVerbose) . fst $ futharkVerbose config) $-            logMsg ("Done." :: String)+  where+    compile = do+      prog <- runPipelineOnProgram config pipeline file+      when ((> NotVerbose) . fst $ futharkVerbose config) $+        logMsg $ "Running action " ++ actionName action+      actionProcedure action prog+      when ((> NotVerbose) . fst $ futharkVerbose config) $+        logMsg ("Done." :: String)  -- | Read a program from the given 'FilePath', run the given -- 'Pipeline', and return it.-runPipelineOnProgram :: FutharkConfig-                     -> Pipeline I.SOACS tolore-                     -> FilePath-                     -> FutharkM (Prog tolore)+runPipelineOnProgram ::+  FutharkConfig ->+  Pipeline I.SOACS tolore ->+  FilePath ->+  FutharkM (Prog tolore) runPipelineOnProgram config pipeline file = do   when (pipelineVerbose pipeline_config) $     logMsg ("Reading and type-checking source program" :: String)   (prog_imports, namesrc) <--    handleWarnings config $ (\(a,b,c) -> (a,(b,c))) <$> readProgram file+    handleWarnings config $ (\(a, b, c) -> (a, (b, c))) <$> readProgram file    putNameSource namesrc   when (pipelineVerbose pipeline_config) $@@ -124,27 +132,34 @@     logMsg ("Type-checking internalised program" :: String)   typeCheckInternalProgram int_prog   runPipeline pipeline pipeline_config int_prog-  where pipeline_config =-          PipelineConfig { pipelineVerbose = fst (futharkVerbose config) > NotVerbose-                         , pipelineValidate = True-                         }+  where+    pipeline_config =+      PipelineConfig+        { pipelineVerbose = fst (futharkVerbose config) > NotVerbose,+          pipelineValidate = True+        }  typeCheckInternalProgram :: I.Prog I.SOACS -> FutharkM () typeCheckInternalProgram prog =   case I.checkProg prog' of     Left err -> internalErrorS ("After internalisation:\n" ++ show err) (ppr prog')     Right () -> return ()-  where prog' = Alias.aliasAnalysis prog+  where+    prog' = Alias.aliasAnalysis prog  -- | Read and type-check a Futhark program, including all imports.-readProgram :: (MonadError CompilerError m, MonadIO m) =>-               FilePath -> m (Warnings, Imports, VNameSource)+readProgram ::+  (MonadError CompilerError m, MonadIO m) =>+  FilePath ->+  m (Warnings, Imports, VNameSource) readProgram = readLibrary . pure  -- | Read and type-check a collection of Futhark files, including all -- imports.-readLibrary :: (MonadError CompilerError m, MonadIO m) =>-               [FilePath] -> m (Warnings, Imports, VNameSource)+readLibrary ::+  (MonadError CompilerError m, MonadIO m) =>+  [FilePath] ->+  m (Warnings, Imports, VNameSource) readLibrary = readLibraryWithBasis emptyBasis  -- | Not verbose, and terminates process on error.
src/Futhark/Compiler/CLI.hs view
@@ -3,131 +3,177 @@ -- A small amount of flexibility is provided for backend-specific -- options. module Futhark.Compiler.CLI-       ( compilerMain-       , CompilerOption-       , CompilerMode(..)-       , module Futhark.Pipeline-       , module Futhark.Compiler-       )+  ( compilerMain,+    CompilerOption,+    CompilerMode (..),+    module Futhark.Pipeline,+    module Futhark.Compiler,+  ) where  import Control.Monad import Data.Maybe-import System.FilePath-import System.Console.GetOpt-import System.IO--import Futhark.Pipeline import Futhark.Compiler import Futhark.IR (Prog) import Futhark.IR.SOACS (SOACS)+import Futhark.Pipeline import Futhark.Util.Options+import System.Console.GetOpt+import System.FilePath+import System.IO  -- | Run a parameterised Futhark compiler, where @cfg@ is a user-given -- configuration type.  Call this from @main@.-compilerMain :: cfg -- ^ Initial configuration.-             -> [CompilerOption cfg] -- ^ Options that affect the configuration.-             -> String -- ^ The short action name (e.g. "compile to C").-             -> String -- ^ The longer action description.-             -> Pipeline SOACS lore -- ^ The pipeline to use.-             -> (FutharkConfig -> cfg -> CompilerMode -> FilePath -> Prog lore-                 -> FutharkM ())-             -- ^ The action to take on the result of the pipeline.-             -> String -- ^ Program name-             -> [String] -- ^ Command line arguments.-             -> IO ()+compilerMain ::+  -- | Initial configuration.+  cfg ->+  -- | Options that affect the configuration.+  [CompilerOption cfg] ->+  -- | The short action name (e.g. "compile to C").+  String ->+  -- | The longer action description.+  String ->+  -- | The pipeline to use.+  Pipeline SOACS lore ->+  -- | The action to take on the result of the pipeline.+  ( FutharkConfig ->+    cfg ->+    CompilerMode ->+    FilePath ->+    Prog lore ->+    FutharkM ()+  ) ->+  -- | Program name+  String ->+  -- | Command line arguments.+  [String] ->+  IO () compilerMain cfg cfg_opts name desc pipeline doIt prog args = do   hSetEncoding stdout utf8   hSetEncoding stderr utf8-  mainWithOptions (newCompilerConfig cfg) (commandLineOptions ++ map wrapOption cfg_opts)-    "options... <program.fut>" inspectNonOptions prog args-  where inspectNonOptions [file] config = Just $ compile config file-        inspectNonOptions _      _      = Nothing+  mainWithOptions+    (newCompilerConfig cfg)+    (commandLineOptions ++ map wrapOption cfg_opts)+    "options... <program.fut>"+    inspectNonOptions+    prog+    args+  where+    inspectNonOptions [file] config = Just $ compile config file+    inspectNonOptions _ _ = Nothing -        compile config filepath =-          runCompilerOnProgram (futharkConfig config)-          pipeline (action config filepath) filepath+    compile config filepath =+      runCompilerOnProgram+        (futharkConfig config)+        pipeline+        (action config filepath)+        filepath -        action config filepath =-          Action { actionName = name-                 , actionDescription = desc-                 , actionProcedure =-                     doIt (futharkConfig config)-                          (compilerConfig config)-                          (compilerMode config)-                          (outputFilePath filepath config)-                 }+    action config filepath =+      Action+        { actionName = name,+          actionDescription = desc,+          actionProcedure =+            doIt+              (futharkConfig config)+              (compilerConfig config)+              (compilerMode config)+              (outputFilePath filepath config)+        }  -- | An option that modifies the configuration of type @cfg@. type CompilerOption cfg = OptDescr (Either (IO ()) (cfg -> cfg)) -type CoreCompilerOption cfg = OptDescr (Either-                                        (IO ())-                                        (CompilerConfig cfg -> CompilerConfig cfg))+type CoreCompilerOption cfg =+  OptDescr+    ( Either+        (IO ())+        (CompilerConfig cfg -> CompilerConfig cfg)+    )  commandLineOptions :: [CoreCompilerOption cfg] commandLineOptions =-  [ Option "o" []-    (ReqArg (\filename -> Right $ \config -> config { compilerOutput = Just filename })-     "FILE")-    "Name of the compiled binary."-  , Option "v" ["verbose"]-    (OptArg (Right . incVerbosity) "FILE")-    "Print verbose output on standard error; wrong program to FILE."-  , Option [] ["library"]-    (NoArg $ Right $ \config -> config { compilerMode = ToLibrary })-    "Generate a library instead of an executable."-  , Option [] ["executable"]-    (NoArg $ Right $ \config -> config { compilerMode = ToExecutable })-    "Generate an executable instead of a library (set by default)."-  , Option "w" []-    (NoArg $ Right $ \config -> config { compilerWarn = False })-    "Disable all warnings."-  , Option [] ["Werror"]-    (NoArg $ Right $ \config -> config { compilerWerror = True })-    "Treat warnings as errors."-  , Option [] ["safe"]-    (NoArg $ Right $ \config -> config { compilerSafe = True })-    "Ignore 'unsafe' in code."+  [ Option+      "o"+      []+      ( ReqArg+          (\filename -> Right $ \config -> config {compilerOutput = Just filename})+          "FILE"+      )+      "Name of the compiled binary.",+    Option+      "v"+      ["verbose"]+      (OptArg (Right . incVerbosity) "FILE")+      "Print verbose output on standard error; wrong program to FILE.",+    Option+      []+      ["library"]+      (NoArg $ Right $ \config -> config {compilerMode = ToLibrary})+      "Generate a library instead of an executable.",+    Option+      []+      ["executable"]+      (NoArg $ Right $ \config -> config {compilerMode = ToExecutable})+      "Generate an executable instead of a library (set by default).",+    Option+      "w"+      []+      (NoArg $ Right $ \config -> config {compilerWarn = False})+      "Disable all warnings.",+    Option+      []+      ["Werror"]+      (NoArg $ Right $ \config -> config {compilerWerror = True})+      "Treat warnings as errors.",+    Option+      []+      ["safe"]+      (NoArg $ Right $ \config -> config {compilerSafe = True})+      "Ignore 'unsafe' in code."   ]  wrapOption :: CompilerOption cfg -> CoreCompilerOption cfg wrapOption = fmap wrap-  where wrap f = do-          g <- f-          return $ \cfg -> cfg { compilerConfig = g (compilerConfig cfg) }+  where+    wrap f = do+      g <- f+      return $ \cfg -> cfg {compilerConfig = g (compilerConfig cfg)}  incVerbosity :: Maybe FilePath -> CompilerConfig cfg -> CompilerConfig cfg incVerbosity file cfg =-  cfg { compilerVerbose = (v, file `mplus` snd (compilerVerbose cfg)) }-  where v = case fst $ compilerVerbose cfg of-              NotVerbose -> Verbose-              Verbose -> VeryVerbose-              VeryVerbose -> VeryVerbose+  cfg {compilerVerbose = (v, file `mplus` snd (compilerVerbose cfg))}+  where+    v = case fst $ compilerVerbose cfg of+      NotVerbose -> Verbose+      Verbose -> VeryVerbose+      VeryVerbose -> VeryVerbose -data CompilerConfig cfg =-  CompilerConfig { compilerOutput :: Maybe FilePath-                 , compilerVerbose :: (Verbosity, Maybe FilePath)-                 , compilerMode :: CompilerMode-                 , compilerWerror :: Bool-                 , compilerSafe :: Bool-                 , compilerWarn :: Bool-                 , compilerConfig :: cfg-                 }+data CompilerConfig cfg = CompilerConfig+  { compilerOutput :: Maybe FilePath,+    compilerVerbose :: (Verbosity, Maybe FilePath),+    compilerMode :: CompilerMode,+    compilerWerror :: Bool,+    compilerSafe :: Bool,+    compilerWarn :: Bool,+    compilerConfig :: cfg+  }  -- | Are we compiling a library or an executable? data CompilerMode = ToLibrary | ToExecutable deriving (Eq, Ord, Show)  -- | The configuration of the compiler. newCompilerConfig :: cfg -> CompilerConfig cfg-newCompilerConfig x = CompilerConfig { compilerOutput = Nothing-                                     , compilerVerbose = (NotVerbose, Nothing)-                                     , compilerMode = ToExecutable-                                     , compilerWerror = False-                                     , compilerSafe = False-                                     , compilerWarn = True-                                     , compilerConfig = x-                                     }+newCompilerConfig x =+  CompilerConfig+    { compilerOutput = Nothing,+      compilerVerbose = (NotVerbose, Nothing),+      compilerMode = ToExecutable,+      compilerWerror = False,+      compilerSafe = False,+      compilerWarn = True,+      compilerConfig = x+    }  outputFilePath :: FilePath -> CompilerConfig cfg -> FilePath outputFilePath srcfile =@@ -135,8 +181,9 @@  futharkConfig :: CompilerConfig cfg -> FutharkConfig futharkConfig config =-  newFutharkConfig { futharkVerbose = compilerVerbose config-                   , futharkWerror = compilerWerror config-                   , futharkSafe = compilerSafe config-                   , futharkWarn = compilerWarn config-                   }+  newFutharkConfig+    { futharkVerbose = compilerVerbose config,+      futharkWerror = compilerWerror config,+      futharkSafe = compilerSafe config,+      futharkWarn = compilerWarn config+    }
src/Futhark/Compiler/Program.hs view
@@ -1,84 +1,94 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-}+ -- | Low-level compilation parts.  Look at "Futhark.Compiler" for a -- more high-level API. module Futhark.Compiler.Program-       ( readLibraryWithBasis-       , readImports-       , Imports-       , FileModule(..)-       , E.Warnings--       , Basis(..)-       , emptyBasis-       )+  ( readLibraryWithBasis,+    readImports,+    Imports,+    FileModule (..),+    E.Warnings,+    Basis (..),+    emptyBasis,+  ) where  import Control.Exception import Control.Monad+import Control.Monad.Except import Control.Monad.Reader import Control.Monad.State-import Control.Monad.Except-import Data.Maybe import Data.List (intercalate)-import qualified System.FilePath.Posix as Posix-import System.IO.Error+import Data.Maybe import qualified Data.Text as T import qualified Data.Text.IO as T- import Futhark.Error import Futhark.FreshNames-import Language.Futhark.Parser+import Futhark.Util.Pretty (ppr) import qualified Language.Futhark as E-import qualified Language.Futhark.TypeChecker as E-import Language.Futhark.Semantic+import Language.Futhark.Parser import Language.Futhark.Prelude-import Futhark.Util.Pretty (ppr)+import Language.Futhark.Semantic+import qualified Language.Futhark.TypeChecker as E+import qualified System.FilePath.Posix as Posix+import System.IO.Error  -- | A little monad for reading and type-checking a Futhark program. type CompilerM m = ReaderT [FilePath] (StateT ReaderState m) -data ReaderState = ReaderState { alreadyImported :: Imports-                               , nameSource :: VNameSource-                               , warnings :: E.Warnings-                               }+data ReaderState = ReaderState+  { alreadyImported :: Imports,+    nameSource :: VNameSource,+    warnings :: E.Warnings+  }  -- | Pre-typechecked imports, including a starting point for the name source.-data Basis = Basis { basisImports :: Imports-                   , basisNameSource :: VNameSource-                   , basisRoots :: [String]-                     -- ^ Files that should be implicitly opened.-                   }+data Basis = Basis+  { basisImports :: Imports,+    basisNameSource :: VNameSource,+    -- | Files that should be implicitly opened.+    basisRoots :: [String]+  }  -- | A basis that contains no imports, and has a properly initialised -- name source. emptyBasis :: Basis-emptyBasis = Basis { basisImports = mempty-                   , basisNameSource = src-                   , basisRoots = mempty-                   }-  where src = newNameSource $ E.maxIntrinsicTag + 1+emptyBasis =+  Basis+    { basisImports = mempty,+      basisNameSource = src,+      basisRoots = mempty+    }+  where+    src = newNameSource $ E.maxIntrinsicTag + 1 -readImport :: (MonadError CompilerError m, MonadIO m) =>-              [ImportName] -> ImportName -> CompilerM m ()+readImport ::+  (MonadError CompilerError m, MonadIO m) =>+  [ImportName] ->+  ImportName ->+  CompilerM m () readImport steps include   | include `elem` steps =-      externalErrorS $-      "Import cycle: " ++ intercalate " -> "-      (map includeToString $ reverse $ include:steps)+    externalErrorS $+      "Import cycle: "+        ++ intercalate+          " -> "+          (map includeToString $ reverse $ include : steps)   | otherwise = do-      already_done <- gets $ isJust . lookup (includeToString include) . alreadyImported+    already_done <- gets $ isJust . lookup (includeToString include) . alreadyImported -      unless already_done $-        uncurry (handleFile steps include) =<< readImportFile include+    unless already_done $+      uncurry (handleFile steps include) =<< readImportFile include -handleFile :: (MonadIO m, MonadError CompilerError m) =>-              [ImportName]-           -> ImportName-           -> T.Text-           -> FilePath-           -> CompilerM m ()+handleFile ::+  (MonadIO m, MonadError CompilerError m) =>+  [ImportName] ->+  ImportName ->+  T.Text ->+  FilePath ->+  CompilerM m () handleFile steps include file_contents file_name = do   prog <- case parseFuthark file_name file_contents of     Left err -> externalErrorS $ show err@@ -98,94 +108,122 @@       externalError $ ppr err     Right (m, ws, src') ->       modify $ \s ->-        s { alreadyImported = (includeToString include,m) : imports-          , nameSource      = src'-          , warnings        = warnings s <> ws+        s+          { alreadyImported = (includeToString include, m) : imports,+            nameSource = src',+            warnings = warnings s <> ws           }-  where steps' = include:steps+  where+    steps' = include : steps  readFileSafely :: String -> IO (Maybe (Either String (String, T.Text))) readFileSafely filepath =   (Just . Right . (filepath,) <$> T.readFile filepath) `catch` couldNotRead-  where couldNotRead e-          | isDoesNotExistError e =-              return Nothing-          | otherwise             =-              return $ Just $ Left $ show e+  where+    couldNotRead e+      | isDoesNotExistError e =+        return Nothing+      | otherwise =+        return $ Just $ Left $ show e -readImportFile :: (MonadError CompilerError m, MonadIO m) =>-                  ImportName -> m (T.Text, FilePath)+readImportFile ::+  (MonadError CompilerError m, MonadIO m) =>+  ImportName ->+  m (T.Text, FilePath) readImportFile include = do   -- First we try to find a file of the given name in the search path,   -- then we look at the builtin library if we have to.  For the   -- builtins, we don't use the search path.   r <- liftIO $ readFileSafely $ includeToFilePath include   case (r, lookup prelude_str prelude) of-    (Just (Right (filepath,s)), _) -> return (s, filepath)-    (Just (Left e), _)  -> externalErrorS e-    (Nothing, Just t)   -> return (t, prelude_str)-    (Nothing, Nothing)  -> externalErrorS not_found-   where prelude_str = "/" Posix.</> includeToString include Posix.<.> "fut"+    (Just (Right (filepath, s)), _) -> return (s, filepath)+    (Just (Left e), _) -> externalErrorS e+    (Nothing, Just t) -> return (t, prelude_str)+    (Nothing, Nothing) -> externalErrorS not_found+  where+    prelude_str = "/" Posix.</> includeToString include Posix.<.> "fut" -         not_found =-           "Error at " ++ E.locStr (E.srclocOf include) ++-           ": could not find import '" ++ includeToString include ++ "'."+    not_found =+      "Error at " ++ E.locStr (E.srclocOf include)+        ++ ": could not find import '"+        ++ includeToString include+        ++ "'."  -- | Read Futhark files from some basis, and printing log messages if -- the first parameter is True.-readLibraryWithBasis :: (MonadError CompilerError m, MonadIO m) =>-                        Basis -> [FilePath]-                     -> m (E.Warnings,-                           Imports,-                           VNameSource)+readLibraryWithBasis ::+  (MonadError CompilerError m, MonadIO m) =>+  Basis ->+  [FilePath] ->+  m+    ( E.Warnings,+      Imports,+      VNameSource+    ) readLibraryWithBasis builtin fps = do-  (_, imps, src) <- runCompilerM builtin $-    readImport [] $ mkInitialImport "/prelude/prelude"+  (_, imps, src) <-+    runCompilerM builtin $+      readImport [] $ mkInitialImport "/prelude/prelude"   let basis = Basis imps src ["/prelude/prelude"]   readLibrary' basis fps  -- | Read and type-check a Futhark library (multiple files, relative -- to the same search path), including all imports.-readLibrary' :: (MonadError CompilerError m, MonadIO m) =>-                Basis -> [FilePath]-             -> m (E.Warnings,-                   Imports,-                   VNameSource)+readLibrary' ::+  (MonadError CompilerError m, MonadIO m) =>+  Basis ->+  [FilePath] ->+  m+    ( E.Warnings,+      Imports,+      VNameSource+    ) readLibrary' basis fps = runCompilerM basis $ mapM onFile fps-  where onFile fp =  do-          r <- liftIO $ readFileSafely fp-          case r of-            Just (Right (_, fs)) ->-              handleFile [] (mkInitialImport fp_name) fs fp-            Just (Left e) -> externalErrorS e-            Nothing -> externalErrorS $ fp ++ ": file not found."-            where (fp_name, _) = Posix.splitExtension fp+  where+    onFile fp = do+      r <- liftIO $ readFileSafely fp+      case r of+        Just (Right (_, fs)) ->+          handleFile [] (mkInitialImport fp_name) fs fp+        Just (Left e) -> externalErrorS e+        Nothing -> externalErrorS $ fp ++ ": file not found."+      where+        (fp_name, _) = Posix.splitExtension fp  -- | Read and type-check Futhark imports (no @.fut@ extension; may -- refer to baked-in prelude).  This is an exotic operation that -- probably only makes sense in an interactive environment.-readImports :: (MonadError CompilerError m, MonadIO m) =>-               Basis -> [ImportName]-            -> m (E.Warnings,-                  Imports,-                  VNameSource)+readImports ::+  (MonadError CompilerError m, MonadIO m) =>+  Basis ->+  [ImportName] ->+  m+    ( E.Warnings,+      Imports,+      VNameSource+    ) readImports basis imps =   runCompilerM basis $ mapM (readImport []) imps -runCompilerM :: Monad m =>-                Basis -> CompilerM m a-             -> m (E.Warnings, [(String, FileModule)], VNameSource)+runCompilerM ::+  Monad m =>+  Basis ->+  CompilerM m a ->+  m (E.Warnings, [(String, FileModule)], VNameSource) runCompilerM (Basis imports src roots) m = do   let s = ReaderState (reverse imports) src mempty   s' <- execStateT (runReaderT m roots) s-  return (warnings s',-          reverse $ alreadyImported s',-          nameSource s')+  return+    ( warnings s',+      reverse $ alreadyImported s',+      nameSource s'+    )  prependRoots :: [FilePath] -> E.UncheckedProg -> E.UncheckedProg prependRoots roots (E.Prog doc ds) =   E.Prog doc $ map mkImport roots ++ ds-  where mkImport fp =-          -- We do not use ImportDec here, because we do not want the-          -- type checker to issue a warning about a redundant import.-          E.LocalDec (E.OpenDec (E.ModImport fp E.NoInfo mempty) mempty) mempty+  where+    mkImport fp =+      -- We do not use ImportDec here, because we do not want the+      -- type checker to issue a warning about a redundant import.+      E.LocalDec (E.OpenDec (E.ModImport fp E.NoInfo mempty) mempty) mempty
src/Futhark/Construct.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE Safe #-}+{-# LANGUAGE TypeFamilies #-}+ -- | = Constructing Futhark ASTs -- -- This module re-exports and defines a bunch of building blocks for@@ -52,82 +53,79 @@ -- (relatively) simple example of how to use these components.  As are -- some of the high-level building blocks in this very module. module Futhark.Construct-  ( letSubExp-  , letSubExps-  , letExp-  , letTupExp-  , letTupExp'-  , letInPlace--  , eSubExp-  , eIf-  , eIf'-  , eBinOp-  , eCmpOp-  , eConvOp-  , eNot-  , eSignum-  , eCopy-  , eAssert-  , eBody-  , eLambda-  , eRoundToMultipleOf-  , eSliceArray-  , eBlank-  , eAll--  , eOutOfBounds-  , eWriteArray--  , asIntZ, asIntS--  , resultBody-  , resultBodyM-  , insertStmsM-  , mapResult--  , foldBinOp-  , binOpLambda-  , cmpOpLambda-  , sliceDim-  , fullSlice-  , fullSliceNum-  , isFullSlice-  , sliceAt-  , ifCommon--  , module Futhark.Binder--  -- * Result types-  , instantiateShapes-  , instantiateShapes'-  , removeExistentials+  ( letSubExp,+    letSubExps,+    letExp,+    letTupExp,+    letTupExp',+    letInPlace,+    eSubExp,+    eIf,+    eIf',+    eBinOp,+    eCmpOp,+    eConvOp,+    eSignum,+    eCopy,+    eAssert,+    eBody,+    eLambda,+    eRoundToMultipleOf,+    eSliceArray,+    eBlank,+    eAll,+    eOutOfBounds,+    eWriteArray,+    asIntZ,+    asIntS,+    resultBody,+    resultBodyM,+    insertStmsM,+    mapResult,+    foldBinOp,+    binOpLambda,+    cmpOpLambda,+    sliceDim,+    fullSlice,+    fullSliceNum,+    isFullSlice,+    sliceAt,+    ifCommon,+    module Futhark.Binder, -  -- * Convenience-  , simpleMkLetNames+    -- * Result types+    instantiateShapes,+    instantiateShapes',+    removeExistentials, -  , ToExp(..)-  , toSubExp+    -- * Convenience+    simpleMkLetNames,+    ToExp (..),+    toSubExp,   ) where -import qualified Data.Map.Strict as M-import Data.List (sortOn) import Control.Monad.Identity import Control.Monad.State import Control.Monad.Writer--import Futhark.IR-import Futhark.MonadFreshNames+import Data.List (sortOn)+import qualified Data.Map.Strict as M import Futhark.Binder+import Futhark.IR -letSubExp :: MonadBinder m =>-             String -> Exp (Lore m) -> m SubExp+letSubExp ::+  MonadBinder m =>+  String ->+  Exp (Lore m) ->+  m SubExp letSubExp _ (BasicOp (SubExp se)) = return se letSubExp desc e = Var <$> letExp desc e -letExp :: MonadBinder m =>-          String -> Exp (Lore m) -> m VName+letExp ::+  MonadBinder m =>+  String ->+  Exp (Lore m) ->+  m VName letExp _ (BasicOp (SubExp (Var v))) =   return v letExp desc e = do@@ -136,22 +134,31 @@   letBindNames vs e   case vs of     [v] -> return v-    _   -> error $ "letExp: tuple-typed expression given:\n" ++ pretty e+    _ -> error $ "letExp: tuple-typed expression given:\n" ++ pretty e -letInPlace :: MonadBinder m =>-              String -> VName -> Slice SubExp -> Exp (Lore m)-           -> m VName+letInPlace ::+  MonadBinder m =>+  String ->+  VName ->+  Slice SubExp ->+  Exp (Lore m) ->+  m VName letInPlace desc src slice e = do   tmp <- letSubExp (desc ++ "_tmp") e   letExp desc $ BasicOp $ Update src slice tmp -letSubExps :: MonadBinder m =>-              String -> [Exp (Lore m)] -> m [SubExp]+letSubExps ::+  MonadBinder m =>+  String ->+  [Exp (Lore m)] ->+  m [SubExp] letSubExps desc = mapM $ letSubExp desc -letTupExp :: (MonadBinder m) =>-             String -> Exp (Lore m)-          -> m [VName]+letTupExp ::+  (MonadBinder m) =>+  String ->+  Exp (Lore m) ->+  m [VName] letTupExp _ (BasicOp (SubExp (Var v))) =   return [v] letTupExp name e = do@@ -160,26 +167,36 @@   letBindNames names e   return names -letTupExp' :: (MonadBinder m) =>-              String -> Exp (Lore m)-           -> m [SubExp]+letTupExp' ::+  (MonadBinder m) =>+  String ->+  Exp (Lore m) ->+  m [SubExp] letTupExp' _ (BasicOp (SubExp se)) = return [se] letTupExp' name ses = map Var <$> letTupExp name ses -eSubExp :: MonadBinder m =>-           SubExp -> m (Exp (Lore m))+eSubExp ::+  MonadBinder m =>+  SubExp ->+  m (Exp (Lore m)) eSubExp = pure . BasicOp . SubExp -eIf :: (MonadBinder m, BranchType (Lore m) ~ ExtType) =>-       m (Exp (Lore m)) -> m (Body (Lore m)) -> m (Body (Lore m))-    -> m (Exp (Lore m))+eIf ::+  (MonadBinder m, BranchType (Lore m) ~ ExtType) =>+  m (Exp (Lore m)) ->+  m (Body (Lore m)) ->+  m (Body (Lore m)) ->+  m (Exp (Lore m)) eIf ce te fe = eIf' ce te fe IfNormal  -- | As 'eIf', but an 'IfSort' can be given.-eIf' :: (MonadBinder m, BranchType (Lore m) ~ ExtType) =>-        m (Exp (Lore m)) -> m (Body (Lore m)) -> m (Body (Lore m))-     -> IfSort-     -> m (Exp (Lore m))+eIf' ::+  (MonadBinder m, BranchType (Lore m) ~ ExtType) =>+  m (Exp (Lore m)) ->+  m (Body (Lore m)) ->+  m (Body (Lore m)) ->+  IfSort ->+  m (Exp (Lore m)) eIf' ce te fe if_sort = do   ce' <- letSubExp "cond" =<< ce   te' <- insertStmsM te@@ -189,50 +206,61 @@   te'' <- addContextForBranch ts te'   fe'' <- addContextForBranch ts fe'   return $ If ce' te'' fe'' $ IfDec ts if_sort-  where addContextForBranch ts (Body _ stms val_res) = do-          body_ts <- extendedScope (traverse subExpType val_res) stmsscope-          let ctx_res = map snd $ sortOn fst $-                        M.toList $ shapeExtMapping ts body_ts-          mkBodyM stms $ ctx_res++val_res-            where stmsscope = scopeOf stms+  where+    addContextForBranch ts (Body _ stms val_res) = do+      body_ts <- extendedScope (traverse subExpType val_res) stmsscope+      let ctx_res =+            map snd $+              sortOn fst $+                M.toList $ shapeExtMapping ts body_ts+      mkBodyM stms $ ctx_res ++ val_res+      where+        stmsscope = scopeOf stms  -- The type of a body.  Watch out: this only works for the degenerate -- case where the body does not already return its context. bodyExtType :: (HasScope lore m, Monad m) => Body lore -> m [ExtType] bodyExtType (Body _ stms res) =-  existentialiseExtTypes (M.keys stmsscope) . staticShapes <$>-  extendedScope (traverse subExpType res) stmsscope-  where stmsscope = scopeOf stms+  existentialiseExtTypes (M.keys stmsscope) . staticShapes+    <$> extendedScope (traverse subExpType res) stmsscope+  where+    stmsscope = scopeOf stms -eBinOp :: MonadBinder m =>-          BinOp -> m (Exp (Lore m)) -> m (Exp (Lore m))-       -> m (Exp (Lore m))+eBinOp ::+  MonadBinder m =>+  BinOp ->+  m (Exp (Lore m)) ->+  m (Exp (Lore m)) ->+  m (Exp (Lore m)) eBinOp op x y = do   x' <- letSubExp "x" =<< x   y' <- letSubExp "y" =<< y   return $ BasicOp $ BinOp op x' y' -eCmpOp :: MonadBinder m =>-          CmpOp -> m (Exp (Lore m)) -> m (Exp (Lore m))-       -> m (Exp (Lore m))+eCmpOp ::+  MonadBinder m =>+  CmpOp ->+  m (Exp (Lore m)) ->+  m (Exp (Lore m)) ->+  m (Exp (Lore m)) eCmpOp op x y = do   x' <- letSubExp "x" =<< x   y' <- letSubExp "y" =<< y   return $ BasicOp $ CmpOp op x' y' -eConvOp :: MonadBinder m =>-           ConvOp -> m (Exp (Lore m))-        -> m (Exp (Lore m))+eConvOp ::+  MonadBinder m =>+  ConvOp ->+  m (Exp (Lore m)) ->+  m (Exp (Lore m)) eConvOp op x = do   x' <- letSubExp "x" =<< x   return $ BasicOp $ ConvOp op x' -eNot :: MonadBinder m =>-        m (Exp (Lore m)) -> m (Exp (Lore m))-eNot e = BasicOp . UnOp Not <$> (letSubExp "not_arg" =<< e)--eSignum :: MonadBinder m =>-        m (Exp (Lore m)) -> m (Exp (Lore m))+eSignum ::+  MonadBinder m =>+  m (Exp (Lore m)) ->+  m (Exp (Lore m)) eSignum em = do   e <- em   e' <- letSubExp "signum_arg" e@@ -243,70 +271,101 @@     _ ->       error $ "eSignum: operand " ++ pretty e ++ " has invalid type." -eCopy :: MonadBinder m =>-         m (Exp (Lore m)) -> m (Exp (Lore m))+eCopy ::+  MonadBinder m =>+  m (Exp (Lore m)) ->+  m (Exp (Lore m)) eCopy e = BasicOp . Copy <$> (letExp "copy_arg" =<< e) -eAssert :: MonadBinder m =>-         m (Exp (Lore m)) -> ErrorMsg SubExp -> SrcLoc -> m (Exp (Lore m))-eAssert e msg loc = do e' <- letSubExp "assert_arg" =<< e-                       return $ BasicOp $ Assert e' msg (loc, mempty)+eAssert ::+  MonadBinder m =>+  m (Exp (Lore m)) ->+  ErrorMsg SubExp ->+  SrcLoc ->+  m (Exp (Lore m))+eAssert e msg loc = do+  e' <- letSubExp "assert_arg" =<< e+  return $ BasicOp $ Assert e' msg (loc, mempty) -eBody :: (MonadBinder m) =>-         [m (Exp (Lore m))]-      -> m (Body (Lore m))+eBody ::+  (MonadBinder m) =>+  [m (Exp (Lore m))] ->+  m (Body (Lore m)) eBody es = insertStmsM $ do-             es' <- sequence es-             xs <- mapM (letTupExp "x") es'-             mkBodyM mempty $ map Var $ concat xs+  es' <- sequence es+  xs <- mapM (letTupExp "x") es'+  mkBodyM mempty $ map Var $ concat xs -eLambda :: MonadBinder m =>-           Lambda (Lore m) -> [m (Exp (Lore m))] -> m [SubExp]-eLambda lam args = do zipWithM_ bindParam (lambdaParams lam) args-                      bodyBind $ lambdaBody lam-  where bindParam param arg = letBindNames [paramName param] =<< arg+eLambda ::+  MonadBinder m =>+  Lambda (Lore m) ->+  [m (Exp (Lore m))] ->+  m [SubExp]+eLambda lam args = do+  zipWithM_ bindParam (lambdaParams lam) args+  bodyBind $ lambdaBody lam+  where+    bindParam param arg = letBindNames [paramName param] =<< arg -eRoundToMultipleOf :: MonadBinder m =>-                      IntType -> m (Exp (Lore m)) -> m (Exp (Lore m)) -> m (Exp (Lore m))+eRoundToMultipleOf ::+  MonadBinder m =>+  IntType ->+  m (Exp (Lore m)) ->+  m (Exp (Lore m)) ->+  m (Exp (Lore m)) eRoundToMultipleOf t x d =   ePlus x (eMod (eMinus d (eMod x d)) d)-  where eMod = eBinOp (SMod t Unsafe)-        eMinus = eBinOp (Sub t OverflowWrap)-        ePlus = eBinOp (Add t OverflowWrap)+  where+    eMod = eBinOp (SMod t Unsafe)+    eMinus = eBinOp (Sub t OverflowWrap)+    ePlus = eBinOp (Add t OverflowWrap)  -- | Construct an 'Index' expressions that slices an array with unit stride.-eSliceArray :: MonadBinder m =>-               Int -> VName -> m (Exp (Lore m)) -> m (Exp (Lore m))-            -> m (Exp (Lore m))+eSliceArray ::+  MonadBinder m =>+  Int ->+  VName ->+  m (Exp (Lore m)) ->+  m (Exp (Lore m)) ->+  m (Exp (Lore m)) eSliceArray d arr i n = do   arr_t <- lookupType arr-  let skips = map (slice (constant (0::Int32))) $ take d $ arrayDims arr_t+  let skips = map (slice (constant (0 :: Int64))) $ take d $ arrayDims arr_t   i' <- letSubExp "slice_i" =<< i   n' <- letSubExp "slice_n" =<< n   return $ BasicOp $ Index arr $ fullSlice arr_t $ skips ++ [slice i' n']-  where slice j m = DimSlice j m (constant (1::Int32))+  where+    slice j m = DimSlice j m (constant (1 :: Int64))  -- | Are these indexes out-of-bounds for the array?-eOutOfBounds :: MonadBinder m =>-                VName -> [m (Exp (Lore m))] -> m (Exp (Lore m))+eOutOfBounds ::+  MonadBinder m =>+  VName ->+  [m (Exp (Lore m))] ->+  m (Exp (Lore m)) eOutOfBounds arr is = do   arr_t <- lookupType arr   let ws = arrayDims arr_t   is' <- mapM (letSubExp "write_i") =<< sequence is   let checkDim w i = do-        less_than_zero <- letSubExp "less_than_zero" $-          BasicOp $ CmpOp (CmpSlt Int32) i (constant (0::Int32))-        greater_than_size <- letSubExp "greater_than_size" $-          BasicOp $ CmpOp (CmpSle Int32) w i+        less_than_zero <-+          letSubExp "less_than_zero" $+            BasicOp $ CmpOp (CmpSlt Int64) i (constant (0 :: Int64))+        greater_than_size <-+          letSubExp "greater_than_size" $+            BasicOp $ CmpOp (CmpSle Int64) w i         letSubExp "outside_bounds_dim" $           BasicOp $ BinOp LogOr less_than_zero greater_than_size   foldBinOp LogOr (constant False) =<< zipWithM checkDim ws is'  -- | Write to an index of the array, if within bounds.  Otherwise, -- nothing.  Produces the updated array.-eWriteArray :: (MonadBinder m, BranchType (Lore m) ~ ExtType) =>-               VName -> [m (Exp (Lore m))] -> m (Exp (Lore m))-            -> m (Exp (Lore m))+eWriteArray ::+  (MonadBinder m, BranchType (Lore m) ~ ExtType) =>+  VName ->+  [m (Exp (Lore m))] ->+  m (Exp (Lore m)) ->+  m (Exp (Lore m)) eWriteArray arr is v = do   arr_t <- lookupType arr   is' <- mapM (letSubExp "write_i") =<< sequence is@@ -317,19 +376,23 @@   outside_bounds_branch <- insertStmsM $ resultBodyM [Var arr]    in_bounds_branch <- insertStmsM $ do-    res <- letInPlace "write_out_inside_bounds" arr-           (fullSlice arr_t (map DimFix is')) $ BasicOp $ SubExp v'+    res <-+      letInPlace+        "write_out_inside_bounds"+        arr+        (fullSlice arr_t (map DimFix is'))+        $ BasicOp $ SubExp v'     resultBodyM [Var res]    return $     If outside_bounds outside_bounds_branch in_bounds_branch $-    ifCommon [arr_t]+      ifCommon [arr_t]  -- | Construct an unspecified value of the given type. eBlank :: MonadBinder m => Type -> m (Exp (Lore m)) eBlank (Prim t) = return $ BasicOp $ SubExp $ Constant $ blankPrimValue t eBlank (Array t shape _) = return $ BasicOp $ Scratch t $ shapeDims shape-eBlank Mem{} = error "eBlank: cannot create blank memory"+eBlank Mem {} = error "eBlank: cannot create blank memory"  -- | Sign-extend to the given integer type. asIntS :: MonadBinder m => IntType -> SubExp -> m SubExp@@ -339,8 +402,12 @@ asIntZ :: MonadBinder m => IntType -> SubExp -> m SubExp asIntZ = asInt ZExt -asInt :: MonadBinder m =>-         (IntType -> IntType -> ConvOp) -> IntType -> SubExp -> m SubExp+asInt ::+  MonadBinder m =>+  (IntType -> IntType -> ConvOp) ->+  IntType ->+  SubExp ->+  m SubExp asInt ext to_it e = do   e_t <- subExpType e   case e_t of@@ -348,55 +415,71 @@       | to_it == from_it -> return e       | otherwise -> letSubExp s $ BasicOp $ ConvOp (ext from_it to_it) e     _ -> error "asInt: wrong type"-  where s = case e of Var v -> baseString v-                      _     -> "to_" ++ pretty to_it-+  where+    s = case e of+      Var v -> baseString v+      _ -> "to_" ++ pretty to_it  -- | Apply a binary operator to several subexpressions.  A left-fold.-foldBinOp :: MonadBinder m =>-             BinOp -> SubExp -> [SubExp] -> m (Exp (Lore m))+foldBinOp ::+  MonadBinder m =>+  BinOp ->+  SubExp ->+  [SubExp] ->+  m (Exp (Lore m)) foldBinOp _ ne [] =   return $ BasicOp $ SubExp ne-foldBinOp bop ne (e:es) =+foldBinOp bop ne (e : es) =   eBinOp bop (pure $ BasicOp $ SubExp e) (foldBinOp bop ne es)  -- | True if all operands are true. eAll :: MonadBinder m => [SubExp] -> m (Exp (Lore m)) eAll [] = pure $ BasicOp $ SubExp $ constant True-eAll (x:xs) = foldBinOp LogAnd x xs+eAll (x : xs) = foldBinOp LogAnd x xs  -- | Create a two-parameter lambda whose body applies the given binary -- operation to its arguments.  It is assumed that both argument and -- result types are the same.  (This assumption should be fixed at -- some point.)-binOpLambda :: (MonadBinder m, Bindable (Lore m)) =>-               BinOp -> PrimType -> m (Lambda (Lore m))+binOpLambda ::+  (MonadBinder m, Bindable (Lore m)) =>+  BinOp ->+  PrimType ->+  m (Lambda (Lore m)) binOpLambda bop t = binLambda (BinOp bop) t t  -- | As 'binOpLambda', but for t'CmpOp's.-cmpOpLambda :: (MonadBinder m, Bindable (Lore m)) =>-               CmpOp -> m (Lambda (Lore m))+cmpOpLambda ::+  (MonadBinder m, Bindable (Lore m)) =>+  CmpOp ->+  m (Lambda (Lore m)) cmpOpLambda cop = binLambda (CmpOp cop) (cmpOpType cop) Bool -binLambda :: (MonadBinder m, Bindable (Lore m)) =>-             (SubExp -> SubExp -> BasicOp) -> PrimType -> PrimType-          -> m (Lambda (Lore m))+binLambda ::+  (MonadBinder m, Bindable (Lore m)) =>+  (SubExp -> SubExp -> BasicOp) ->+  PrimType ->+  PrimType ->+  m (Lambda (Lore m)) binLambda bop arg_t ret_t = do-  x   <- newVName "x"-  y   <- newVName "y"+  x <- newVName "x"+  y <- newVName "y"   body <- insertStmsM $ do     res <- letSubExp "binlam_res" $ BasicOp $ bop (Var x) (Var y)     return $ resultBody [res]-  return Lambda {-             lambdaParams     = [Param x (Prim arg_t),-                                 Param y (Prim arg_t)]-           , lambdaReturnType = [Prim ret_t]-           , lambdaBody       = body-           }+  return+    Lambda+      { lambdaParams =+          [ Param x (Prim arg_t),+            Param y (Prim arg_t)+          ],+        lambdaReturnType = [Prim ret_t],+        lambdaBody = body+      }  -- | Slice a full dimension of the given size. sliceDim :: SubExp -> DimIndex SubExp-sliceDim d = DimSlice (constant (0::Int32)) d (constant (1::Int32))+sliceDim d = DimSlice (constant (0 :: Int64)) d (constant (1 :: Int64))  -- | @fullSlice t slice@ returns @slice@, but with 'DimSlice's of -- entire dimensions appended to the full dimensionality of @t@.  This@@ -423,9 +506,10 @@ -- dimension, but also one that fixes all unit dimensions. isFullSlice :: Shape -> Slice SubExp -> Bool isFullSlice shape slice = and $ zipWith allOfIt (shapeDims shape) slice-  where allOfIt (Constant v) DimFix{} = oneIsh v-        allOfIt d (DimSlice _ n _) = d == n-        allOfIt _ _ = False+  where+    allOfIt (Constant v) DimFix {} = oneIsh v+    allOfIt d (DimSlice _ n _) = d == n+    allOfIt _ _ = False  ifCommon :: [Type] -> IfDec ExtType ifCommon ts = IfDec (staticShapes ts) IfNormal@@ -436,75 +520,96 @@  -- | Conveniently construct a body that contains no bindings - but -- this time, monadically!-resultBodyM :: MonadBinder m =>-               [SubExp]-            -> m (Body (Lore m))+resultBodyM ::+  MonadBinder m =>+  [SubExp] ->+  m (Body (Lore m)) resultBodyM = mkBodyM mempty  -- | Evaluate the action, producing a body, then wrap it in all the -- bindings it created using 'addStm'.-insertStmsM :: (MonadBinder m) =>-               m (Body (Lore m)) -> m (Body (Lore m))+insertStmsM ::+  (MonadBinder m) =>+  m (Body (Lore m)) ->+  m (Body (Lore m)) insertStmsM m = do   (Body _ bnds res, otherbnds) <- collectStms m   mkBodyM (otherbnds <> bnds) res  -- | Change that result where evaluation of the body would stop.  Also -- change type annotations at branches.-mapResult :: Bindable lore =>-             (Result -> Body lore) -> Body lore -> Body lore+mapResult ::+  Bindable lore =>+  (Result -> Body lore) ->+  Body lore ->+  Body lore mapResult f (Body _ bnds res) =   let Body _ bnds2 newres = f res-  in mkBody (bnds<>bnds2) newres+   in mkBody (bnds <> bnds2) newres  -- | Instantiate all existential parts dimensions of the given -- type, using a monadic action to create the necessary t'SubExp's. -- You should call this function within some monad that allows you to -- collect the actions performed (say, 'Writer').-instantiateShapes :: Monad m =>-                     (Int -> m SubExp)-                  -> [TypeBase ExtShape u]-                  -> m [TypeBase Shape u]+instantiateShapes ::+  Monad m =>+  (Int -> m SubExp) ->+  [TypeBase ExtShape u] ->+  m [TypeBase Shape u] instantiateShapes f ts = evalStateT (mapM instantiate ts) M.empty-  where instantiate t = do-          shape <- mapM instantiate' $ shapeDims $ arrayShape t-          return $ t `setArrayShape` Shape shape-        instantiate' (Ext x) = do-          m <- get-          case M.lookup x m of-            Just se -> return se-            Nothing -> do se <- lift $ f x-                          put $ M.insert x se m-                          return se-        instantiate' (Free se) = return se+  where+    instantiate t = do+      shape <- mapM instantiate' $ shapeDims $ arrayShape t+      return $ t `setArrayShape` Shape shape+    instantiate' (Ext x) = do+      m <- get+      case M.lookup x m of+        Just se -> return se+        Nothing -> do+          se <- lift $ f x+          put $ M.insert x se m+          return se+    instantiate' (Free se) = return se -instantiateShapes' :: MonadFreshNames m =>-                      [TypeBase ExtShape u]-                   -> m ([TypeBase Shape u], [Ident])+instantiateShapes' ::+  MonadFreshNames m =>+  [TypeBase ExtShape u] ->+  m ([TypeBase Shape u], [Ident]) instantiateShapes' ts =   runWriterT $ instantiateShapes instantiate ts-  where instantiate _ = do v <- lift $ newIdent "size" $ Prim int32-                           tell [v]-                           return $ Var $ identName v+  where+    instantiate _ = do+      v <- lift $ newIdent "size" $ Prim int64+      tell [v]+      return $ Var $ identName v  removeExistentials :: ExtType -> Type -> Type removeExistentials t1 t2 =-  t1 `setArrayDims`-  zipWith nonExistential-  (shapeDims $ arrayShape t1)-  (arrayDims t2)-  where nonExistential (Ext _)    dim = dim-        nonExistential (Free dim) _   = dim+  t1+    `setArrayDims` zipWith+      nonExistential+      (shapeDims $ arrayShape t1)+      (arrayDims t2)+  where+    nonExistential (Ext _) dim = dim+    nonExistential (Free dim) _ = dim  -- | Can be used as the definition of 'mkLetNames' for a 'Bindable' -- instance for simple representations.-simpleMkLetNames :: (ExpDec lore ~ (), LetDec lore ~ Type,-                     MonadFreshNames m, TypedOp (Op lore), HasScope lore m) =>-                    [VName] -> Exp lore -> m (Stm lore)+simpleMkLetNames ::+  ( ExpDec lore ~ (),+    LetDec lore ~ Type,+    MonadFreshNames m,+    TypedOp (Op lore),+    HasScope lore m+  ) =>+  [VName] ->+  Exp lore ->+  m (Stm lore) simpleMkLetNames names e = do   et <- expExtType e   (ts, shapes) <- instantiateShapes' et-  let shapeElems = [ PatElem shape shapet | Ident shape shapet <- shapes ]+  let shapeElems = [PatElem shape shapet | Ident shape shapet <- shapes]   let valElems = zipWith PatElem names ts   return $ Let (Pattern shapeElems valElems) (defAux ()) e 
src/Futhark/Doc/Generator.hs view
@@ -1,33 +1,32 @@ {-# LANGUAGE OverloadedStrings #-}+ -- | The core logic of @futhark doc@. module Futhark.Doc.Generator (renderFiles) where +import qualified CMarkGFM as GFM import Control.Arrow ((***)) import Control.Monad import Control.Monad.Reader import Control.Monad.Writer hiding (Sum)-import Data.List (sort, sortOn, intersperse, inits, tails, isPrefixOf, find, groupBy, partition)-import Data.Char (isSpace, isAlpha, toUpper)+import Data.Char (isAlpha, isSpace, toUpper)+import Data.List (find, groupBy, inits, intersperse, isPrefixOf, partition, sort, sortOn, tails)+import qualified Data.Map as M import Data.Maybe import Data.Ord-import qualified Data.Map as M import qualified Data.Set as S-import System.FilePath (splitPath, (</>), (-<.>), (<.>), makeRelative)-import Text.Blaze.Html5 (AttributeValue, Html, (!), toHtml)-import qualified Text.Blaze.Html5 as H-import qualified Text.Blaze.Html5.Attributes as A import Data.String (fromString)-import Data.Version import qualified Data.Text as T-import qualified CMarkGFM as GFM--import Prelude hiding (abs)--import Language.Futhark.Semantic-import Language.Futhark.TypeChecker.Monad hiding (warn)-import Language.Futhark+import Data.Version import Futhark.Util.Pretty (Doc, ppr) import Futhark.Version+import Language.Futhark+import Language.Futhark.Semantic+import Language.Futhark.TypeChecker.Monad hiding (warn)+import System.FilePath (makeRelative, splitPath, (-<.>), (<.>), (</>))+import Text.Blaze.Html5 (AttributeValue, Html, toHtml, (!))+import qualified Text.Blaze.Html5 as H+import qualified Text.Blaze.Html5.Attributes as A+import Prelude hiding (abs)  docToHtml :: Doc -> Html docToHtml = toHtml . pretty@@ -44,7 +43,7 @@ joinBy :: Html -> [Html] -> Html joinBy _ [] = mempty joinBy _ [x] = x-joinBy sep (x:xs) = x <> foldMap (sep <>) xs+joinBy sep (x : xs) = x <> foldMap (sep <>) xs  commas :: [Html] -> Html commas = joinBy ", "@@ -54,6 +53,7 @@  braces :: Html -> Html braces x = "{" <> x <> "}"+ brackets :: Html -> Html brackets x = "[" <> x <> "]" @@ -64,17 +64,20 @@ -- are uninteresting.  These are for example type parameters. type NoLink = S.Set VName -data Context = Context { ctxCurrent :: String-                       , ctxFileMod :: FileModule-                       , ctxImports :: Imports-                       , ctxNoLink :: NoLink-                       , ctxFileMap :: FileMap-                       , ctxVisibleMTys :: S.Set VName-                         -- ^ Local module types that show up in the-                         -- interface.  These should be documented,-                         -- but clearly marked local.-                       }+data Context = Context+  { ctxCurrent :: String,+    ctxFileMod :: FileModule,+    ctxImports :: Imports,+    ctxNoLink :: NoLink,+    ctxFileMap :: FileMap,+    -- | Local module types that show up in the+    -- interface.  These should be documented,+    -- but clearly marked local.+    ctxVisibleMTys :: S.Set VName+  }+ type FileMap = M.Map VName (String, Namespace)+ type DocM = ReaderT Context (WriterT Documented (Writer Warnings))  data IndexWhat = IndexValue | IxFun | IndexModule | IndexModuleType | IndexType@@ -91,7 +94,7 @@  noLink :: [VName] -> DocM a -> DocM a noLink names = local $ \ctx ->-  ctx { ctxNoLink = S.fromList names <> ctxNoLink ctx }+  ctx {ctxNoLink = S.fromList names <> ctxNoLink ctx}  selfLink :: AttributeValue -> Html -> Html selfLink s = H.a ! A.id s ! A.href ("#" <> s) ! A.class_ "self_link"@@ -103,24 +106,28 @@ emptyRow = H.tr $ H.td mempty <> H.td mempty <> H.td mempty  specRow :: Html -> Html -> Html -> Html-specRow a b c = H.tr $ (H.td ! A.class_ "spec_lhs") a <>-                       (H.td ! A.class_ "spec_eql") b <>-                       (H.td ! A.class_ "spec_rhs") c+specRow a b c =+  H.tr $+    (H.td ! A.class_ "spec_lhs") a+      <> (H.td ! A.class_ "spec_eql") b+      <> (H.td ! A.class_ "spec_rhs") c  vnameToFileMap :: Imports -> FileMap vnameToFileMap = mconcat . map forFile-  where forFile (file, FileModule abs file_env _prog) =-          mconcat (map (vname Type) (M.keys abs)) <>-          forEnv file_env-          where vname ns v = M.singleton (qualLeaf v) (file, ns)-                vname' ((ns, _), v) = vname ns v+  where+    forFile (file, FileModule abs file_env _prog) =+      mconcat (map (vname Type) (M.keys abs))+        <> forEnv file_env+      where+        vname ns v = M.singleton (qualLeaf v) (file, ns)+        vname' ((ns, _), v) = vname ns v -                forEnv env =-                  mconcat (map vname' $ M.toList $ envNameMap env) <>-                  mconcat (map forMty $ M.elems $ envSigTable env)-                forMod (ModEnv env) = forEnv env-                forMod ModFun{} = mempty-                forMty = forMod . mtyMod+        forEnv env =+          mconcat (map vname' $ M.toList $ envNameMap env)+            <> mconcat (map forMty $ M.elems $ envSigTable env)+        forMod (ModEnv env) = forEnv env+        forMod ModFun {} = mempty+        forMty = forMod . mtyMod  -- | @renderFiles important_imports imports@ produces HTML files -- documenting the type-checked program @imports@, with the files in@@ -129,32 +136,46 @@ -- or the relative links will be wrong. renderFiles :: [FilePath] -> Imports -> ([(FilePath, Html)], Warnings) renderFiles important_imports imports = runWriter $ do-  (import_pages, documented) <- runWriterT $ forM imports $ \(current, fm) ->-    let ctx = Context current fm imports mempty file_map-              (progModuleTypes $ fileProg fm) in-    flip runReaderT ctx $ do--    (first_paragraph, maybe_abstract, maybe_sections) <- headerDoc $ fileProg fm+  (import_pages, documented) <- runWriterT $+    forM imports $ \(current, fm) ->+      let ctx =+            Context+              current+              fm+              imports+              mempty+              file_map+              (progModuleTypes $ fileProg fm)+       in flip runReaderT ctx $ do+            (first_paragraph, maybe_abstract, maybe_sections) <- headerDoc $ fileProg fm -    synopsis <- (H.div ! A.id "module") <$> synopsisDecs (progDecs $ fileProg fm)+            synopsis <- (H.div ! A.id "module") <$> synopsisDecs (progDecs $ fileProg fm) -    description <- describeDecs $ progDecs $ fileProg fm+            description <- describeDecs $ progDecs $ fileProg fm -    return (current,-            (H.docTypeHtml ! A.lang "en" $-             addBoilerplateWithNav important_imports imports ("doc" </> current) current $-             H.main $ maybe_abstract <>-             selfLink "synopsis" (H.h2 "Synopsis") <> (H.div ! A.id "overview") synopsis <>-             selfLink "description" (H.h2 "Description") <> description <>-             maybe_sections,-             first_paragraph))+            return+              ( current,+                ( H.docTypeHtml ! A.lang "en" $+                    addBoilerplateWithNav important_imports imports ("doc" </> current) current $+                      H.main $+                        maybe_abstract+                          <> selfLink "synopsis" (H.h2 "Synopsis")+                          <> (H.div ! A.id "overview") synopsis+                          <> selfLink "description" (H.h2 "Description")+                          <> description+                          <> maybe_sections,+                  first_paragraph+                )+              )    return $-    [("index.html", contentsPage important_imports $ map (fmap snd) import_pages),-     ("doc-index.html", indexPage important_imports imports documented file_map)]-    ++ map (importHtml *** fst) import_pages-  where file_map = vnameToFileMap imports-        importHtml import_name = "doc" </> import_name <.> "html"+    [ ("index.html", contentsPage important_imports $ map (fmap snd) import_pages),+      ("doc-index.html", indexPage important_imports imports documented file_map)+    ]+      ++ map (importHtml *** fst) import_pages+  where+    file_map = vnameToFileMap imports+    importHtml import_name = "doc" </> import_name <.> "html"  -- | The header documentation (which need not be present) can contain -- an abstract and further sections.@@ -166,135 +187,163 @@       first_paragraph <- docHtml $ Just $ DocComment (firstParagraph abstract) loc       abstract' <- docHtml $ Just $ DocComment abstract loc       more_sections' <- docHtml $ Just $ DocComment more_sections loc-      return (first_paragraph,-              selfLink "abstract" (H.h2 "Abstract") <> abstract',-              more_sections')+      return+        ( first_paragraph,+          selfLink "abstract" (H.h2 "Abstract") <> abstract',+          more_sections'+        )     _ -> return mempty-  where splitHeaderDoc s = fromMaybe (s, mempty) $-                           find (("\n##" `isPrefixOf`) . snd) $-                           zip (inits s) (tails s)-        firstParagraph = unlines . takeWhile (not . paragraphSeparator) . lines-        paragraphSeparator = all isSpace-+  where+    splitHeaderDoc s =+      fromMaybe (s, mempty) $+        find (("\n##" `isPrefixOf`) . snd) $+          zip (inits s) (tails s)+    firstParagraph = unlines . takeWhile (not . paragraphSeparator) . lines+    paragraphSeparator = all isSpace  contentsPage :: [FilePath] -> [(String, Html)] -> Html contentsPage important_imports pages =-  H.docTypeHtml $ addBoilerplate "index.html" "Futhark Library Documentation" $-  H.main $ H.h2 "Main libraries" <>-  fileList important_pages <>-  if null unimportant_pages then mempty else-    H.h2 "Supporting libraries" <>-    fileList unimportant_pages-  where (important_pages, unimportant_pages) =-          partition ((`elem` important_imports) . fst) pages+  H.docTypeHtml $+    addBoilerplate "index.html" "Futhark Library Documentation" $+      H.main $+        H.h2 "Main libraries"+          <> fileList important_pages+          <> if null unimportant_pages+            then mempty+            else+              H.h2 "Supporting libraries"+                <> fileList unimportant_pages+  where+    (important_pages, unimportant_pages) =+      partition ((`elem` important_imports) . fst) pages -        fileList pages' =-          H.dl ! A.class_ "file_list" $-          mconcat $ map linkTo $ sortOn fst pages'+    fileList pages' =+      H.dl ! A.class_ "file_list" $+        mconcat $ map linkTo $ sortOn fst pages' -        linkTo (name, maybe_abstract) =-          H.div ! A.class_ "file_desc" $-          (H.dt ! A.class_ "desc_header") (importLink "index.html" name) <>-          (H.dd ! A.class_ "desc_doc") maybe_abstract+    linkTo (name, maybe_abstract) =+      H.div ! A.class_ "file_desc" $+        (H.dt ! A.class_ "desc_header") (importLink "index.html" name)+          <> (H.dd ! A.class_ "desc_doc") maybe_abstract  importLink :: FilePath -> String -> Html importLink current name =   let file = relativise (makeRelative "/" $ "doc" </> name -<.> "html") current-  in (H.a ! A.href (fromString file) $ fromString name)+   in (H.a ! A.href (fromString file) $ fromString name)  indexPage :: [FilePath] -> Imports -> Documented -> FileMap -> Html indexPage important_imports imports documented fm =-  H.docTypeHtml $ addBoilerplateWithNav important_imports imports "doc-index.html" "Index" $-  H.main $-  (H.ul ! A.id "doc_index_list" $-   mconcat $ map initialListEntry $-   letter_group_links ++ [symbol_group_link]) <>-  (H.table ! A.id "doc_index" $-   H.thead (H.tr $ H.td "Who" <> H.td "What" <> H.td "Where") <>-   mconcat (letter_groups ++ [symbol_group]))-  where (letter_names, sym_names) =-          partition (isLetterName . baseString . fst) $-          sortOn (map toUpper . baseString . fst) $+  H.docTypeHtml $+    addBoilerplateWithNav important_imports imports "doc-index.html" "Index" $+      H.main $+        ( H.ul ! A.id "doc_index_list" $+            mconcat $+              map initialListEntry $+                letter_group_links ++ [symbol_group_link]+        )+          <> ( H.table ! A.id "doc_index" $+                 H.thead (H.tr $ H.td "Who" <> H.td "What" <> H.td "Where")+                   <> mconcat (letter_groups ++ [symbol_group])+             )+  where+    (letter_names, sym_names) =+      partition (isLetterName . baseString . fst) $+        sortOn (map toUpper . baseString . fst) $           mapMaybe isDocumented $ M.toList fm -        isDocumented (k, (file, _)) = do-          what <- M.lookup k documented-          Just (k, (file, what))+    isDocumented (k, (file, _)) = do+      what <- M.lookup k documented+      Just (k, (file, what)) -        (letter_groups, letter_group_links) =-          unzip $ map tbodyForNames $ groupBy sameInitial letter_names-        (symbol_group, symbol_group_link) =-          tbodyForInitial "Symbols" sym_names+    (letter_groups, letter_group_links) =+      unzip $ map tbodyForNames $ groupBy sameInitial letter_names+    (symbol_group, symbol_group_link) =+      tbodyForInitial "Symbols" sym_names -        isLetterName [] = False-        isLetterName (c:_) = isAlpha c+    isLetterName [] = False+    isLetterName (c : _) = isAlpha c -        sameInitial (x, _) (y, _) =-          case (baseString x, baseString y) of-            (x':_, y':_) -> toUpper x' == toUpper y'-            _            -> False+    sameInitial (x, _) (y, _) =+      case (baseString x, baseString y) of+        (x' : _, y' : _) -> toUpper x' == toUpper y'+        _ -> False -        tbodyForNames names@((s,_):_) =-          tbodyForInitial (map toUpper $ take 1 $ baseString s) names-        tbodyForNames _ = mempty+    tbodyForNames names@((s, _) : _) =+      tbodyForInitial (map toUpper $ take 1 $ baseString s) names+    tbodyForNames _ = mempty -        tbodyForInitial initial names =-          (H.tbody $ mconcat $ initial' : map linkTo names,-           initial)-          where initial' =-                  H.tr $ H.td ! A.colspan "2" ! A.class_ "doc_index_initial" $-                  H.a ! A.id (fromString initial)-                      ! A.href (fromString $ '#' : initial)-                      $ fromString initial+    tbodyForInitial initial names =+      ( H.tbody $ mconcat $ initial' : map linkTo names,+        initial+      )+      where+        initial' =+          H.tr $+            H.td ! A.colspan "2" ! A.class_ "doc_index_initial" $+              H.a ! A.id (fromString initial)+                ! A.href (fromString $ '#' : initial)+                $ fromString initial -        initialListEntry initial =-          H.li $ H.a ! A.href (fromString $ '#' : initial) $ fromString initial+    initialListEntry initial =+      H.li $ H.a ! A.href (fromString $ '#' : initial) $ fromString initial -        linkTo (name, (file, what)) =-          let link = (H.a ! A.href (fromString (makeRelative "/" $ "doc" </> vnameLink' name "" file))) $-                     fromString $ baseString name-              what' = case what of IndexValue -> "value"-                                   IxFun -> "function"-                                   IndexType -> "type"-                                   IndexModuleType -> "module type"-                                   IndexModule -> "module"-              html_file = makeRelative "/" $ "doc" </> file -<.> "html"-          in H.tr $-             (H.td ! A.class_ "doc_index_name" $ link) <>-             (H.td ! A.class_ "doc_index_namespace" $ what') <>-             (H.td ! A.class_ "doc_index_file" $-              (H.a ! A.href (fromString html_file) $ fromString file))+    linkTo (name, (file, what)) =+      let link =+            (H.a ! A.href (fromString (makeRelative "/" $ "doc" </> vnameLink' name "" file))) $+              fromString $ baseString name+          what' = case what of+            IndexValue -> "value"+            IxFun -> "function"+            IndexType -> "type"+            IndexModuleType -> "module type"+            IndexModule -> "module"+          html_file = makeRelative "/" $ "doc" </> file -<.> "html"+       in H.tr $+            (H.td ! A.class_ "doc_index_name" $ link)+              <> (H.td ! A.class_ "doc_index_namespace" $ what')+              <> ( H.td ! A.class_ "doc_index_file" $+                     (H.a ! A.href (fromString html_file) $ fromString file)+                 )  addBoilerplate :: String -> String -> Html -> Html addBoilerplate current titleText content =-  let headHtml = H.head $-                 H.meta ! A.charset "utf-8" <>-                 H.title (fromString titleText) <>-                 H.link ! A.href (fromString $ relativise "style.css" current)-                        ! A.rel "stylesheet"-                        ! A.type_ "text/css"+  let headHtml =+        H.head $+          H.meta+            ! A.charset "utf-8"+              <> H.title (fromString titleText)+              <> H.link+            ! A.href (fromString $ relativise "style.css" current)+            ! A.rel "stylesheet"+            ! A.type_ "text/css" -      navigation = H.ul ! A.id "navigation" $-                   H.li (H.a ! A.href (fromString $ relativise "index.html" current) $ "Contents") <>-                   H.li (H.a ! A.href (fromString $ relativise "doc-index.html" current) $ "Index")+      navigation =+        H.ul ! A.id "navigation" $+          H.li (H.a ! A.href (fromString $ relativise "index.html" current) $ "Contents")+            <> H.li (H.a ! A.href (fromString $ relativise "doc-index.html" current) $ "Index")        madeByHtml =         "Generated by " <> (H.a ! A.href futhark_doc_url) "futhark-doc"-        <> " " <> fromString (showVersion version)-  in headHtml <>-     H.body ((H.div ! A.id "header") (H.h1 (toHtml titleText) <> navigation) <>-             (H.div ! A.id "content") content <>-             (H.div ! A.id "footer") madeByHtml)-  where futhark_doc_url =-          "https://futhark.readthedocs.io/en/latest/man/futhark-doc.html"+          <> " "+          <> fromString (showVersion version)+   in headHtml+        <> H.body+          ( (H.div ! A.id "header") (H.h1 (toHtml titleText) <> navigation)+              <> (H.div ! A.id "content") content+              <> (H.div ! A.id "footer") madeByHtml+          )+  where+    futhark_doc_url =+      "https://futhark.readthedocs.io/en/latest/man/futhark-doc.html"  addBoilerplateWithNav :: [FilePath] -> Imports -> String -> String -> Html -> Html addBoilerplateWithNav important_imports imports current titleText content =   addBoilerplate current titleText $-  (H.nav ! A.id "filenav" $ files) <> content-  where files = H.ul $ mconcat $ map pp $ sort $ filter visible important_imports-        pp name = H.li $ importLink current name-        visible = (`elem` map fst imports)+    (H.nav ! A.id "filenav" $ files) <> content+  where+    files = H.ul $ mconcat $ map pp $ sort $ filter visible important_imports+    pp name = H.li $ importLink current name+    visible = (`elem` map fst imports)  synopsisDecs :: [Dec] -> DocM Html synopsisDecs decs = do@@ -302,8 +351,8 @@   fm <- asks ctxFileMod   -- We add an empty row to avoid generating invalid HTML in cases   -- where all rows are otherwise colspan=2.-  (H.table ! A.class_ "specs") . (emptyRow<>) . mconcat <$>-    sequence (mapMaybe (synopsisDec visible fm) decs)+  (H.table ! A.class_ "specs") . (emptyRow <>) . mconcat+    <$> sequence (mapMaybe (synopsisDec visible fm) decs)  synopsisDec :: S.Set VName -> FileModule -> Dec -> Maybe (DocM Html) synopsisDec visible fm dec = case dec of@@ -313,21 +362,24 @@   TypeDec t -> synopsisType t   OpenDec x _     | Just opened <- synopsisOpened x -> Just $ do-        opened' <- opened-        return $ fullRow $ keyword "open " <> opened'+      opened' <- opened+      return $ fullRow $ keyword "open " <> opened'     | otherwise ->-        Just $ return $ fullRow $-        keyword "open" <> fromString (" <" <> pretty x <> ">")+      Just $+        return $+          fullRow $+            keyword "open" <> fromString (" <" <> pretty x <> ">")   LocalDec (SigDec s) _     | sigName s `S.member` visible ->-        synopsisModType (keyword "local" <> " ") s-  LocalDec{} -> Nothing-  ImportDec{} -> Nothing+      synopsisModType (keyword "local" <> " ") s+  LocalDec {} -> Nothing+  ImportDec {} -> Nothing  synopsisOpened :: ModExp -> Maybe (DocM Html) synopsisOpened (ModVar qn _) = Just $ qualNameHtml qn-synopsisOpened (ModParens me _) = do me' <- synopsisOpened me-                                     Just $ parens <$> me'+synopsisOpened (ModParens me _) = do+  me' <- synopsisOpened me+  Just $ parens <$> me' synopsisOpened (ModImport _ (Info file) _) = Just $ do   current <- asks ctxCurrent   let dest = fromString $ relativise file current <> ".html"@@ -345,14 +397,18 @@  valBindHtml :: Html -> ValBind -> DocM (Html, Html, Html) valBindHtml name (ValBind _ _ retdecl (Info (rettype, _)) tparams params _ _ _ _) = do-  let tparams' = mconcat $ map ((" "<>) . typeParamHtml) tparams-      noLink' = noLink $ map typeParamName tparams ++-                map identName (S.toList $ mconcat $ map patternIdents params)+  let tparams' = mconcat $ map ((" " <>) . typeParamHtml) tparams+      noLink' =+        noLink $+          map typeParamName tparams+            ++ map identName (S.toList $ mconcat $ map patternIdents params)   rettype' <- noLink' $ maybe (typeHtml rettype) typeExpHtml retdecl   params' <- noLink' $ mapM patternHtml params-  return (keyword "val " <> (H.span ! A.class_ "decl_name") name,-          tparams',-          mconcat (intersperse " -> " $ params' ++ [rettype']))+  return+    ( keyword "val " <> (H.span ! A.class_ "decl_name") name,+      tparams',+      mconcat (intersperse " -> " $ params' ++ [rettype'])+    )  synopsisModType :: Html -> SigBind -> Maybe (DocM Html) synopsisModType prefix sb = Just $ do@@ -363,16 +419,18 @@  synopsisMod :: FileModule -> ModBind -> Maybe (DocM Html) synopsisMod fm (ModBind name ps sig _ _ _) =-  case sig of Nothing    -> (proceed <=< envSig) <$> M.lookup name modtable-              Just (s,_) -> Just $ proceed =<< synopsisSigExp s-  where proceed sig' = do-          let name' = vnameSynopsisDef name-          ps' <- modParamHtml ps-          return $ specRow (keyword "module " <> name') ": " (ps' <> sig')+  case sig of+    Nothing -> (proceed <=< envSig) <$> M.lookup name modtable+    Just (s, _) -> Just $ proceed =<< synopsisSigExp s+  where+    proceed sig' = do+      let name' = vnameSynopsisDef name+      ps' <- modParamHtml ps+      return $ specRow (keyword "module " <> name') ": " (ps' <> sig') -        FileModule _abs Env { envModTable = modtable} _ = fm-        envSig (ModEnv e) = renderEnv e-        envSig (ModFun (FunSig _ _ (MTy _ m))) = envSig m+    FileModule _abs Env {envModTable = modtable} _ = fm+    envSig (ModEnv e) = renderEnv e+    envSig (ModFun (FunSig _ _ (MTy _ m))) = envSig m  synopsisType :: TypeBind -> Maybe (DocM Html) synopsisType tb = Just $ do@@ -413,8 +471,10 @@   let tps' = map typeParamHtml tps   t' <- typeHtml t   return $-    keyword "val " <> vnameHtml name <>-    mconcat (map (" "<>) tps') <> ": " <> t'+    keyword "val " <> vnameHtml name+      <> mconcat (map (" " <>) tps')+      <> ": "+      <> t'  typeHtml :: StructType -> DocM Html typeHtml t = case t of@@ -425,16 +485,17 @@   Scalar (Prim et) -> return $ primTypeHtml et   Scalar (Record fs)     | Just ts <- areTupleFields fs ->-        parens . commas <$> mapM typeHtml ts+      parens . commas <$> mapM typeHtml ts     | otherwise ->-        braces . commas <$> mapM ppField (M.toList fs)-    where ppField (name, tp) = do-            tp' <- typeHtml tp-            return $ toHtml (nameToString name) <> ": " <> tp'+      braces . commas <$> mapM ppField (M.toList fs)+    where+      ppField (name, tp) = do+        tp' <- typeHtml tp+        return $ toHtml (nameToString name) <> ": " <> tp'   Scalar (TypeVar _ u et targs) -> do     targs' <- mapM typeArgHtml targs     et' <- typeNameHtml et-    return $ prettyU u <> et' <> mconcat (map (" "<>) targs')+    return $ prettyU u <> et' <> mconcat (map (" " <>) targs')   Scalar (Arrow _ pname t1 t2) -> do     t1' <- typeHtml t1     t2' <- typeHtml t2@@ -444,8 +505,9 @@       Unnamed ->         t1' <> " -> " <> t2'   Scalar (Sum cs) -> pipes <$> mapM ppClause (sortConstrs cs)-    where ppClause (n, ts) = joinBy " " . (ppConstr n :) <$> mapM typeHtml ts-          ppConstr name = "#" <> toHtml (nameToString name)+    where+      ppClause (n, ts) = joinBy " " . (ppConstr n :) <$> mapM typeHtml ts+      ppConstr name = "#" <> toHtml (nameToString name)  prettyShapeDecl :: ShapeDecl (DimDecl VName) -> DocM Html prettyShapeDecl (ShapeDecl ds) =@@ -459,8 +521,13 @@ modParamHtml [] = return mempty modParamHtml (ModParam pname psig _ _ : mps) =   liftM2 f (synopsisSigExp psig) (modParamHtml mps)-  where f se params = "(" <> vnameHtml pname <>-                      ": " <> se <> ") -> " <> params+  where+    f se params =+      "(" <> vnameHtml pname+        <> ": "+        <> se+        <> ") -> "+        <> params  synopsisSigExp :: SigExpBase Info VName -> DocM Html synopsisSigExp e = case e of@@ -471,16 +538,18 @@     s' <- synopsisSigExp s     t' <- typeDeclHtml t     v' <- qualNameHtml v-    let ps' = mconcat $ map ((" "<>) . typeParamHtml) ps+    let ps' = mconcat $ map ((" " <>) . typeParamHtml) ps     return $ s' <> keyword " with " <> v' <> ps' <> " = " <> t'   SigArrow Nothing e1 e2 _ ->     liftM2 f (synopsisSigExp e1) (synopsisSigExp e2)-    where f e1' e2' = e1' <> " -> " <> e2'+    where+      f e1' e2' = e1' <> " -> " <> e2'   SigArrow (Just v) e1 e2 _ ->-    do let name = vnameHtml v-       e1' <- synopsisSigExp e1-       e2' <- noLink [v] $ synopsisSigExp e2-       return $ "(" <> name <> ": " <> e1' <> ") -> " <> e2'+    do+      let name = vnameHtml v+      e1' <- synopsisSigExp e1+      e2' <- noLink [v] $ synopsisSigExp e2+      return $ "(" <> name <> ": " <> e1' <> ") -> " <> e2'  keyword :: String -> Html keyword = (H.span ! A.class_ "keyword") . fromString@@ -497,29 +566,35 @@ vnameSynopsisDef :: VName -> Html vnameSynopsisDef (VName name tag) =   H.span ! A.id (fromString (show tag ++ "s")) $-  H.a ! A.href (fromString ("#" ++ show tag)) $ renderName name+    H.a ! A.href (fromString ("#" ++ show tag)) $ renderName name  vnameSynopsisRef :: VName -> Html-vnameSynopsisRef v = H.a ! A.class_ "synopsis_link"-                         ! A.href (fromString ("#" ++ show (baseTag v) ++ "s")) $-                     "↑"+vnameSynopsisRef v =+  H.a ! A.class_ "synopsis_link"+    ! A.href (fromString ("#" ++ show (baseTag v) ++ "s"))+    $ "↑"  synopsisSpec :: SpecBase Info VName -> DocM Html synopsisSpec spec = case spec of   TypeAbbrSpec tpsig ->     fullRow <$> typeBindHtml (vnameSynopsisDef $ typeAlias tpsig) tpsig   TypeSpec l name ps _ _ ->-    return $ fullRow $ keyword l' <> vnameSynopsisDef name <> mconcat (map ((" "<>) . typeParamHtml) ps)-    where l' = case l of Unlifted -> "type "-                         SizeLifted -> "type~ "-                         Lifted -> "type^ "+    return $ fullRow $ keyword l' <> vnameSynopsisDef name <> mconcat (map ((" " <>) . typeParamHtml) ps)+    where+      l' = case l of+        Unlifted -> "type "+        SizeLifted -> "type~ "+        Lifted -> "type^ "   ValSpec name tparams rettype _ _ -> do     let tparams' = map typeParamHtml tparams-    rettype' <- noLink (map typeParamName tparams) $-                typeDeclHtml rettype-    return $ specRow-      (keyword "val " <> vnameSynopsisDef name)-      (mconcat (map (" "<>) tparams') <> ": ") rettype'+    rettype' <-+      noLink (map typeParamName tparams) $+        typeDeclHtml rettype+    return $+      specRow+        (keyword "val " <> vnameSynopsisDef name)+        (mconcat (map (" " <>) tparams') <> ": ")+        rettype'   ModSpec name sig _ _ ->     specRow (keyword "module " <> vnameSynopsisDef name) ": " <$> synopsisSigExp sig   IncludeSpec e _ -> fullRow . (keyword "include " <>) <$> synopsisSigExp e@@ -529,24 +604,26 @@  typeExpHtml :: TypeExp VName -> DocM Html typeExpHtml e = case e of-  TEUnique t _  -> ("*"<>) <$> typeExpHtml t+  TEUnique t _ -> ("*" <>) <$> typeExpHtml t   TEArray at d _ -> do     at' <- typeExpHtml at     d' <- dimExpHtml d     return $ brackets d' <> at'   TETuple ts _ -> parens . commas <$> mapM typeExpHtml ts   TERecord fs _ -> braces . commas <$> mapM ppField fs-    where ppField (name, t) = do-            t' <- typeExpHtml t-            return $ toHtml (nameToString name) <> ": " <> t'-  TEVar name  _ -> qualNameHtml name+    where+      ppField (name, t) = do+        t' <- typeExpHtml t+        return $ toHtml (nameToString name) <> ": " <> t'+  TEVar name _ -> qualNameHtml name   TEApply t arg _ -> do     t' <- typeExpHtml t     arg' <- typeArgExpHtml arg     return $ t' <> " " <> arg'   TEArrow pname t1 t2 _ -> do-    t1' <- case t1 of TEArrow{} -> parens <$> typeExpHtml t1-                      _         -> typeExpHtml t1+    t1' <- case t1 of+      TEArrow {} -> parens <$> typeExpHtml t1+      _ -> typeExpHtml t1     t2' <- typeExpHtml t2     return $ case pname of       Just v ->@@ -554,23 +631,26 @@       Nothing ->         t1' <> " -> " <> t2'   TESum cs _ -> pipes <$> mapM ppClause cs-    where ppClause (n, ts) = joinBy " " . (ppConstr n :) <$> mapM typeExpHtml ts-          ppConstr name = "#" <> toHtml (nameToString name)+    where+      ppClause (n, ts) = joinBy " " . (ppConstr n :) <$> mapM typeExpHtml ts+      ppConstr name = "#" <> toHtml (nameToString name)  qualNameHtml :: QualName VName -> DocM Html qualNameHtml (QualName names vname@(VName name tag)) =   if tag <= maxIntrinsicTag-      then return $ renderName name-      else f <$> ref-  where prefix :: Html-        prefix = mapM_ ((<> ".") . renderName . baseName) names-        f (Just s) = H.a ! A.href (fromString s) $ prefix <> renderName name-        f Nothing = prefix <> renderName name+    then return $ renderName name+    else f <$> ref+  where+    prefix :: Html+    prefix = mapM_ ((<> ".") . renderName . baseName) names+    f (Just s) = H.a ! A.href (fromString s) $ prefix <> renderName name+    f Nothing = prefix <> renderName name -        ref = do boring <- asks $ S.member vname . ctxNoLink-                 if boring-                   then return Nothing-                   else Just <$> vnameLink vname+    ref = do+      boring <- asks $ S.member vname . ctxNoLink+      if boring+        then return Nothing+        else Just <$> vnameLink vname  vnameLink' :: VName -> String -> String -> String vnameLink :: VName -> DocM String@@ -578,7 +658,6 @@   current <- asks ctxCurrent   file <- asks $ maybe current fst . M.lookup vname . ctxFileMap   return $ vnameLink' vname current file- vnameLink' (VName _ tag) current file =   if file == current     then "#" ++ show tag@@ -592,8 +671,8 @@   let (pat_param, t) = patternParam pat   t' <- typeHtml t   return $ case pat_param of-             Named v -> parens (vnameHtml v <> ": " <> t')-             Unnamed -> t'+    Named v -> parens (vnameHtml v <> ": " <> t')+    Unnamed -> t'  relativise :: FilePath -> FilePath -> FilePath relativise dest src =@@ -621,42 +700,48 @@  typeAbbrevHtml :: Liftedness -> Html -> [TypeParam] -> Html typeAbbrevHtml l name params =-  what <> name <> mconcat (map ((" "<>) . typeParamHtml) params)-  where what = keyword $ "type" ++ pretty l ++ " "+  what <> name <> mconcat (map ((" " <>) . typeParamHtml) params)+  where+    what = keyword $ "type" ++ pretty l ++ " "  docHtml :: Maybe DocComment -> DocM Html docHtml (Just (DocComment doc loc)) =-  H.preEscapedText .-  GFM.commonmarkToHtml [] [GFM.extAutolink] .-  T.pack <$> identifierLinks loc doc+  H.preEscapedText+    . GFM.commonmarkToHtml [] [GFM.extAutolink]+    . T.pack+    <$> identifierLinks loc doc docHtml Nothing = return mempty  identifierLinks :: SrcLoc -> String -> DocM String identifierLinks _ [] = return [] identifierLinks loc s   | Just ((name, namespace, file), s') <- identifierReference s = do-      let proceed x = (x<>) <$> identifierLinks loc s'-          unknown = proceed $ "`" <> name <> "`"-      case knownNamespace namespace of-        Just namespace' -> do-          maybe_v <- lookupName (namespace', name, file)-          case maybe_v of-            Nothing -> do-              warn loc $-                "Identifier '" <> name <> "' not found in namespace '" <>-                namespace <> "'" <> maybe "" (" in file "<>) file <> "."-              unknown-            Just v' -> do-              link <- vnameLink v'-              proceed $ "[`" <> name <> "`](" <> link <> ")"-        _ -> do-          warn loc $ "Unknown namespace '" <> namespace <> "'."-          unknown-  where knownNamespace "term" = Just Term-        knownNamespace "mtype" = Just Signature-        knownNamespace "type" = Just Type-        knownNamespace _ = Nothing-identifierLinks loc (c:s') = (c:) <$> identifierLinks loc s'+    let proceed x = (x <>) <$> identifierLinks loc s'+        unknown = proceed $ "`" <> name <> "`"+    case knownNamespace namespace of+      Just namespace' -> do+        maybe_v <- lookupName (namespace', name, file)+        case maybe_v of+          Nothing -> do+            warn loc $+              "Identifier '" <> name <> "' not found in namespace '"+                <> namespace+                <> "'"+                <> maybe "" (" in file " <>) file+                <> "."+            unknown+          Just v' -> do+            link <- vnameLink v'+            proceed $ "[`" <> name <> "`](" <> link <> ")"+      _ -> do+        warn loc $ "Unknown namespace '" <> namespace <> "'."+        unknown+  where+    knownNamespace "term" = Just Term+    knownNamespace "mtype" = Just Signature+    knownNamespace "type" = Just Type+    knownNamespace _ = Nothing+identifierLinks loc (c : s') = (c :) <$> identifierLinks loc s'  lookupName :: (Namespace, String, Maybe FilePath) -> DocM (Maybe VName) lookupName (namespace, name, file) = do@@ -668,83 +753,84 @@     Just qn -> return $ Just $ qualLeaf qn  lookupEnvForFile :: Maybe FilePath -> DocM (Maybe Env)-lookupEnvForFile Nothing     = asks $ Just . fileEnv . ctxFileMod+lookupEnvForFile Nothing = asks $ Just . fileEnv . ctxFileMod lookupEnvForFile (Just file) = asks $ fmap fileEnv . lookup file . ctxImports -describeGeneric :: VName-                -> IndexWhat-                -> Maybe DocComment-                -> (Html -> DocM Html)-                -> DocM Html+describeGeneric ::+  VName ->+  IndexWhat ->+  Maybe DocComment ->+  (Html -> DocM Html) ->+  DocM Html describeGeneric name what doc f = do   name' <- H.span ! A.class_ "decl_name" <$> vnameDescDef name what   decl_type <- f name'   doc' <- docHtml doc   let decl_doc = H.dd ! A.class_ "desc_doc" $ doc'-      decl_header = (H.dt ! A.class_ "desc_header") $-                    vnameSynopsisRef name <> decl_type+      decl_header =+        (H.dt ! A.class_ "desc_header") $+          vnameSynopsisRef name <> decl_type   return $ decl_header <> decl_doc -describeGenericMod :: VName-                   -> IndexWhat-                   -> SigExp-                   -> Maybe DocComment-                   -> (Html -> DocM Html)-                   -> DocM Html+describeGenericMod ::+  VName ->+  IndexWhat ->+  SigExp ->+  Maybe DocComment ->+  (Html -> DocM Html) ->+  DocM Html describeGenericMod name what se doc f = do   name' <- H.span ! A.class_ "decl_name" <$> vnameDescDef name what    decl_type <- f name'    doc' <- case se of-            SigSpecs specs _ -> (<>) <$> docHtml doc <*> describeSpecs specs-            _                -> docHtml doc+    SigSpecs specs _ -> (<>) <$> docHtml doc <*> describeSpecs specs+    _ -> docHtml doc    let decl_doc = H.dd ! A.class_ "desc_doc" $ doc'-      decl_header = (H.dt ! A.class_ "desc_header") $-                    vnameSynopsisRef name <> decl_type+      decl_header =+        (H.dt ! A.class_ "desc_header") $+          vnameSynopsisRef name <> decl_type   return $ decl_header <> decl_doc  describeDecs :: [Dec] -> DocM Html describeDecs decs = do   visible <- asks ctxVisibleMTys-  H.dl . mconcat <$>-    mapM (fmap $ H.div ! A.class_ "decl_description")-    (mapMaybe (describeDec visible) decs)+  H.dl . mconcat+    <$> mapM+      (fmap $ H.div ! A.class_ "decl_description")+      (mapMaybe (describeDec visible) decs)  describeDec :: S.Set VName -> Dec -> Maybe (DocM Html) describeDec _ (ValDec vb) = Just $   describeGeneric (valBindName vb) (valBindWhat vb) (valBindDoc vb) $ \name -> do-  (lhs, mhs, rhs) <- valBindHtml name vb-  return $ lhs <> mhs <> ": " <> rhs--describeDec _ (TypeDec vb) = Just $-  describeGeneric (typeAlias vb) IndexType (typeDoc vb) (`typeBindHtml` vb)-+    (lhs, mhs, rhs) <- valBindHtml name vb+    return $ lhs <> mhs <> ": " <> rhs+describeDec _ (TypeDec vb) =+  Just $+    describeGeneric (typeAlias vb) IndexType (typeDoc vb) (`typeBindHtml` vb) describeDec _ (SigDec (SigBind name se doc _)) = Just $   describeGenericMod name IndexModuleType se doc $ \name' ->-  return $ keyword "module type " <> name'-+    return $ keyword "module type " <> name' describeDec _ (ModDec mb) = Just $   describeGeneric (modName mb) IndexModule (modDoc mb) $ \name' ->-  return $ keyword "module " <> name'--describeDec _ OpenDec{} = Nothing-+    return $ keyword "module " <> name'+describeDec _ OpenDec {} = Nothing describeDec visible (LocalDec (SigDec (SigBind name se doc _)) _)   | name `S.member` visible = Just $-  describeGenericMod name IndexModuleType se doc $ \name' ->-  return $ keyword "local module type " <> name'--describeDec _ LocalDec{} = Nothing-describeDec _ ImportDec{} = Nothing+    describeGenericMod name IndexModuleType se doc $ \name' ->+      return $ keyword "local module type " <> name'+describeDec _ LocalDec {} = Nothing+describeDec _ ImportDec {} = Nothing  valBindWhat :: ValBind -> IndexWhat-valBindWhat vb | null (valBindParams vb),-                 orderZero (fst $ unInfo $ valBindRetType vb) =-                   IndexValue-               | otherwise =-                   IxFun+valBindWhat vb+  | null (valBindParams vb),+    orderZero (fst $ unInfo $ valBindRetType vb) =+    IndexValue+  | otherwise =+    IxFun  describeSpecs :: [Spec] -> DocM Html describeSpecs specs =@@ -753,29 +839,35 @@ describeSpec :: Spec -> DocM Html describeSpec (ValSpec name tparams t doc _) =   describeGeneric name what doc $ \name' -> do-    let tparams' = mconcat $ map ((" "<>) . typeParamHtml) tparams-    t' <- noLink (map typeParamName tparams) $-          typeExpHtml $ declaredType t-    return $ keyword "val " <>  name' <> tparams' <> ": " <> t'-  where what = if orderZero (unInfo $ expandedType t)-               then IndexValue else IxFun+    let tparams' = mconcat $ map ((" " <>) . typeParamHtml) tparams+    t' <-+      noLink (map typeParamName tparams) $+        typeExpHtml $ declaredType t+    return $ keyword "val " <> name' <> tparams' <> ": " <> t'+  where+    what =+      if orderZero (unInfo $ expandedType t)+        then IndexValue+        else IxFun describeSpec (TypeAbbrSpec vb) =   describeGeneric (typeAlias vb) IndexType (typeDoc vb) (`typeBindHtml` vb) describeSpec (TypeSpec l name tparams doc _) =   describeGeneric name IndexType doc $-  return . (\name' -> typeAbbrevHtml l name' tparams)+    return . (\name' -> typeAbbrevHtml l name' tparams) describeSpec (ModSpec name se doc _) =   describeGenericMod name IndexModule se doc $ \name' ->-  case se of-    SigSpecs{} -> return $ keyword "module " <> name'-    _ -> do se' <- synopsisSigExp se-            return $ keyword "module " <> name' <> ": " <> se'+    case se of+      SigSpecs {} -> return $ keyword "module " <> name'+      _ -> do+        se' <- synopsisSigExp se+        return $ keyword "module " <> name' <> ": " <> se' describeSpec (IncludeSpec sig _) = do   sig' <- synopsisSigExp sig   doc' <- docHtml Nothing-  let decl_header = (H.dt ! A.class_ "desc_header") $-                    (H.span ! A.class_ "synopsis_link") mempty <>-                    keyword "include " <>-                    sig'+  let decl_header =+        (H.dt ! A.class_ "desc_header") $+          (H.span ! A.class_ "synopsis_link") mempty+            <> keyword "include "+            <> sig'       decl_doc = H.dd ! A.class_ "desc_doc" $ doc'   return $ decl_header <> decl_doc
src/Futhark/Error.hs view
@@ -1,19 +1,18 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE Safe #-}+ -- | Futhark error definitions. module Futhark.Error-  ( CompilerError(..)-  , ErrorClass(..)--  , externalError-  , externalErrorS--  , InternalError(..)-  , compilerBug-  , compilerBugS-  , compilerLimitation-  , compilerLimitationS-  , internalErrorS+  ( CompilerError (..),+    ErrorClass (..),+    externalError,+    externalErrorS,+    InternalError (..),+    compilerBug,+    compilerBugS,+    compilerLimitation,+    compilerLimitationS,+    internalErrorS,   ) where @@ -25,18 +24,19 @@ -- | There are two classes of internal errors: actual bugs, and -- implementation limitations.  The latter are already known and need -- not be reported.-data ErrorClass = CompilerBug-                | CompilerLimitation-                deriving (Eq, Ord, Show)+data ErrorClass+  = CompilerBug+  | CompilerLimitation+  deriving (Eq, Ord, Show)  -- | A compiler error.-data CompilerError =-    ExternalError Doc-    -- ^ An error that happened due to something the user did, such as+data CompilerError+  = -- | An error that happened due to something the user did, such as     -- provide incorrect code or options.-  | InternalError T.Text T.Text ErrorClass-    -- ^ An internal compiler error.  The second text is extra data+    ExternalError Doc+  | -- | An internal compiler error.  The second text is extra data     -- for debugging, which can be written to a file.+    InternalError T.Text T.Text ErrorClass  instance Show CompilerError where   show (ExternalError s) = pretty s
src/Futhark/FreshNames.hs view
@@ -1,15 +1,16 @@ {-# LANGUAGE DeriveLift #-}+ -- | This module provides facilities for generating unique names. module Futhark.FreshNames-  ( VNameSource-  , blankNameSource-  , newNameSource-  , newName-  ) where--import Language.Haskell.TH.Syntax (Lift)+  ( VNameSource,+    blankNameSource,+    newNameSource,+    newName,+  )+where  import Language.Futhark.Core+import Language.Haskell.TH.Syntax (Lift)  -- | A name source is conceptually an infinite sequence of names with -- no repeating entries.  In practice, when asked for a name, the name@@ -30,7 +31,8 @@ -- | Produce a fresh name, using the given name as a template. newName :: VNameSource -> VName -> (VName, VNameSource) newName (VNameSource i) k = i' `seq` (VName (baseName k) i, VNameSource i')-  where i' = i+1+  where+    i' = i + 1  -- | A blank name source. blankNameSource :: VNameSource
src/Futhark/IR.hs view
@@ -1,17 +1,18 @@ {-# LANGUAGE Safe #-}+ -- | A convenient re-export of basic AST modules.  Note that -- "Futhark.IR.Lore" is not exported, as this would -- cause name clashes.  You are advised to use a qualified import of -- the lore module, if you need it. module Futhark.IR-       ( module Futhark.IR.Prop-       , module Futhark.IR.Traversals-       , module Futhark.IR.Pretty-       , module Futhark.IR.Syntax-       )+  ( module Futhark.IR.Prop,+    module Futhark.IR.Traversals,+    module Futhark.IR.Pretty,+    module Futhark.IR.Syntax,+  ) where -import Futhark.IR.Syntax+import Futhark.IR.Pretty import Futhark.IR.Prop+import Futhark.IR.Syntax import Futhark.IR.Traversals-import Futhark.IR.Pretty
src/Futhark/IR/Aliases.hs view
@@ -1,67 +1,79 @@-{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+ -- | A representation where all bindings are annotated with aliasing -- information. module Futhark.IR.Aliases-       ( -- * The Lore definition-         Aliases-       , AliasDec (..)-       , VarAliases-       , ConsumedInExp-       , BodyAliasing-       , module Futhark.IR.Prop.Aliases-         -- * Module re-exports-       , module Futhark.IR.Prop-       , module Futhark.IR.Traversals-       , module Futhark.IR.Pretty-       , module Futhark.IR.Syntax-         -- * Adding aliases-       , addAliasesToPattern-       , mkAliasedLetStm-       , mkAliasedBody-       , mkPatternAliases-       , mkBodyAliases-         -- * Removing aliases-       , removeProgAliases-       , removeFunDefAliases-       , removeExpAliases-       , removeStmAliases-       , removeLambdaAliases-       , removePatternAliases-       , removeScopeAliases-       -- * Tracking aliases-       , AliasesAndConsumed-       , trackAliases-       , consumedInStms-       )+  ( -- * The Lore definition+    Aliases,+    AliasDec (..),+    VarAliases,+    ConsumedInExp,+    BodyAliasing,+    module Futhark.IR.Prop.Aliases,++    -- * Module re-exports+    module Futhark.IR.Prop,+    module Futhark.IR.Traversals,+    module Futhark.IR.Pretty,+    module Futhark.IR.Syntax,++    -- * Adding aliases+    addAliasesToPattern,+    mkAliasedLetStm,+    mkAliasedBody,+    mkPatternAliases,+    mkBodyAliases,++    -- * Removing aliases+    removeProgAliases,+    removeFunDefAliases,+    removeExpAliases,+    removeStmAliases,+    removeLambdaAliases,+    removePatternAliases,+    removeScopeAliases,++    -- * Tracking aliases+    AliasesAndConsumed,+    trackAliases,+    consumedInStms,+  ) where  import Control.Monad.Identity import Control.Monad.Reader-import Data.Maybe import qualified Data.Map.Strict as M--import Futhark.IR.Syntax+import Data.Maybe+import Futhark.Analysis.Rephrase+import Futhark.Binder+import Futhark.IR.Pretty import Futhark.IR.Prop import Futhark.IR.Prop.Aliases+import Futhark.IR.Syntax import Futhark.IR.Traversals-import Futhark.IR.Pretty import Futhark.Transform.Rename-import Futhark.Binder import Futhark.Transform.Substitute-import Futhark.Analysis.Rephrase import qualified Futhark.Util.Pretty as PP+import GHC.Generics+import Language.SexpGrammar as Sexp+import Language.SexpGrammar.Generic  -- | The lore for the basic representation. data Aliases lore  -- | A wrapper around 'AliasDec to get around the fact that we need an -- 'Ord' instance, which 'AliasDec does not have.-newtype AliasDec = AliasDec { unAliases :: Names }-               deriving (Show)+newtype AliasDec = AliasDec {unAliases :: Names}+  deriving (Show, Generic) +instance SexpIso AliasDec where+  sexpIso = with $ \vname -> sexpIso >>> vname+ instance Semigroup AliasDec where   x <> y = AliasDec $ unAliases x <> unAliases y @@ -96,8 +108,10 @@ -- consumed inside of it. type BodyAliasing = ([VarAliases], ConsumedInExp) -instance (Decorations lore, CanBeAliased (Op lore)) =>-         Decorations (Aliases lore) where+instance+  (Decorations lore, CanBeAliased (Op lore)) =>+  Decorations (Aliases lore)+  where   type LetDec (Aliases lore) = (VarAliases, LetDec lore)   type ExpDec (Aliases lore) = (ConsumedInExp, ExpDec lore)   type BodyDec (Aliases lore) = (BodyAliasing, BodyDec lore)@@ -110,10 +124,12 @@ instance AliasesOf (VarAliases, dec) where   aliasesOf = unAliases . fst -instance FreeDec AliasDec where+instance FreeDec AliasDec -withoutAliases :: (HasScope (Aliases lore) m, Monad m) =>-                 ReaderT (Scope lore) m a -> m a+withoutAliases ::+  (HasScope (Aliases lore) m, Monad m) =>+  ReaderT (Scope lore) m a ->+  m a withoutAliases m = do   scope <- asksScope removeScopeAliases   runReaderT m scope@@ -126,42 +142,49 @@   bodyAliases = map unAliases . fst . fst . bodyDec   consumedInBody = unAliases . snd . fst . bodyDec -instance PrettyAnnot (PatElemT dec) =>-  PrettyAnnot (PatElemT (VarAliases, dec)) where-+instance+  PrettyAnnot (PatElemT dec) =>+  PrettyAnnot (PatElemT (VarAliases, dec))+  where   ppAnnot (PatElem name (AliasDec als, dec)) =     let alias_comment = PP.oneLine <$> aliasComment name als-    in case (alias_comment, ppAnnot (PatElem name dec)) of-         (_, Nothing) ->-           alias_comment-         (Just alias_comment', Just inner_comment) ->-           Just $ alias_comment' PP.</> inner_comment-         (Nothing, Just inner_comment) ->-           Just inner_comment-+     in case (alias_comment, ppAnnot (PatElem name dec)) of+          (_, Nothing) ->+            alias_comment+          (Just alias_comment', Just inner_comment) ->+            Just $ alias_comment' PP.</> inner_comment+          (Nothing, Just inner_comment) ->+            Just inner_comment  instance (ASTLore lore, CanBeAliased (Op lore)) => PrettyLore (Aliases lore) where   ppExpLore (consumed, inner) e =-    maybeComment $ catMaybes [exp_dec,-                              merge_dec,-                              ppExpLore inner $ removeExpAliases e]-    where merge_dec =-            case e of-              DoLoop _ merge _ body ->-                let mergeParamAliases fparam als-                      | primType (paramType fparam) =-                          Nothing-                      | otherwise =-                          resultAliasComment (paramName fparam) als-                in maybeComment $ catMaybes $-                   zipWith mergeParamAliases (map fst merge) $-                   bodyAliases body-              _ -> Nothing+    maybeComment $+      catMaybes+        [ exp_dec,+          merge_dec,+          ppExpLore inner $ removeExpAliases e+        ]+    where+      merge_dec =+        case e of+          DoLoop _ merge _ body ->+            let mergeParamAliases fparam als+                  | primType (paramType fparam) =+                    Nothing+                  | otherwise =+                    resultAliasComment (paramName fparam) als+             in maybeComment $+                  catMaybes $+                    zipWith mergeParamAliases (map fst merge) $+                      bodyAliases body+          _ -> Nothing -          exp_dec = case namesToList $ unAliases consumed of-            []  -> Nothing-            als -> Just $ PP.oneLine $-                   PP.text "-- Consumes " <> PP.commasep (map PP.ppr als)+      exp_dec = case namesToList $ unAliases consumed of+        [] -> Nothing+        als ->+          Just $+            PP.oneLine $+              PP.text "-- Consumes " <> PP.commasep (map PP.ppr als)  maybeComment :: [PP.Doc] -> Maybe PP.Doc maybeComment [] = Nothing@@ -171,117 +194,153 @@ aliasComment name als =   case namesToList als of     [] -> Nothing-    als' -> Just $ PP.oneLine $-            PP.text "-- " <> PP.ppr name <> PP.text " aliases " <>-            PP.commasep (map PP.ppr als')+    als' ->+      Just $+        PP.oneLine $+          PP.text "-- " <> PP.ppr name <> PP.text " aliases "+            <> PP.commasep (map PP.ppr als')  resultAliasComment :: PP.Pretty a => a -> Names -> Maybe PP.Doc resultAliasComment name als =   case namesToList als of     [] -> Nothing-    als' -> Just $ PP.oneLine $-            PP.text "-- Result of " <> PP.ppr name <> PP.text " aliases " <>-            PP.commasep (map PP.ppr als')+    als' ->+      Just $+        PP.oneLine $+          PP.text "-- Result of " <> PP.ppr name <> PP.text " aliases "+            <> PP.commasep (map PP.ppr als')  removeAliases :: CanBeAliased (Op lore) => Rephraser Identity (Aliases lore) lore-removeAliases = Rephraser { rephraseExpLore = return . snd-                          , rephraseLetBoundLore = return . snd-                          , rephraseBodyLore = return . snd-                          , rephraseFParamLore = return-                          , rephraseLParamLore = return-                          , rephraseRetType = return-                          , rephraseBranchType = return-                          , rephraseOp = return . removeOpAliases-                          }+removeAliases =+  Rephraser+    { rephraseExpLore = return . snd,+      rephraseLetBoundLore = return . snd,+      rephraseBodyLore = return . snd,+      rephraseFParamLore = return,+      rephraseLParamLore = return,+      rephraseRetType = return,+      rephraseBranchType = return,+      rephraseOp = return . removeOpAliases+    }  removeScopeAliases :: Scope (Aliases lore) -> Scope lore removeScopeAliases = M.map unAlias-  where unAlias (LetName (_, dec)) = LetName dec-        unAlias (FParamName dec) = FParamName dec-        unAlias (LParamName dec) = LParamName dec-        unAlias (IndexName it) = IndexName it+  where+    unAlias (LetName (_, dec)) = LetName dec+    unAlias (FParamName dec) = FParamName dec+    unAlias (LParamName dec) = LParamName dec+    unAlias (IndexName it) = IndexName it -removeProgAliases :: CanBeAliased (Op lore) =>-                     Prog (Aliases lore) -> Prog lore+removeProgAliases ::+  CanBeAliased (Op lore) =>+  Prog (Aliases lore) ->+  Prog lore removeProgAliases = runIdentity . rephraseProg removeAliases -removeFunDefAliases :: CanBeAliased (Op lore) =>-                       FunDef (Aliases lore) -> FunDef lore+removeFunDefAliases ::+  CanBeAliased (Op lore) =>+  FunDef (Aliases lore) ->+  FunDef lore removeFunDefAliases = runIdentity . rephraseFunDef removeAliases -removeExpAliases :: CanBeAliased (Op lore) =>-                    Exp (Aliases lore) -> Exp lore+removeExpAliases ::+  CanBeAliased (Op lore) =>+  Exp (Aliases lore) ->+  Exp lore removeExpAliases = runIdentity . rephraseExp removeAliases -removeStmAliases :: CanBeAliased (Op lore) =>-                        Stm (Aliases lore) -> Stm lore+removeStmAliases ::+  CanBeAliased (Op lore) =>+  Stm (Aliases lore) ->+  Stm lore removeStmAliases = runIdentity . rephraseStm removeAliases -removeLambdaAliases :: CanBeAliased (Op lore) =>-                       Lambda (Aliases lore) -> Lambda lore+removeLambdaAliases ::+  CanBeAliased (Op lore) =>+  Lambda (Aliases lore) ->+  Lambda lore removeLambdaAliases = runIdentity . rephraseLambda removeAliases -removePatternAliases :: PatternT (AliasDec, a)-                     -> PatternT a+removePatternAliases ::+  PatternT (AliasDec, a) ->+  PatternT a removePatternAliases = runIdentity . rephrasePattern (return . snd) -addAliasesToPattern :: (ASTLore lore, CanBeAliased (Op lore), Typed dec) =>-                       PatternT dec -> Exp (Aliases lore)-                    -> PatternT (VarAliases, dec)+addAliasesToPattern ::+  (ASTLore lore, CanBeAliased (Op lore), Typed dec) =>+  PatternT dec ->+  Exp (Aliases lore) ->+  PatternT (VarAliases, dec) addAliasesToPattern pat e =   uncurry Pattern $ mkPatternAliases pat e -mkAliasedBody :: (ASTLore lore, CanBeAliased (Op lore)) =>-                 BodyDec lore -> Stms (Aliases lore) -> Result -> Body (Aliases lore)+mkAliasedBody ::+  (ASTLore lore, CanBeAliased (Op lore)) =>+  BodyDec lore ->+  Stms (Aliases lore) ->+  Result ->+  Body (Aliases lore) mkAliasedBody innerlore bnds res =   Body (mkBodyAliases bnds res, innerlore) bnds res -mkPatternAliases :: (Aliased lore, Typed dec) =>-                    PatternT dec -> Exp lore-                 -> ([PatElemT (VarAliases, dec)],-                     [PatElemT (VarAliases, dec)])+mkPatternAliases ::+  (Aliased lore, Typed dec) =>+  PatternT dec ->+  Exp lore ->+  ( [PatElemT (VarAliases, dec)],+    [PatElemT (VarAliases, dec)]+  ) mkPatternAliases pat e =   -- Some part of the pattern may be the context.  This does not have   -- aliases from expAliases, so we use a hack to compute aliases of   -- the context.   let als = expAliases e ++ repeat mempty -- In case the pattern has-                                          -- more elements (this-                                          -- implies a type error).+  -- more elements (this+  -- implies a type error).       context_als = mkContextAliases pat e-  in (zipWith annotateBindee (patternContextElements pat) context_als,-      zipWith annotateBindee (patternValueElements pat) als)-  where annotateBindee bindee names =-            bindee `setPatElemLore` (AliasDec names', patElemDec bindee)-          where names' =-                  case patElemType bindee of-                    Array {} -> names-                    Mem _    -> names-                    _        -> mempty+   in ( zipWith annotateBindee (patternContextElements pat) context_als,+        zipWith annotateBindee (patternValueElements pat) als+      )+  where+    annotateBindee bindee names =+      bindee `setPatElemLore` (AliasDec names', patElemDec bindee)+      where+        names' =+          case patElemType bindee of+            Array {} -> names+            Mem _ -> names+            _ -> mempty -mkContextAliases :: Aliased lore =>-                    PatternT dec -> Exp lore -> [Names]+mkContextAliases ::+  Aliased lore =>+  PatternT dec ->+  Exp lore ->+  [Names] mkContextAliases pat (DoLoop ctxmerge valmerge _ body) =   let ctx = map fst ctxmerge       init_als = zip mergenames $ map (subExpAliases . snd) $ ctxmerge ++ valmerge       expand als = als <> mconcat (mapMaybe (`lookup` init_als) (namesToList als))-      merge_als = zip mergenames $-                  map ((`namesSubtract` mergenames_set) . expand) $-                  bodyAliases body-  in if length ctx == length (patternContextElements pat)-     then map (fromMaybe mempty . flip lookup merge_als . paramName) ctx-     else map (const mempty) $ patternContextElements pat-  where mergenames = map (paramName . fst) $ ctxmerge ++ valmerge-        mergenames_set = namesFromList mergenames+      merge_als =+        zip mergenames $+          map ((`namesSubtract` mergenames_set) . expand) $+            bodyAliases body+   in if length ctx == length (patternContextElements pat)+        then map (fromMaybe mempty . flip lookup merge_als . paramName) ctx+        else map (const mempty) $ patternContextElements pat+  where+    mergenames = map (paramName . fst) $ ctxmerge ++ valmerge+    mergenames_set = namesFromList mergenames mkContextAliases pat (If _ tbranch fbranch _) =   take (length $ patternContextNames pat) $-  zipWith (<>) (bodyAliases tbranch) (bodyAliases fbranch)+    zipWith (<>) (bodyAliases tbranch) (bodyAliases fbranch) mkContextAliases pat _ =   replicate (length $ patternContextElements pat) mempty -mkBodyAliases :: Aliased lore =>-                 Stms lore-              -> Result-              -> BodyAliasing+mkBodyAliases ::+  Aliased lore =>+  Stms lore ->+  Result ->+  BodyAliasing mkBodyAliases bnds res =   -- We need to remove the names that are bound in bnds from the alias   -- and consumption sets.  We do this by computing the transitive@@ -292,56 +351,70 @@         foldMap (namesFromList . patternNames . stmPattern) bnds       aliases' = map (`namesSubtract` boundNames) aliases       consumed' = consumed `namesSubtract` boundNames-  in (map AliasDec aliases', AliasDec consumed')+   in (map AliasDec aliases', AliasDec consumed') -mkStmsAliases :: Aliased lore =>-                 Stms lore -> [SubExp]-              -> ([Names], Names)+mkStmsAliases ::+  Aliased lore =>+  Stms lore ->+  [SubExp] ->+  ([Names], Names) mkStmsAliases bnds res = delve mempty $ stmsToList bnds-  where delve (aliasmap, consumed) [] =-          (map (aliasClosure aliasmap . subExpAliases) res,-           consumed)-        delve (aliasmap, consumed) (bnd:bnds') =-          delve (trackAliases (aliasmap, consumed) bnd) bnds'-        aliasClosure aliasmap names =-          names <> mconcat (map look $ namesToList names)-          where look k = M.findWithDefault mempty k aliasmap+  where+    delve (aliasmap, consumed) [] =+      ( map (aliasClosure aliasmap . subExpAliases) res,+        consumed+      )+    delve (aliasmap, consumed) (bnd : bnds') =+      delve (trackAliases (aliasmap, consumed) bnd) bnds'+    aliasClosure aliasmap names =+      names <> mconcat (map look $ namesToList names)+      where+        look k = M.findWithDefault mempty k aliasmap  -- | Everything consumed in the given statements and result (even -- transitively). consumedInStms :: Aliased lore => Stms lore -> Names consumedInStms = snd . flip mkStmsAliases [] -type AliasesAndConsumed = (M.Map VName Names,-                           Names)+type AliasesAndConsumed =+  ( M.Map VName Names,+    Names+  ) -trackAliases :: Aliased lore =>-                AliasesAndConsumed -> Stm lore-             -> AliasesAndConsumed+trackAliases ::+  Aliased lore =>+  AliasesAndConsumed ->+  Stm lore ->+  AliasesAndConsumed trackAliases (aliasmap, consumed) bnd =   let pat = stmPattern bnd-      als = M.fromList $-            zip (patternNames pat) (map addAliasesOfAliases $ patternAliases pat)+      als =+        M.fromList $+          zip (patternNames pat) (map addAliasesOfAliases $ patternAliases pat)       aliasmap' = als <> aliasmap       consumed' = consumed <> addAliasesOfAliases (consumedInStm bnd)-  in (aliasmap', consumed')-  where addAliasesOfAliases names = names <> aliasesOfAliases names-        aliasesOfAliases =  mconcat . map look . namesToList-        look k = M.findWithDefault mempty k aliasmap+   in (aliasmap', consumed')+  where+    addAliasesOfAliases names = names <> aliasesOfAliases names+    aliasesOfAliases = mconcat . map look . namesToList+    look k = M.findWithDefault mempty k aliasmap -mkAliasedLetStm :: (ASTLore lore, CanBeAliased (Op lore)) =>-                   Pattern lore-                -> StmAux (ExpDec lore) -> Exp (Aliases lore)-                -> Stm (Aliases lore)+mkAliasedLetStm ::+  (ASTLore lore, CanBeAliased (Op lore)) =>+  Pattern lore ->+  StmAux (ExpDec lore) ->+  Exp (Aliases lore) ->+  Stm (Aliases lore) mkAliasedLetStm pat (StmAux cs attrs dec) e =-  Let (addAliasesToPattern pat e)-  (StmAux cs attrs (AliasDec $ consumedInExp e, dec))-  e+  Let+    (addAliasesToPattern pat e)+    (StmAux cs attrs (AliasDec $ consumedInExp e, dec))+    e  instance (Bindable lore, CanBeAliased (Op lore)) => Bindable (Aliases lore) where   mkExpDec pat e =     let dec = mkExpDec (removePatternAliases pat) $ removeExpAliases e-    in (AliasDec $ consumedInExp e, dec)+     in (AliasDec $ consumedInExp e, dec)    mkExpPat ctx val e =     addAliasesToPattern (mkExpPat ctx val $ removeExpAliases e) e@@ -354,6 +427,6 @@    mkBody bnds res =     let Body bodylore _ _ = mkBody (fmap removeStmAliases bnds) res-    in mkAliasedBody bodylore bnds res+     in mkAliasedBody bodylore bnds res -instance (ASTLore (Aliases lore), Bindable (Aliases lore)) => BinderOps (Aliases lore) where+instance (ASTLore (Aliases lore), Bindable (Aliases lore)) => BinderOps (Aliases lore)
src/Futhark/IR/Decorations.hs view
@@ -1,52 +1,101 @@-{-# LANGUAGE TypeFamilies, FlexibleContexts #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+ -- | The core Futhark AST is parameterised by a @lore@ type parameter, -- which is then used to invoke the type families defined here. module Futhark.IR.Decorations-       ( Decorations (..)-       , module Futhark.IR.RetType-       )-       where+  ( Decorations (..),+    module Futhark.IR.RetType,+  )+where  import qualified Data.Kind--import Futhark.IR.Syntax.Core-import Futhark.IR.RetType import Futhark.IR.Prop.Types+import Futhark.IR.RetType+import Futhark.IR.Syntax.Core+import Language.SexpGrammar (SexpIso)  -- | A collection of type families, along with constraints specifying -- that the types they map to should satisfy some minimal -- requirements.-class (Show (LetDec l), Show (ExpDec l), Show (BodyDec l), Show (FParamInfo l), Show (LParamInfo l), Show (RetType l), Show (BranchType l), Show (Op l),-       Eq (LetDec l), Eq (ExpDec l), Eq (BodyDec l), Eq (FParamInfo l), Eq (LParamInfo l), Eq (RetType l), Eq (BranchType l), Eq (Op l),-       Ord (LetDec l), Ord (ExpDec l), Ord (BodyDec l), Ord (FParamInfo l), Ord (LParamInfo l), Ord (RetType l), Ord (BranchType l), Ord (Op l),-       IsRetType (RetType l), IsBodyType (BranchType l),-       Typed (FParamInfo l), Typed (LParamInfo l), Typed (LetDec l),-       DeclTyped (FParamInfo l))-      => Decorations l where+class+  ( Show (LetDec l),+    Show (ExpDec l),+    Show (BodyDec l),+    Show (FParamInfo l),+    Show (LParamInfo l),+    Show (RetType l),+    Show (BranchType l),+    Show (Op l),+    Eq (LetDec l),+    Eq (ExpDec l),+    Eq (BodyDec l),+    Eq (FParamInfo l),+    Eq (LParamInfo l),+    Eq (RetType l),+    Eq (BranchType l),+    Eq (Op l),+    Ord (LetDec l),+    Ord (ExpDec l),+    Ord (BodyDec l),+    Ord (FParamInfo l),+    Ord (LParamInfo l),+    Ord (RetType l),+    Ord (BranchType l),+    Ord (Op l),+    IsRetType (RetType l),+    IsBodyType (BranchType l),+    Typed (FParamInfo l),+    Typed (LParamInfo l),+    Typed (LetDec l),+    DeclTyped (FParamInfo l),+    SexpIso (LetDec l),+    SexpIso (ExpDec l),+    SexpIso (BodyDec l),+    SexpIso (FParamInfo l),+    SexpIso (LParamInfo l),+    SexpIso (RetType l),+    SexpIso (BranchType l),+    SexpIso (Op l)+  ) =>+  Decorations l+  where   -- | Decoration for every let-pattern element.   type LetDec l :: Data.Kind.Type+   type LetDec l = Type+   -- | Decoration for every expression.   type ExpDec l :: Data.Kind.Type+   type ExpDec l = ()+   -- | Decoration for every body.   type BodyDec l :: Data.Kind.Type+   type BodyDec l = ()+   -- | Decoration for every (non-lambda) function parameter.   type FParamInfo l :: Data.Kind.Type+   type FParamInfo l = DeclType+   -- | Decoration for every lambda function parameter.   type LParamInfo l :: Data.Kind.Type+   type LParamInfo l = Type    -- | The return type decoration of function calls.   type RetType l :: Data.Kind.Type+   type RetType l = DeclExtType    -- | The return type decoration of branches.   type BranchType l :: Data.Kind.Type+   type BranchType l = ExtType    -- | Extensible operation.   type Op l :: Data.Kind.Type+   type Op l = ()
src/Futhark/IR/Kernels.hs view
@@ -1,29 +1,31 @@-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeFamilies #-}+ -- | A representation with flat parallelism via GPU-oriented kernels. module Futhark.IR.Kernels-       ( -- * The Lore definition-         Kernels-         -- * Module re-exports-       , module Futhark.IR.Prop-       , module Futhark.IR.Traversals-       , module Futhark.IR.Pretty-       , module Futhark.IR.Syntax-       , module Futhark.IR.Kernels.Kernel-       , module Futhark.IR.Kernels.Sizes-       , module Futhark.IR.SOACS.SOAC-       )+  ( -- * The Lore definition+    Kernels,++    -- * Module re-exports+    module Futhark.IR.Prop,+    module Futhark.IR.Traversals,+    module Futhark.IR.Pretty,+    module Futhark.IR.Syntax,+    module Futhark.IR.Kernels.Kernel,+    module Futhark.IR.Kernels.Sizes,+    module Futhark.IR.SOACS.SOAC,+  ) where -import Futhark.IR.Syntax+import Futhark.Binder+import Futhark.Construct import Futhark.IR.Kernels.Kernel import Futhark.IR.Kernels.Sizes+import Futhark.IR.Pretty import Futhark.IR.Prop+import Futhark.IR.SOACS.SOAC hiding (HistOp (..))+import Futhark.IR.Syntax import Futhark.IR.Traversals-import Futhark.IR.Pretty-import Futhark.IR.SOACS.SOAC hiding (HistOp(..))-import Futhark.Binder-import Futhark.Construct import qualified Futhark.TypeCheck as TypeCheck  -- | The phantom data type for the kernels representation.@@ -31,15 +33,17 @@  instance Decorations Kernels where   type Op Kernels = HostOp Kernels (SOAC Kernels)+ instance ASTLore Kernels where   expTypesFromPattern = return . expExtTypesFromPattern  instance TypeCheck.CheckableOp Kernels where   checkOp = typeCheckKernelsOp Nothing-    where typeCheckKernelsOp lvl =-            typeCheckHostOp (typeCheckKernelsOp . Just) lvl typeCheckSOAC+    where+      typeCheckKernelsOp lvl =+        typeCheckHostOp (typeCheckKernelsOp . Just) lvl typeCheckSOAC -instance TypeCheck.Checkable Kernels where+instance TypeCheck.Checkable Kernels  instance Bindable Kernels where   mkBody = Body ()@@ -47,9 +51,9 @@   mkExpDec _ _ = ()   mkLetNames = simpleMkLetNames -instance BinderOps Kernels where+instance BinderOps Kernels -instance PrettyLore Kernels where+instance PrettyLore Kernels  instance HasSegOp Kernels where   type SegOpLevel Kernels = SegLevel
src/Futhark/IR/Kernels/Kernel.hs view
@@ -1,81 +1,117 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+ module Futhark.IR.Kernels.Kernel   ( -- * Size operations-    SizeOp(..)+    SizeOp (..),      -- * Host operations-  , HostOp(..)-  , typeCheckHostOp+    HostOp (..),+    typeCheckHostOp,      -- * SegOp refinements-  , SegLevel(..)+    SegLevel (..),      -- * Reexports-  , module Futhark.IR.Kernels.Sizes-  , module Futhark.IR.SegOp+    module Futhark.IR.Kernels.Sizes,+    module Futhark.IR.SegOp,   ) where -import Futhark.IR+import Control.Category+import Futhark.Analysis.Metrics import qualified Futhark.Analysis.SymbolTable as ST-import qualified Futhark.Util.Pretty as PP-import Futhark.Util.Pretty-  ((</>), (<+>), ppr, commasep, parens, text)-import Futhark.Transform.Substitute-import Futhark.Transform.Rename-import Futhark.Optimise.Simplify.Lore-import qualified Futhark.Optimise.Simplify.Engine as Engine-import Futhark.IR.Prop.Aliases+import Futhark.IR import Futhark.IR.Aliases (Aliases)-import Futhark.IR.SegOp import Futhark.IR.Kernels.Sizes+import Futhark.IR.Prop.Aliases+import Futhark.IR.SegOp+import qualified Futhark.Optimise.Simplify.Engine as Engine+import Futhark.Optimise.Simplify.Lore+import Futhark.Transform.Rename+import Futhark.Transform.Substitute import qualified Futhark.TypeCheck as TC-import Futhark.Analysis.Metrics+import Futhark.Util.Pretty+  ( commasep,+    parens,+    ppr,+    text,+    (<+>),+    (</>),+  )+import qualified Futhark.Util.Pretty as PP+import GHC.Generics (Generic)+import Language.SexpGrammar as Sexp+import Language.SexpGrammar.Generic+import Prelude hiding (id, (.))  -- | At which level the *body* of a t'SegOp' executes.-data SegLevel = SegThread { segNumGroups :: Count NumGroups SubExp-                          , segGroupSize :: Count GroupSize SubExp-                          , segVirt :: SegVirt }-              | SegGroup { segNumGroups :: Count NumGroups SubExp-                         , segGroupSize :: Count GroupSize SubExp-                         , segVirt :: SegVirt }-              deriving (Eq, Ord, Show)+data SegLevel+  = SegThread+      { segNumGroups :: Count NumGroups SubExp,+        segGroupSize :: Count GroupSize SubExp,+        segVirt :: SegVirt+      }+  | SegGroup+      { segNumGroups :: Count NumGroups SubExp,+        segGroupSize :: Count GroupSize SubExp,+        segVirt :: SegVirt+      }+  deriving (Eq, Ord, Show, Generic) +instance SexpIso SegLevel where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "thread") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+        With+          (. Sexp.list (Sexp.el (Sexp.sym "group") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso))+          End+ instance PP.Pretty SegLevel where   ppr lvl =-    lvl' </>-    PP.parens (text "#groups=" <> ppr (segNumGroups lvl) <> PP.semi <+>-               text "groupsize=" <> ppr (segGroupSize lvl) <>-               case segVirt lvl of-                 SegNoVirt -> mempty-                 SegNoVirtFull -> PP.semi <+> text "full"-                 SegVirt -> PP.semi <+> text "virtualise")--    where lvl' = case lvl of SegThread{} -> "_thread"-                             SegGroup{} -> "_group"+    lvl'+      </> PP.parens+        ( text "#groups=" <> ppr (segNumGroups lvl) <> PP.semi+            <+> text "groupsize="+            <> ppr (segGroupSize lvl)+            <> case segVirt lvl of+              SegNoVirt -> mempty+              SegNoVirtFull -> PP.semi <+> text "full"+              SegVirt -> PP.semi <+> text "virtualise"+        )+    where+      lvl' = case lvl of+        SegThread {} -> "_thread"+        SegGroup {} -> "_group"  instance Engine.Simplifiable SegLevel where   simplify (SegThread num_groups group_size virt) =-    SegThread <$> traverse Engine.simplify num_groups <*>-    traverse Engine.simplify group_size <*> pure virt+    SegThread <$> traverse Engine.simplify num_groups+      <*> traverse Engine.simplify group_size+      <*> pure virt   simplify (SegGroup num_groups group_size virt) =-    SegGroup <$> traverse Engine.simplify num_groups <*>-    traverse Engine.simplify group_size <*> pure virt+    SegGroup <$> traverse Engine.simplify num_groups+      <*> traverse Engine.simplify group_size+      <*> pure virt  instance Substitute SegLevel where   substituteNames substs (SegThread num_groups group_size virt) =     SegThread-    (substituteNames substs num_groups) (substituteNames substs group_size) virt+      (substituteNames substs num_groups)+      (substituteNames substs group_size)+      virt   substituteNames substs (SegGroup num_groups group_size virt) =     SegGroup-    (substituteNames substs num_groups) (substituteNames substs group_size) virt+      (substituteNames substs num_groups)+      (substituteNames substs group_size)+      virt  instance Rename SegLevel where   rename = substituteRename@@ -88,8 +124,7 @@  -- | A simple size-level query or computation. data SizeOp-  = SplitSpace SplitOrdering SubExp SubExp SubExp-    -- ^ @SplitSpace o w i elems_per_thread@.+  = -- | @SplitSpace o w i elems_per_thread@.     --     -- Computes how to divide array elements to     -- threads in a kernel.  Returns the number of@@ -110,42 +145,54 @@     -- the thread will receive elements @i,     -- i+stride, i+2*stride, ...,     -- i+(elems_per_thread-1)*stride@.-  | GetSize Name SizeClass-    -- ^ Produce some runtime-configurable size.-  | GetSizeMax SizeClass-    -- ^ The maximum size of some class.-  | CmpSizeLe Name SizeClass SubExp-    -- ^ Compare size (likely a threshold) with some integer value.-  | CalcNumGroups SubExp Name SubExp-    -- ^ @CalcNumGroups w max_num_groups group_size@ calculates the+    SplitSpace SplitOrdering SubExp SubExp SubExp+  | -- | Produce some runtime-configurable size.+    GetSize Name SizeClass+  | -- | The maximum size of some class.+    GetSizeMax SizeClass+  | -- | Compare size (likely a threshold) with some integer value.+    CmpSizeLe Name SizeClass SubExp+  | -- | @CalcNumGroups w max_num_groups group_size@ calculates the     -- number of GPU workgroups to use for an input of the given size.     -- The @Name@ is a size name.  Note that @w@ is an i64 to avoid     -- overflow issues.-  deriving (Eq, Ord, Show)+    CalcNumGroups SubExp Name SubExp+  deriving (Eq, Ord, Show, Generic) +instance SexpIso SizeOp where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "split-space") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+        With (. Sexp.list (Sexp.el (Sexp.sym "get-size") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+          With (. Sexp.list (Sexp.el (Sexp.sym "get-size-max") >>> Sexp.el sexpIso)) $+            With (. Sexp.list (Sexp.el (Sexp.sym "cmp-size-le") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+              With+                (. Sexp.list (Sexp.el (Sexp.sym "calc-num-groups") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso))+                End+ instance Substitute SizeOp where   substituteNames subst (SplitSpace o w i elems_per_thread) =     SplitSpace-    (substituteNames subst o)-    (substituteNames subst w)-    (substituteNames subst i)-    (substituteNames subst elems_per_thread)+      (substituteNames subst o)+      (substituteNames subst w)+      (substituteNames subst i)+      (substituteNames subst elems_per_thread)   substituteNames substs (CmpSizeLe name sclass x) =     CmpSizeLe name sclass (substituteNames substs x)   substituteNames substs (CalcNumGroups w max_num_groups group_size) =     CalcNumGroups-    (substituteNames substs w)-    max_num_groups-    (substituteNames substs group_size)+      (substituteNames substs w)+      max_num_groups+      (substituteNames substs group_size)   substituteNames _ op = op  instance Rename SizeOp where   rename (SplitSpace o w i elems_per_thread) =     SplitSpace-    <$> rename o-    <*> rename w-    <*> rename i-    <*> rename elems_per_thread+      <$> rename o+      <*> rename w+      <*> rename i+      <*> rename elems_per_thread   rename (CmpSizeLe name sclass x) =     CmpSizeLe name sclass <$> rename x   rename (CalcNumGroups w max_num_groups group_size) =@@ -157,11 +204,11 @@   cheapOp _ = True  instance TypedOp SizeOp where-  opType SplitSpace{} = pure [Prim int32]-  opType (GetSize _ _) = pure [Prim int32]-  opType (GetSizeMax _) = pure [Prim int32]-  opType CmpSizeLe{} = pure [Prim Bool]-  opType CalcNumGroups{} = pure [Prim int32]+  opType SplitSpace {} = pure [Prim int64]+  opType (GetSize _ _) = pure [Prim int64]+  opType (GetSizeMax _) = pure [Prim int64]+  opType CmpSizeLe {} = pure [Prim Bool]+  opType CalcNumGroups {} = pure [Prim int64]  instance AliasedOp SizeOp where   opAliases _ = [mempty]@@ -176,51 +223,60 @@  instance PP.Pretty SizeOp where   ppr (SplitSpace o w i elems_per_thread) =-    text "splitSpace" <> suff <>-    parens (commasep [ppr w, ppr i, ppr elems_per_thread])-    where suff = case o of SplitContiguous     -> mempty-                           SplitStrided stride -> text "Strided" <> parens (ppr stride)-+    text "splitSpace" <> suff+      <> parens (commasep [ppr w, ppr i, ppr elems_per_thread])+    where+      suff = case o of+        SplitContiguous -> mempty+        SplitStrided stride -> text "Strided" <> parens (ppr stride)   ppr (GetSize name size_class) =     text "get_size" <> parens (commasep [ppr name, ppr size_class])-   ppr (GetSizeMax size_class) =     text "get_size_max" <> parens (commasep [ppr size_class])-   ppr (CmpSizeLe name size_class x) =-    text "get_size" <> parens (commasep [ppr name, ppr size_class]) <+>-    text "<=" <+> ppr x-+    text "get_size" <> parens (commasep [ppr name, ppr size_class])+      <+> text "<="+      <+> ppr x   ppr (CalcNumGroups w max_num_groups group_size) =     text "calc_num_groups" <> parens (commasep [ppr w, ppr max_num_groups, ppr group_size])  instance OpMetrics SizeOp where-  opMetrics SplitSpace{} = seen "SplitSpace"-  opMetrics GetSize{} = seen "GetSize"-  opMetrics GetSizeMax{} = seen "GetSizeMax"-  opMetrics CmpSizeLe{} = seen "CmpSizeLe"-  opMetrics CalcNumGroups{} = seen "CalcNumGroups"+  opMetrics SplitSpace {} = seen "SplitSpace"+  opMetrics GetSize {} = seen "GetSize"+  opMetrics GetSizeMax {} = seen "GetSizeMax"+  opMetrics CmpSizeLe {} = seen "CmpSizeLe"+  opMetrics CalcNumGroups {} = seen "CalcNumGroups"  typeCheckSizeOp :: TC.Checkable lore => SizeOp -> TC.TypeM lore () typeCheckSizeOp (SplitSpace o w i elems_per_thread) = do   case o of-    SplitContiguous     -> return ()-    SplitStrided stride -> TC.require [Prim int32] stride-  mapM_ (TC.require [Prim int32]) [w, i, elems_per_thread]-typeCheckSizeOp GetSize{} = return ()-typeCheckSizeOp GetSizeMax{} = return ()-typeCheckSizeOp (CmpSizeLe _ _ x) = TC.require [Prim int32] x-typeCheckSizeOp (CalcNumGroups w _ group_size) = do TC.require [Prim int64] w-                                                    TC.require [Prim int32] group_size+    SplitContiguous -> return ()+    SplitStrided stride -> TC.require [Prim int64] stride+  mapM_ (TC.require [Prim int64]) [w, i, elems_per_thread]+typeCheckSizeOp GetSize {} = return ()+typeCheckSizeOp GetSizeMax {} = return ()+typeCheckSizeOp (CmpSizeLe _ _ x) = TC.require [Prim int64] x+typeCheckSizeOp (CalcNumGroups w _ group_size) = do+  TC.require [Prim int64] w+  TC.require [Prim int64] group_size  -- | A host-level operation; parameterised by what else it can do. data HostOp lore op-  = SegOp (SegOp SegLevel lore)-    -- ^ A segmented operation.+  = -- | A segmented operation.+    SegOp (SegOp SegLevel lore)   | SizeOp SizeOp   | OtherOp op-  deriving (Eq, Ord, Show)+  deriving (Eq, Ord, Show, Generic) +instance (SexpIso op, Decorations lore) => SexpIso (HostOp lore op) where+  sexpIso =+    match $+      With (. sexpIso) $+        With (. sexpIso) $+          With+            (. sexpIso)+            End+ instance (ASTLore lore, Substitute op) => Substitute (HostOp lore op) where   substituteNames substs (SegOp op) =     SegOp $ substituteNames substs op@@ -295,28 +351,32 @@   opMetrics (OtherOp op) = opMetrics op   opMetrics (SizeOp op) = opMetrics op -checkSegLevel :: TC.Checkable lore =>-                 Maybe SegLevel -> SegLevel -> TC.TypeM lore ()+checkSegLevel ::+  TC.Checkable lore =>+  Maybe SegLevel ->+  SegLevel ->+  TC.TypeM lore () checkSegLevel Nothing lvl = do-  TC.require [Prim int32] $ unCount $ segNumGroups lvl-  TC.require [Prim int32] $ unCount $ segGroupSize lvl-checkSegLevel (Just SegThread{}) _ =+  TC.require [Prim int64] $ unCount $ segNumGroups lvl+  TC.require [Prim int64] $ unCount $ segGroupSize lvl+checkSegLevel (Just SegThread {}) _ =   TC.bad $ TC.TypeError "SegOps cannot occur when already at thread level." checkSegLevel (Just x) y   | x == y = TC.bad $ TC.TypeError $ "Already at at level " ++ pretty x   | segNumGroups x /= segNumGroups y || segGroupSize x /= segGroupSize y =-      TC.bad $ TC.TypeError "Physical layout for SegLevel does not match parent SegLevel."+    TC.bad $ TC.TypeError "Physical layout for SegLevel does not match parent SegLevel."   | otherwise =-      return ()+    return () -typeCheckHostOp :: TC.Checkable lore =>-                   (SegLevel -> OpWithAliases (Op lore) -> TC.TypeM lore ())-                -> Maybe SegLevel-                -> (op -> TC.TypeM lore ())-                -> HostOp (Aliases lore) op-                -> TC.TypeM lore ()+typeCheckHostOp ::+  TC.Checkable lore =>+  (SegLevel -> OpWithAliases (Op lore) -> TC.TypeM lore ()) ->+  Maybe SegLevel ->+  (op -> TC.TypeM lore ()) ->+  HostOp (Aliases lore) op ->+  TC.TypeM lore () typeCheckHostOp checker lvl _ (SegOp op) =   TC.checkOpWith (checker $ segLevel op) $-  typeCheckSegOp (checkSegLevel lvl) op+    typeCheckSegOp (checkSegLevel lvl) op typeCheckHostOp _ _ f (OtherOp op) = f op typeCheckHostOp _ _ _ (SizeOp op) = typeCheckSizeOp op
src/Futhark/IR/Kernels/Simplify.hs view
@@ -1,31 +1,31 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ConstraintKinds #-}-module Futhark.IR.Kernels.Simplify-       ( simplifyKernels-       , simplifyLambda+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} -       , Kernels+module Futhark.IR.Kernels.Simplify+  ( simplifyKernels,+    simplifyLambda,+    Kernels, -       -- * Building blocks-       , simplifyKernelOp-       )+    -- * Building blocks+    simplifyKernelOp,+  ) where +import qualified Futhark.Analysis.SymbolTable as ST import Futhark.IR.Kernels-import qualified Futhark.Optimise.Simplify.Engine as Engine-import Futhark.Optimise.Simplify.Rules-import Futhark.Optimise.Simplify.Lore-import Futhark.MonadFreshNames-import Futhark.Tools-import Futhark.Pass import qualified Futhark.IR.SOACS.Simplify as SOAC+import Futhark.MonadFreshNames import qualified Futhark.Optimise.Simplify as Simplify+import qualified Futhark.Optimise.Simplify.Engine as Engine+import Futhark.Optimise.Simplify.Lore import Futhark.Optimise.Simplify.Rule-import qualified Futhark.Analysis.SymbolTable as ST+import Futhark.Optimise.Simplify.Rules+import Futhark.Pass+import Futhark.Tools import qualified Futhark.Transform.FirstOrderTransform as FOT  simpleKernels :: Simplify.SimpleOps Kernels@@ -35,30 +35,35 @@ simplifyKernels =   Simplify.simplifyProg simpleKernels kernelRules Simplify.noExtraHoistBlockers -simplifyLambda :: (HasScope Kernels m, MonadFreshNames m) =>-                  Lambda Kernels -> m (Lambda Kernels)+simplifyLambda ::+  (HasScope Kernels m, MonadFreshNames m) =>+  Lambda Kernels ->+  m (Lambda Kernels) simplifyLambda =   Simplify.simplifyLambda simpleKernels kernelRules Engine.noExtraHoistBlockers -simplifyKernelOp :: (Engine.SimplifiableLore lore,-                     BodyDec lore ~ ()) =>-                    Simplify.SimplifyOp lore op-                 -> HostOp lore op-                 -> Engine.SimpleM lore (HostOp (Wise lore) (OpWithWisdom op), Stms (Wise lore))-+simplifyKernelOp ::+  ( Engine.SimplifiableLore lore,+    BodyDec lore ~ ()+  ) =>+  Simplify.SimplifyOp lore op ->+  HostOp lore op ->+  Engine.SimpleM lore (HostOp (Wise lore) (OpWithWisdom op), Stms (Wise lore)) simplifyKernelOp f (OtherOp op) = do   (op', stms) <- f op   return (OtherOp op', stms)- simplifyKernelOp _ (SegOp op) = do   (op', hoisted) <- simplifySegOp op   return (SegOp op', hoisted)- simplifyKernelOp _ (SizeOp (SplitSpace o w i elems_per_thread)) =-  (,) <$> (SizeOp <$>-           (SplitSpace <$> Engine.simplify o <*> Engine.simplify w-            <*> Engine.simplify i <*> Engine.simplify elems_per_thread))-      <*> pure mempty+  (,)+    <$> ( SizeOp+            <$> ( SplitSpace <$> Engine.simplify o <*> Engine.simplify w+                    <*> Engine.simplify i+                    <*> Engine.simplify elems_per_thread+                )+        )+    <*> pure mempty simplifyKernelOp _ (SizeOp (GetSize key size_class)) =   return (SizeOp $ GetSize key size_class, mempty) simplifyKernelOp _ (SizeOp (GetSizeMax size_class)) =@@ -70,7 +75,7 @@   w' <- Engine.simplify w   return (SizeOp $ CalcNumGroups w' max_num_groups group_size, mempty) -instance BinderOps (Wise Kernels) where+instance BinderOps (Wise Kernels)  instance HasSegOp (Wise Kernels) where   type SegOpLevel (Wise Kernels) = SegLevel@@ -84,13 +89,16 @@   soacOp = OtherOp  kernelRules :: RuleBook (Wise Kernels)-kernelRules = standardRules <> segOpRules <>-              ruleBook-              [ RuleOp redomapIotaToLoop-              , RuleOp SOAC.simplifyKnownIterationSOAC-              , RuleOp SOAC.removeReplicateMapping ]-              [ RuleBasicOp removeUnnecessaryCopy-              , RuleOp SOAC.liftIdentityMapping ]+kernelRules =+  standardRules <> segOpRules+    <> ruleBook+      [ RuleOp redomapIotaToLoop,+        RuleOp SOAC.simplifyKnownIterationSOAC,+        RuleOp SOAC.removeReplicateMapping+      ]+      [ RuleBasicOp removeUnnecessaryCopy,+        RuleOp SOAC.liftIdentityMapping+      ]  -- We turn reductions over (solely) iotas into do-loops, because there -- is no useful structure here anyway.  This is mostly a hack to work@@ -99,7 +107,7 @@ redomapIotaToLoop :: TopDownRuleOp (Wise Kernels) redomapIotaToLoop vtable pat aux (OtherOp soac@(Screma _ form [arr]))   | Just _ <- isRedomapSOAC form,-    Just (Iota{}, _) <- ST.lookupBasicOp arr vtable =-      Simplify $ certifying (stmAuxCerts aux) $ FOT.transformSOAC pat soac+    Just (Iota {}, _) <- ST.lookupBasicOp arr vtable =+    Simplify $ certifying (stmAuxCerts aux) $ FOT.transformSOAC pat soac redomapIotaToLoop _ _ _ _ =   Skip
src/Futhark/IR/Kernels/Sizes.hs view
@@ -1,24 +1,33 @@+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Trustworthy #-}+ -- | In the context of this module, a "size" is any kind of tunable -- (run-time) constant. module Futhark.IR.Kernels.Sizes-  ( SizeClass (..)-  , sizeDefault-  , KernelPath-  , Count(..)-  , NumGroups, GroupSize, NumThreads+  ( SizeClass (..),+    sizeDefault,+    KernelPath,+    Count (..),+    NumGroups,+    GroupSize,+    NumThreads,   )-  where+where -import Data.Int (Int32)+import Control.Category+import Data.Int (Int64) import Data.Traversable--import Futhark.Util.Pretty+import Futhark.IR.Prop.Names (FreeIn) import Futhark.Transform.Substitute-import Language.Futhark.Core (Name) import Futhark.Util.IntegralExp (IntegralExp)-import Futhark.IR.Prop.Names (FreeIn)+import Futhark.Util.Pretty+import GHC.Generics (Generic)+import Language.Futhark.Core (Name)+import Language.SexpGrammar as Sexp+import Language.SexpGrammar.Generic+import Prelude hiding (id, (.))  -- | An indication of which comparisons have been performed to get to -- this point, as well as the result of each comparison.@@ -26,22 +35,36 @@  -- | The class of some kind of configurable size.  Each class may -- impose constraints on the valid values.-data SizeClass = SizeThreshold KernelPath (Maybe Int32)-                 -- ^ A threshold with an optional default.-               | SizeGroup-               | SizeNumGroups-               | SizeTile-               | SizeLocalMemory-               -- ^ Likely not useful on its own, but querying the-               -- maximum can be handy.-               | SizeBespoke Name Int32-               -- ^ A bespoke size with a default.-               deriving (Eq, Ord, Show)+data SizeClass+  = -- | A threshold with an optional default.+    SizeThreshold KernelPath (Maybe Int64)+  | SizeGroup+  | SizeNumGroups+  | SizeTile+  | -- | Likely not useful on its own, but querying the+    -- maximum can be handy.+    SizeLocalMemory+  | -- | A bespoke size with a default.+    SizeBespoke Name Int64+  deriving (Eq, Ord, Show, Generic) +instance SexpIso SizeClass where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "threshold") >>> Sexp.el sexpIso >>> props (Sexp.optKey "default" (iso fromIntegral fromIntegral . Sexp.int)))) $+        With (. Sexp.sym "group") $+          With (. Sexp.sym "num-groups") $+            With (. Sexp.sym "tile") $+              With (. Sexp.sym "local-memory") $+                With+                  (. Sexp.list (Sexp.el (Sexp.sym "bespoke") >>> Sexp.el sexpIso >>> Sexp.el (iso fromIntegral fromIntegral . Sexp.int)))+                  End+ instance Pretty SizeClass where   ppr (SizeThreshold path _) = text $ "threshold (" ++ unwords (map pStep path) ++ ")"-    where pStep (v, True) = pretty v-          pStep (v, False) = '!' : pretty v+    where+      pStep (v, True) = pretty v+      pStep (v, False) = '!' : pretty v   ppr SizeGroup = text "group_size"   ppr SizeNumGroups = text "num_groups"   ppr SizeTile = text "tile_size"@@ -49,14 +72,17 @@   ppr (SizeBespoke k _) = ppr k  -- | The default value for the size.  If 'Nothing', that means the backend gets to decide.-sizeDefault :: SizeClass -> Maybe Int32+sizeDefault :: SizeClass -> Maybe Int64 sizeDefault (SizeThreshold _ x) = x sizeDefault (SizeBespoke _ x) = Just x sizeDefault _ = Nothing  -- | A wrapper supporting a phantom type for indicating what we are counting.-newtype Count u e = Count { unCount :: e }-  deriving (Eq, Ord, Show, Num, IntegralExp, FreeIn, Pretty, Substitute)+newtype Count u e = Count {unCount :: e}+  deriving (Eq, Ord, Show, Num, IntegralExp, FreeIn, Pretty, Substitute, Generic)++instance SexpIso e => SexpIso (Count u e) where+  sexpIso = with $ \count -> sexpIso >>> count  instance Functor (Count u) where   fmap = fmapDefault
src/Futhark/IR/KernelsMem.hs view
@@ -1,47 +1,44 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE TypeFamilies #-}+ module Futhark.IR.KernelsMem-  ( KernelsMem+  ( KernelsMem, -  -- * Simplification-  , simplifyProg-  , simplifyStms-  , simpleKernelsMem+    -- * Simplification+    simplifyProg,+    simplifyStms,+    simpleKernelsMem,      -- * Module re-exports-  , module Futhark.IR.Mem-  , module Futhark.IR.Kernels.Kernel+    module Futhark.IR.Mem,+    module Futhark.IR.Kernels.Kernel,   )-  where+where  import Futhark.Analysis.PrimExp.Convert import qualified Futhark.Analysis.UsageTable as UT-import Futhark.MonadFreshNames-import Futhark.Pass-import Futhark.IR.Syntax-import Futhark.IR.Prop-import Futhark.IR.Traversals-import Futhark.IR.Pretty import Futhark.IR.Kernels.Kernel import Futhark.IR.Kernels.Simplify (simplifyKernelOp)-import qualified Futhark.TypeCheck as TC import Futhark.IR.Mem import Futhark.IR.Mem.Simplify-import Futhark.Pass.ExplicitAllocations (BinderOps(..), mkLetNamesB', mkLetNamesB'')+import Futhark.MonadFreshNames import qualified Futhark.Optimise.Simplify.Engine as Engine+import Futhark.Pass+import Futhark.Pass.ExplicitAllocations (BinderOps (..), mkLetNamesB', mkLetNamesB'')+import qualified Futhark.TypeCheck as TC  data KernelsMem  instance Decorations KernelsMem where-  type LetDec     KernelsMem = LetDecMem+  type LetDec KernelsMem = LetDecMem   type FParamInfo KernelsMem = FParamMem   type LParamInfo KernelsMem = LParamMem-  type RetType    KernelsMem = RetTypeMem+  type RetType KernelsMem = RetTypeMem   type BranchType KernelsMem = BranchTypeMem-  type Op         KernelsMem = MemOp (HostOp KernelsMem ())+  type Op KernelsMem = MemOp (HostOp KernelsMem ())  instance ASTLore KernelsMem where   expTypesFromPattern = return . map snd . snd . bodyReturnsFromPattern@@ -52,14 +49,15 @@   opReturns (Inner (SegOp op)) = segOpReturns op   opReturns k = extReturns <$> opType k -instance PrettyLore KernelsMem where+instance PrettyLore KernelsMem  instance TC.CheckableOp KernelsMem where   checkOp = typeCheckMemoryOp Nothing-    where typeCheckMemoryOp _ (Alloc size _) =-            TC.require [Prim int64] size-          typeCheckMemoryOp lvl (Inner op) =-            typeCheckHostOp (typeCheckMemoryOp . Just) lvl (const $ return ()) op+    where+      typeCheckMemoryOp _ (Alloc size _) =+        TC.require [Prim int64] size+      typeCheckMemoryOp lvl (Inner op) =+        typeCheckHostOp (typeCheckMemoryOp . Just) lvl (const $ return ()) op  instance TC.Checkable KernelsMem where   checkFParamLore = checkMemInfo@@ -85,10 +83,13 @@ simplifyProg :: Prog KernelsMem -> PassM (Prog KernelsMem) simplifyProg = simplifyProgGeneric simpleKernelsMem -simplifyStms :: (HasScope KernelsMem m, MonadFreshNames m) =>-                 Stms KernelsMem-             -> m (Engine.SymbolTable (Engine.Wise KernelsMem),-                   Stms KernelsMem)+simplifyStms ::+  (HasScope KernelsMem m, MonadFreshNames m) =>+  Stms KernelsMem ->+  m+    ( Engine.SymbolTable (Engine.Wise KernelsMem),+      Stms KernelsMem+    ) simplifyStms = simplifyStmsGeneric simpleKernelsMem  simpleKernelsMem :: Engine.SimpleOps KernelsMem@@ -100,7 +101,7 @@     -- usages for those sizes.  This is necessary so the simplifier     -- will hoist those sizes out as far as possible (most     -- importantly, past the versioning If).-    usage (SegOp (SegMap SegGroup{} _ _ kbody)) = localAllocs kbody+    usage (SegOp (SegMap SegGroup {} _ _ kbody)) = localAllocs kbody     usage _ = mempty     localAllocs = foldMap stmLocalAlloc . kernelBodyStms     stmLocalAlloc = expLocalAlloc . stmExp
src/Futhark/IR/Mem.hs view
@@ -1,1003 +1,1230 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ConstraintKinds #-}--- | Building blocks for defining representations where every array--- is given information about which memory block is it based in, and--- how array elements map to memory block offsets.------ There are two primary concepts you will need to understand:------  1. Memory blocks, which are Futhark values of type v'Mem'---     (parametrized with their size).  These correspond to arbitrary---     blocks of memory, and are created using the 'Alloc' operation.------  2. Index functions, which describe a mapping from the index space---     of an array (eg. a two-dimensional space for an array of type---     @[[int]]@) to a one-dimensional offset into a memory block.---     Thus, index functions describe how arbitrary-dimensional arrays---     are mapped to the single-dimensional world of memory.------ At a conceptual level, imagine that we have a two-dimensional array--- @a@ of 32-bit integers, consisting of @n@ rows of @m@ elements--- each.  This array could be represented in classic row-major format--- with an index function like the following:------ @---   f(i,j) = i * m + j--- @------ When we want to know the location of element @a[2,3]@, we simply--- call the index function as @f(2,3)@ and obtain @2*m+3@.  We could--- also have chosen another index function, one that represents the--- array in column-major (or "transposed") format:------ @---   f(i,j) = j * n + i--- @------ Index functions are not Futhark-level functions, but a special--- construct that the final code generator will eventually use to--- generate concrete access code.  By modifying the index functions we--- can change how an array is represented in memory, which can permit--- memory access pattern optimisations.------ Every time we bind an array, whether in a @let@-binding, @loop@--- merge parameter, or @lambda@ parameter, we have an annotation--- specifying a memory block and an index function.  In some cases,--- such as @let@-bindings for many expressions, we are free to specify--- an arbitrary index function and memory block - for example, we get--- to decide where 'Copy' stores its result - but in other cases the--- type rules of the expression chooses for us.  For example, 'Index'--- always produces an array in the same memory block as its input, and--- with the same index function, except with some indices fixed.-module Futhark.IR.Mem-       ( LetDecMem-       , FParamMem-       , LParamMem-       , RetTypeMem-       , BranchTypeMem--       , MemOp (..)-       , MemInfo (..)-       , MemBound-       , MemBind (..)-       , MemReturn (..)-       , IxFun-       , ExtIxFun-       , isStaticIxFun-       , ExpReturns-       , BodyReturns-       , FunReturns-       , noUniquenessReturns-       , bodyReturnsToExpReturns-       , Mem-       , AllocOp(..)-       , OpReturns(..)-       , varReturns-       , expReturns-       , extReturns-       , lookupMemInfo-       , subExpMemInfo-       , lookupArraySummary-       , existentialiseIxFun--       -- * Type checking parts-       , matchBranchReturnType-       , matchPatternToExp-       , matchFunctionReturnType-       , matchLoopResultMem-       , bodyReturnsFromPattern-       , checkMemInfo--       -- * Module re-exports-       , module Futhark.IR.Prop-       , module Futhark.IR.Traversals-       , module Futhark.IR.Pretty-       , module Futhark.IR.Syntax-       , module Futhark.Analysis.PrimExp.Convert-       )-where--import Data.Maybe-import Control.Monad.State-import Control.Monad.Reader-import Control.Monad.Except-import qualified Data.Map.Strict as M-import Data.Foldable (traverse_, toList)-import Data.List (elemIndex, find)-import qualified Data.Set as S--import Futhark.Analysis.Metrics-import Futhark.IR.Syntax-import Futhark.IR.Prop-import Futhark.IR.Prop.Aliases-import Futhark.IR.Traversals-import Futhark.IR.Pretty-import Futhark.Transform.Rename-import Futhark.Transform.Substitute-import qualified Futhark.TypeCheck as TC-import qualified Futhark.IR.Mem.IxFun as IxFun-import Futhark.Analysis.PrimExp.Convert-import Futhark.Analysis.PrimExp.Simplify-import Futhark.Util-import qualified Futhark.Util.Pretty as PP-import qualified Futhark.Optimise.Simplify.Engine as Engine-import Futhark.Optimise.Simplify.Lore-import Futhark.IR.Aliases-  (Aliases, removeScopeAliases, removeExpAliases, removePatternAliases)-import qualified Futhark.Analysis.SymbolTable as ST--type LetDecMem = MemInfo SubExp NoUniqueness MemBind-type FParamMem = MemInfo SubExp Uniqueness MemBind-type LParamMem = MemInfo SubExp NoUniqueness MemBind-type RetTypeMem = FunReturns-type BranchTypeMem = BodyReturns---- | The class of ops that have memory allocation.-class AllocOp op where-  allocOp :: SubExp -> Space -> op--type Mem lore = (AllocOp (Op lore),-                              FParamInfo lore ~ FParamMem,-                              LParamInfo lore ~ LParamMem,-                              LetDec lore ~ LetDecMem,-                              RetType lore ~ RetTypeMem,-                              BranchType lore ~ BranchTypeMem,-                              CanBeAliased (Op lore),-                              ASTLore lore, Decorations lore,-                              TC.Checkable lore,-                              OpReturns lore)--instance IsRetType FunReturns where-  primRetType = MemPrim-  applyRetType = applyFunReturns--instance IsBodyType BodyReturns where-  primBodyType = MemPrim--data MemOp inner = Alloc SubExp Space-                   -- ^ Allocate a memory block.  This really should not be an-                   -- expression, but what are you gonna do...-                 | Inner inner-            deriving (Eq, Ord, Show)--instance AllocOp (MemOp inner) where-  allocOp = Alloc--instance FreeIn inner => FreeIn (MemOp inner) where-  freeIn' (Alloc size _) = freeIn' size-  freeIn' (Inner k) = freeIn' k--instance TypedOp inner => TypedOp (MemOp inner) where-  opType (Alloc _ space) = pure [Mem space]-  opType (Inner k) = opType k--instance AliasedOp inner => AliasedOp (MemOp inner) where-  opAliases Alloc{} = [mempty]-  opAliases (Inner k) = opAliases k--  consumedInOp Alloc{} = mempty-  consumedInOp (Inner k) = consumedInOp k--instance CanBeAliased inner => CanBeAliased (MemOp inner) where-  type OpWithAliases (MemOp inner) = MemOp (OpWithAliases inner)-  removeOpAliases (Alloc se space) = Alloc se space-  removeOpAliases (Inner k) = Inner $ removeOpAliases k--  addOpAliases (Alloc se space) = Alloc se space-  addOpAliases (Inner k) = Inner $ addOpAliases k--instance Rename inner => Rename (MemOp inner) where-  rename (Alloc size space) = Alloc <$> rename size <*> pure space-  rename (Inner k) = Inner <$> rename k--instance Substitute inner => Substitute (MemOp inner) where-  substituteNames subst (Alloc size space) = Alloc (substituteNames subst size) space-  substituteNames subst (Inner k) = Inner $ substituteNames subst k--instance PP.Pretty inner => PP.Pretty (MemOp inner) where-  ppr (Alloc e DefaultSpace) = PP.text "alloc" <> PP.apply [PP.ppr e]-  ppr (Alloc e s) = PP.text "alloc" <> PP.apply [PP.ppr e, PP.ppr s]-  ppr (Inner k) = PP.ppr k--instance OpMetrics inner => OpMetrics (MemOp inner) where-  opMetrics Alloc{} = seen "Alloc"-  opMetrics (Inner k) = opMetrics k--instance IsOp inner => IsOp (MemOp inner) where-  safeOp Alloc{} = False-  safeOp (Inner k) = safeOp k-  cheapOp (Inner k) = cheapOp k-  cheapOp Alloc{} = True--instance CanBeWise inner => CanBeWise (MemOp inner) where-  type OpWithWisdom (MemOp inner) = MemOp (OpWithWisdom inner)-  removeOpWisdom (Alloc size space) = Alloc size space-  removeOpWisdom (Inner k) = Inner $ removeOpWisdom k--instance ST.IndexOp inner => ST.IndexOp (MemOp inner) where-  indexOp vtable k (Inner op) is = ST.indexOp vtable k op is-  indexOp _ _ _ _ = Nothing---- | The index function representation used for memory annotations.-type IxFun = IxFun.IxFun (PrimExp VName)---- | An index function that may contain existential variables.-type ExtIxFun = IxFun.IxFun (PrimExp (Ext VName))---- | A summary of the memory information for every let-bound--- identifier, function parameter, and return value.  Parameterisered--- over uniqueness, dimension, and auxiliary array information.-data MemInfo d u ret = MemPrim PrimType-                     -- ^ A primitive value.-                     | MemMem Space-                     -- ^ A memory block.-                     | MemArray PrimType (ShapeBase d) u ret-                     -- ^ The array is stored in the named memory block,-                     -- and with the given index function.  The index-                     -- function maps indices in the array to /element/-                     -- offset, /not/ byte offsets!  To translate to byte-                     -- offsets, multiply the offset with the size of the-                     -- array element type.-                     deriving (Eq, Show, Ord) --- XXX Ord?--type MemBound u = MemInfo SubExp u MemBind--instance FixExt ret => DeclExtTyped (MemInfo ExtSize Uniqueness ret) where-  declExtTypeOf (MemPrim pt) = Prim pt-  declExtTypeOf (MemMem space) = Mem space-  declExtTypeOf (MemArray pt shape u _) = Array pt shape u--instance FixExt ret => ExtTyped (MemInfo ExtSize NoUniqueness ret) where-  extTypeOf (MemPrim pt) = Prim pt-  extTypeOf (MemMem space) = Mem space-  extTypeOf (MemArray pt shape u _) = Array pt shape u--instance FixExt ret => FixExt (MemInfo ExtSize u ret) where-  fixExt _ _ (MemPrim pt) = MemPrim pt-  fixExt _ _ (MemMem space) = MemMem space-  fixExt i se (MemArray pt shape u ret) =-    MemArray pt (fixExt i se shape) u (fixExt i se ret)--instance Typed (MemInfo SubExp Uniqueness ret) where-  typeOf = fromDecl . declTypeOf--instance Typed (MemInfo SubExp NoUniqueness ret) where-  typeOf (MemPrim pt) = Prim pt-  typeOf (MemMem space) = Mem space-  typeOf (MemArray bt shape u _) = Array bt shape u--instance DeclTyped (MemInfo SubExp Uniqueness ret) where-  declTypeOf (MemPrim bt) = Prim bt-  declTypeOf (MemMem space) = Mem space-  declTypeOf (MemArray bt shape u _) = Array bt shape u--instance (FreeIn d, FreeIn ret) => FreeIn (MemInfo d u ret) where-  freeIn' (MemArray _ shape _ ret) = freeIn' shape <> freeIn' ret-  freeIn' (MemMem s) = freeIn' s-  freeIn' MemPrim{} = mempty--instance (Substitute d, Substitute ret) => Substitute (MemInfo d u ret) where-  substituteNames subst (MemArray bt shape u ret) =-    MemArray bt-    (substituteNames subst shape) u-    (substituteNames subst ret)-  substituteNames _ (MemMem space) =-    MemMem space-  substituteNames _ (MemPrim bt) =-    MemPrim bt--instance (Substitute d, Substitute ret) => Rename (MemInfo d u ret) where-  rename = substituteRename--simplifyIxFun :: Engine.SimplifiableLore lore =>-                 IxFun -> Engine.SimpleM lore IxFun-simplifyIxFun = traverse simplifyPrimExp--simplifyExtIxFun :: Engine.SimplifiableLore lore =>-                    ExtIxFun -> Engine.SimpleM lore ExtIxFun-simplifyExtIxFun = traverse simplifyExtPrimExp--isStaticIxFun :: ExtIxFun -> Maybe IxFun-isStaticIxFun = traverse $ traverse inst-  where inst Ext{} = Nothing-        inst (Free x) = Just x--instance (Engine.Simplifiable d, Engine.Simplifiable ret) =>-         Engine.Simplifiable (MemInfo d u ret) where-  simplify (MemPrim bt) =-    return $ MemPrim bt-  simplify (MemMem space) =-    pure $ MemMem space-  simplify (MemArray bt shape u ret) =-    MemArray bt <$> Engine.simplify shape <*> pure u <*> Engine.simplify ret--instance (PP.Pretty (TypeBase (ShapeBase d) u),-          PP.Pretty d, PP.Pretty u, PP.Pretty ret) => PP.Pretty (MemInfo d u ret) where-  ppr (MemPrim bt) = PP.ppr bt-  ppr (MemMem DefaultSpace) = PP.text "mem"-  ppr (MemMem s) = PP.text "mem" <> PP.ppr s-  ppr (MemArray bt shape u ret) =-    PP.ppr (Array bt shape u) <> PP.text "@" <> PP.ppr ret--instance PP.Pretty (Param (MemInfo SubExp Uniqueness ret)) where-  ppr = PP.ppr . fmap declTypeOf--instance PP.Pretty (Param (MemInfo SubExp NoUniqueness ret)) where-  ppr = PP.ppr . fmap typeOf--instance PP.Pretty (PatElemT (MemInfo SubExp NoUniqueness ret)) where-  ppr = PP.ppr . fmap typeOf---- | Memory information for an array bound somewhere in the program.-data MemBind = ArrayIn VName IxFun-             -- ^ Located in this memory block with this index-             -- function.-             deriving (Show)--instance Eq MemBind where-  _ == _ = True--instance Ord MemBind where-  _ `compare` _ = EQ--instance Rename MemBind where-  rename = substituteRename--instance Substitute MemBind where-  substituteNames substs (ArrayIn ident ixfun) =-    ArrayIn (substituteNames substs ident) (substituteNames substs ixfun)--instance PP.Pretty MemBind where-  ppr (ArrayIn mem ixfun) =-    PP.text "@" <> PP.ppr mem <> PP.text "->" PP.</> PP.ppr ixfun--instance FreeIn MemBind where-  freeIn' (ArrayIn mem ixfun) = freeIn' mem <> freeIn' ixfun---- | A description of the memory properties of an array being returned--- by an operation.-data MemReturn = ReturnsInBlock VName ExtIxFun-                 -- ^ The array is located in a memory block that is-                 -- already in scope.-               | ReturnsNewBlock Space Int ExtIxFun-                 -- ^ The operation returns a new (existential) memory-                 -- block.-               deriving (Show)--instance Eq MemReturn where-  _ == _ = True--instance Ord MemReturn where-  _ `compare` _ = EQ--instance Rename MemReturn where-  rename = substituteRename--instance Substitute MemReturn where-  substituteNames substs (ReturnsInBlock ident ixfun) =-    ReturnsInBlock (substituteNames substs ident) (substituteNames substs ixfun)-  substituteNames substs (ReturnsNewBlock space i ixfun) =-    ReturnsNewBlock space i (substituteNames substs ixfun)--instance FixExt MemReturn where-  fixExt i (Var v) (ReturnsNewBlock _ j ixfun)-    | j == i = ReturnsInBlock v $ fixExtIxFun i-               (primExpFromSubExp int32 (Var v)) ixfun-  fixExt i se (ReturnsNewBlock space j ixfun) =-    ReturnsNewBlock space j'-    (fixExtIxFun i (primExpFromSubExp int32 se) ixfun)-    where j' | i < j     = j-1-             | otherwise = j-  fixExt i se (ReturnsInBlock mem ixfun) =-    ReturnsInBlock mem (fixExtIxFun i (primExpFromSubExp int32 se) ixfun)--fixExtIxFun :: Int -> PrimExp VName -> ExtIxFun -> ExtIxFun-fixExtIxFun i e = fmap $ replaceInPrimExp update-  where update (Ext j) t | j > i     = LeafExp (Ext $ j - 1) t-                         | j == i    = fmap Free e-                         | otherwise = LeafExp (Ext j) t-        update (Free x) t = LeafExp (Free x) t--leafExp :: Int -> PrimExp (Ext a)-leafExp i = LeafExp (Ext i) int32--existentialiseIxFun :: [VName] -> IxFun -> ExtIxFun-existentialiseIxFun ctx = IxFun.substituteInIxFun ctx' . fmap (fmap Free)-  where ctx' = M.map leafExp $ M.fromList $ zip (map Free ctx) [0..]--instance PP.Pretty MemReturn where-  ppr (ReturnsInBlock v ixfun) =-    PP.parens $ PP.text (pretty v) <> PP.text "->" PP.</> PP.ppr ixfun-  ppr (ReturnsNewBlock space i ixfun) =-    PP.text ("?" ++ show i) <> PP.ppr space <> PP.text "->" PP.</> PP.ppr ixfun--instance FreeIn MemReturn where-  freeIn' (ReturnsInBlock v ixfun) = freeIn' v <> freeIn' ixfun-  freeIn' (ReturnsNewBlock space _ ixfun) = freeIn' space <> freeIn' ixfun--instance Engine.Simplifiable MemReturn where-  simplify (ReturnsNewBlock space i ixfun) =-    ReturnsNewBlock space i <$> simplifyExtIxFun ixfun-  simplify (ReturnsInBlock v ixfun) =-    ReturnsInBlock <$> Engine.simplify v <*> simplifyExtIxFun ixfun---instance Engine.Simplifiable MemBind where-  simplify (ArrayIn mem ixfun) =-    ArrayIn <$> Engine.simplify mem <*> simplifyIxFun ixfun--instance Engine.Simplifiable [FunReturns] where-  simplify = mapM Engine.simplify---- | The memory return of an expression.  An array is annotated with--- @Maybe MemReturn@, which can be interpreted as the expression--- either dictating exactly where the array is located when it is--- returned (if 'Just'), or able to put it whereever the binding--- prefers (if 'Nothing').------ This is necessary to capture the difference between an expression--- that is just an array-typed variable, in which the array being--- "returned" is located where it already is, and a @copy@ expression,--- whose entire purpose is to store an existing array in some--- arbitrary location.  This is a consequence of the design decision--- never to have implicit memory copies.-type ExpReturns = MemInfo ExtSize NoUniqueness (Maybe MemReturn)---- | The return of a body, which must always indicate where--- returned arrays are located.-type BodyReturns = MemInfo ExtSize NoUniqueness MemReturn---- | The memory return of a function, which must always indicate where--- returned arrays are located.-type FunReturns = MemInfo ExtSize Uniqueness MemReturn--maybeReturns :: MemInfo d u r -> MemInfo d u (Maybe r)-maybeReturns (MemArray bt shape u ret) =-  MemArray bt shape u $ Just ret-maybeReturns (MemPrim bt) =-  MemPrim bt-maybeReturns (MemMem space) =-  MemMem space--noUniquenessReturns :: MemInfo d u r -> MemInfo d NoUniqueness r-noUniquenessReturns (MemArray bt shape _ r) =-  MemArray bt shape NoUniqueness r-noUniquenessReturns (MemPrim bt) =-  MemPrim bt-noUniquenessReturns (MemMem space) =-  MemMem space--funReturnsToExpReturns :: FunReturns -> ExpReturns-funReturnsToExpReturns = noUniquenessReturns . maybeReturns--bodyReturnsToExpReturns :: BodyReturns -> ExpReturns-bodyReturnsToExpReturns = noUniquenessReturns . maybeReturns--matchRetTypeToResult :: Mem lore =>-                        [FunReturns] -> Result -> TC.TypeM lore ()-matchRetTypeToResult rettype result = do-  scope <- askScope-  result_ts <- runReaderT (mapM subExpMemInfo result) $ removeScopeAliases scope-  matchReturnType rettype result result_ts--matchFunctionReturnType :: Mem lore =>-                           [FunReturns] -> Result -> TC.TypeM lore ()-matchFunctionReturnType rettype result = do-  matchRetTypeToResult rettype result-  mapM_ checkResultSubExp result-  where checkResultSubExp Constant{} =-          return ()-        checkResultSubExp (Var v) = do-          dec <- varMemInfo v-          case dec of-            MemPrim _ -> return ()-            MemMem{} -> return ()-            MemArray _ _ _ (ArrayIn _ ixfun)-              | IxFun.isLinear ixfun ->-                return ()-              | otherwise ->-                  TC.bad $ TC.TypeError $-                  "Array " ++ pretty v ++-                  " returned by function, but has nontrivial index function " ++-                  pretty ixfun--matchLoopResultMem :: Mem lore =>-                      [FParam (Aliases lore)] -> [FParam (Aliases lore)]-                   -> [SubExp] -> TC.TypeM lore ()-matchLoopResultMem ctx val = matchRetTypeToResult rettype-  where ctx_names = map paramName ctx--        -- Invent a ReturnType so we can pretend that the loop body is-        -- actually returning from a function.-        rettype = map (toRet . paramDec) val--        toExtV v-          | Just i <- v `elemIndex` ctx_names = Ext i-          | otherwise                         = Free v--        toExtSE (Var v) = Var <$> toExtV v-        toExtSE (Constant v) = Free $ Constant v--        toRet (MemPrim t) =-          MemPrim t-        toRet (MemMem space) =-          MemMem space-        toRet (MemArray pt shape u (ArrayIn mem ixfun))-          | Just i <- mem `elemIndex` ctx_names,-            Param _ (MemMem space) : _ <- drop i ctx =-              MemArray pt shape' u $ ReturnsNewBlock space i ixfun'-          | otherwise =-              MemArray pt shape' u $ ReturnsInBlock mem ixfun'-          where shape' = fmap toExtSE shape-                ixfun' = existentialiseIxFun ctx_names ixfun--matchBranchReturnType :: Mem lore =>-                         [BodyReturns]-                      -> Body (Aliases lore)-                      -> TC.TypeM lore ()-matchBranchReturnType rettype (Body _ stms res) = do-  scope <- askScope-  ts <- runReaderT (mapM subExpMemInfo res) $ removeScopeAliases (scope <> scopeOf stms)-  matchReturnType rettype res ts---- | Helper function for index function unification.------ The first return value maps a VName (wrapped in 'Free') to its Int--- (wrapped in 'Ext').  In case of duplicates, it is mapped to the--- *first* Int that occurs.------ The second return value maps each Int (wrapped in an 'Ext') to a--- 'LeafExp' 'Ext' with the Int at which its associated VName first--- occurs.-getExtMaps :: [(VName,Int)] -> (M.Map (Ext VName) (PrimExp (Ext VName)),-                                M.Map (Ext VName) (PrimExp (Ext VName)))-getExtMaps ctx_lst_ids =-  (M.map leafExp $ M.mapKeys Free $ M.fromListWith (flip const) ctx_lst_ids,-   M.fromList $-   mapMaybe (traverse (fmap (\i -> LeafExp (Ext i) int32) .-                       (`lookup` ctx_lst_ids)) .-             uncurry (flip (,)) . fmap Ext) ctx_lst_ids)--matchReturnType :: PP.Pretty u =>-                   [MemInfo ExtSize u MemReturn]-                -> [SubExp]-                -> [MemInfo SubExp NoUniqueness MemBind]-                -> TC.TypeM lore ()-matchReturnType rettype res ts = do-  let (ctx_ts, val_ts) = splitFromEnd (length rettype) ts-      (ctx_res, _val_res) = splitFromEnd (length rettype) res--      existentialiseIxFun0 :: IxFun -> ExtIxFun-      existentialiseIxFun0 = fmap $ fmap Free--      fetchCtx i = case maybeNth i $ zip ctx_res ctx_ts of-                     Nothing -> throwError $ "Cannot find context variable " ++-                                show i ++ " in context results: " ++ pretty ctx_res-                     Just (se, t) -> return (se, t)--      checkReturn (MemPrim x) (MemPrim y)-        | x == y = return ()-      checkReturn (MemMem x) (MemMem y)-        | x == y = return ()-      checkReturn (MemArray x_pt x_shape _ x_ret)-                  (MemArray y_pt y_shape _ y_ret)-        | x_pt == y_pt, shapeRank x_shape == shapeRank y_shape = do-            zipWithM_ checkDim (shapeDims x_shape) (shapeDims y_shape)-            checkMemReturn x_ret y_ret-      checkReturn x y =-        throwError $ unwords ["Expected ", pretty x, " but got ", pretty y]--      checkDim (Free x) y-        | x == y = return ()-        | otherwise = throwError $ unwords ["Expected dim", pretty x,-                                            "but got", pretty y]-      checkDim (Ext i) y = do-        (x, _) <- fetchCtx i-        unless (x == y) $-          throwError $ unwords ["Expected ext dim", pretty i, "=>", pretty x,-                                "but got", pretty y]--      extsInMemInfo :: MemInfo ExtSize u MemReturn -> S.Set Int-      extsInMemInfo (MemArray _ shp _ ret) =-        extInShape shp <> extInMemReturn ret-      extsInMemInfo _ = S.empty--      checkMemReturn (ReturnsInBlock x_mem x_ixfun) (ArrayIn y_mem y_ixfun)-          | x_mem == y_mem =-              unless (IxFun.closeEnough x_ixfun $ existentialiseIxFun0 y_ixfun) $-                throwError $ unwords  ["Index function unification failed (ReturnsInBlock)",-                    "\nixfun of body result: ", pretty y_ixfun,-                    "\nixfun of return type: ", pretty x_ixfun,-                    "\nand context elements: ", pretty ctx_res]-      checkMemReturn (ReturnsNewBlock x_space x_ext x_ixfun)-                     (ArrayIn y_mem y_ixfun) = do-        (x_mem, x_mem_type)  <- fetchCtx x_ext-        unless (IxFun.closeEnough x_ixfun $ existentialiseIxFun0 y_ixfun) $-          throwError $ unwords  ["Index function unification failed (ReturnsNewBlock)",-            "\nixfun of body result: ", pretty y_ixfun,-            "\nixfun of return type: ", pretty x_ixfun,-            "\nand context elements: ", pretty ctx_res]-        case x_mem_type of-          MemMem y_space ->-            unless (x_space == y_space) $-              throwError $ unwords ["Expected memory", pretty y_mem, "in space", pretty x_space,-                                    "but actually in space", pretty y_space]-          t ->-            throwError $ unwords ["Expected memory", pretty x_ext, "=>", pretty x_mem,-                                  "but but has type", pretty t]-      checkMemReturn x y =-        throwError $ unwords ["Expected array in", pretty x,-                              "but array returned in", pretty y]--      bad :: String -> TC.TypeM lore a-      bad s = TC.bad $ TC.TypeError $ PP.pretty $-              "Return type" PP.</>-              PP.indent 2 (ppTuple' rettype) PP.</>-              "cannot match returns of results" PP.</>-              PP.indent 2 (ppTuple' ts) PP.</>-              PP.text s--  unless (length (S.unions $ map extsInMemInfo rettype)  == length ctx_res) $-    TC.bad $ TC.TypeError $ "Too many context parameters for the number of " ++-    "existentials in the return type! type:\n  " ++-    prettyTuple rettype ++-    "\ncannot match context parameters:\n  " ++ prettyTuple ctx_res---  either bad return =<< runExceptT (zipWithM_ checkReturn rettype val_ts)--matchPatternToExp :: (Mem lore) =>-                     Pattern (Aliases lore)-                  -> Exp (Aliases lore)-                  -> TC.TypeM lore ()-matchPatternToExp pat e = do-  scope <- asksScope removeScopeAliases-  rt <- runReaderT (expReturns $ removeExpAliases e) scope--  let (ctxs, vals) = bodyReturnsFromPattern $ removePatternAliases pat-      (ctx_ids, _ctx_ts) = unzip ctxs-      (_val_ids, val_ts) = unzip vals-      (ctx_map_ids, ctx_map_exts) =-        getExtMaps $ zip ctx_ids [0..length ctx_ids - 1]--  let rt_exts = foldMap extInExpReturns rt--  unless (length val_ts == length rt &&-          and (zipWith (matches ctx_map_ids ctx_map_exts) val_ts rt) &&-          M.keysSet ctx_map_exts `S.isSubsetOf` S.map Ext rt_exts) $-    TC.bad $ TC.TypeError $ "Expression type:\n  " ++ prettyTuple rt ++-                            "\ncannot match pattern type:\n  " ++ prettyTuple val_ts ++-                            "\nwith context elements: " ++ pretty ctx_ids-  where matches _ _ (MemPrim x) (MemPrim y) = x == y-        matches _ _ (MemMem x_space) (MemMem y_space) =-          x_space == y_space-        matches ctxids ctxexts (MemArray x_pt x_shape _ x_ret) (MemArray y_pt y_shape _ y_ret) =-          x_pt == y_pt && x_shape == y_shape &&-          case (x_ret, y_ret) of-            (ReturnsInBlock _ x_ixfun, Just (ReturnsInBlock _ y_ixfun)) ->-              let x_ixfun' = IxFun.substituteInIxFun ctxids  x_ixfun-                  y_ixfun' = IxFun.substituteInIxFun ctxexts y_ixfun-              in  IxFun.closeEnough x_ixfun' y_ixfun'-            (ReturnsInBlock _ x_ixfun,-             Just (ReturnsNewBlock _ _ y_ixfun)) ->-              let x_ixfun' = IxFun.substituteInIxFun ctxids  x_ixfun-                  y_ixfun' = IxFun.substituteInIxFun ctxexts y_ixfun-              in  IxFun.closeEnough x_ixfun' y_ixfun'-            (ReturnsNewBlock _ x_i x_ixfun,-             Just (ReturnsNewBlock _ y_i y_ixfun)) ->-              let x_ixfun' = IxFun.substituteInIxFun  ctxids x_ixfun-                  y_ixfun' = IxFun.substituteInIxFun ctxexts y_ixfun-              in  x_i == y_i && IxFun.closeEnough x_ixfun' y_ixfun'-            (_, Nothing) -> True-            _ -> False-        matches _ _ _ _ = False--        extInExpReturns :: ExpReturns -> S.Set Int-        extInExpReturns (MemArray _ shape _ mem_return) =-          extInShape shape <> maybe S.empty extInMemReturn mem_return-        extInExpReturns _ = mempty---extInShape :: ShapeBase (Ext SubExp) -> S.Set Int-extInShape shape = S.fromList $ mapMaybe isExt $ shapeDims shape--extInMemReturn :: MemReturn -> S.Set Int-extInMemReturn (ReturnsInBlock _ extixfn) = extInIxFn extixfn-extInMemReturn (ReturnsNewBlock _ i extixfn) =-  S.singleton i <> extInIxFn extixfn--extInIxFn :: ExtIxFun -> S.Set Int-extInIxFn ixfun = S.fromList $ concatMap (mapMaybe isExt . toList) ixfun--varMemInfo :: Mem lore =>-              VName -> TC.TypeM lore (MemInfo SubExp NoUniqueness MemBind)-varMemInfo name = do-  dec <- TC.lookupVar name--  case dec of-    LetName (_, summary) -> return summary-    FParamName summary -> return $ noUniquenessReturns summary-    LParamName summary -> return summary-    IndexName it -> return $ MemPrim $ IntType it--nameInfoToMemInfo :: Mem lore => NameInfo lore -> MemBound NoUniqueness-nameInfoToMemInfo info =-  case info of-    FParamName summary -> noUniquenessReturns summary-    LParamName summary -> summary-    LetName summary -> summary-    IndexName it -> MemPrim $ IntType it--lookupMemInfo :: (HasScope lore m, Mem lore) =>-                  VName -> m (MemInfo SubExp NoUniqueness MemBind)-lookupMemInfo = fmap nameInfoToMemInfo . lookupInfo--subExpMemInfo :: (HasScope lore m, Monad m, Mem lore) =>-                 SubExp -> m (MemInfo SubExp NoUniqueness MemBind)-subExpMemInfo (Var v) = lookupMemInfo v-subExpMemInfo (Constant v) = return $ MemPrim $ primValueType v--lookupArraySummary :: (Mem lore, HasScope lore m, Monad m) =>-                      VName -> m (VName, IxFun.IxFun (PrimExp VName))-lookupArraySummary name = do-  summary <- lookupMemInfo name-  case summary of-    MemArray _ _ _ (ArrayIn mem ixfun) ->-      return (mem, ixfun)-    _ ->-      error $ "Variable " ++ pretty name ++ " does not look like an array."--checkMemInfo :: TC.Checkable lore =>-                 VName -> MemInfo SubExp u MemBind-             -> TC.TypeM lore ()-checkMemInfo _ (MemPrim _) = return ()-checkMemInfo _ (MemMem (ScalarSpace d _)) = mapM_ (TC.require [Prim int32]) d-checkMemInfo _ (MemMem _) = return ()-checkMemInfo name (MemArray _ shape _ (ArrayIn v ixfun)) = do-  t <- lookupType v-  case t of-    Mem{} ->-      return ()-    _        ->-      TC.bad $ TC.TypeError $-      "Variable " ++ pretty v ++-      " used as memory block, but is of type " ++-      pretty t ++ "."--  TC.context ("in index function " ++ pretty ixfun) $ do-    traverse_ (TC.requirePrimExp int32) ixfun-    let ixfun_rank = IxFun.rank ixfun-        ident_rank = shapeRank shape-    unless (ixfun_rank == ident_rank) $-      TC.bad $ TC.TypeError $-      "Arity of index function (" ++ pretty ixfun_rank ++-      ") does not match rank of array " ++ pretty name ++-      " (" ++ show ident_rank ++ ")"--bodyReturnsFromPattern :: PatternT (MemBound NoUniqueness)-                       -> ([(VName,BodyReturns)], [(VName,BodyReturns)])-bodyReturnsFromPattern pat =-  (map asReturns $ patternContextElements pat,-   map asReturns $ patternValueElements pat)-  where ctx = patternContextElements pat--        ext (Var v)-          | Just (i, _) <- find ((==v) . patElemName . snd) $ zip [0..] ctx =-              Ext i-        ext se = Free se--        asReturns pe =-         (patElemName pe,-          case patElemDec pe of-            MemPrim pt -> MemPrim pt-            MemMem space -> MemMem space-            MemArray pt shape u (ArrayIn mem ixfun) ->-              MemArray pt (Shape $ map ext $ shapeDims shape) u $-              case find ((==mem) . patElemName . snd) $ zip [0..] ctx  of-                Just (i, PatElem _ (MemMem space)) ->-                  ReturnsNewBlock space i $-                  existentialiseIxFun (map patElemName ctx) ixfun-                _ -> ReturnsInBlock mem $ existentialiseIxFun [] ixfun-         )--instance (PP.Pretty u, PP.Pretty r) => PrettyAnnot (PatElemT (MemInfo SubExp u r)) where-  ppAnnot = bindeeAnnot patElemName patElemDec--instance (PP.Pretty u, PP.Pretty r) => PrettyAnnot (Param (MemInfo SubExp u r)) where-  ppAnnot = bindeeAnnot paramName paramDec--bindeeAnnot :: (PP.Pretty u, PP.Pretty r) =>-               (a -> VName) -> (a -> MemInfo SubExp u r)-            -> a -> Maybe PP.Doc-bindeeAnnot bindeeName bindeeLore bindee =-  case bindeeLore bindee of-    dec@MemArray{} ->-      Just $-      PP.stack $ map (("-- "<>) . PP.text) $ lines $-      pretty (PP.ppr (bindeeName bindee) PP.<+> ":" PP.<+> PP.ppr dec)-    MemMem {} ->-      Nothing-    MemPrim _ ->-      Nothing--extReturns :: [ExtType] -> [ExpReturns]-extReturns ts =-    evalState (mapM addDec ts) 0-    where addDec (Prim bt) =-            return $ MemPrim bt-          addDec (Mem space) =-            return $ MemMem space-          addDec t@(Array bt shape u)-            | existential t = do-              i <- get <* modify (+1)-              return $ MemArray bt shape u $ Just $-                ReturnsNewBlock DefaultSpace i $-                IxFun.iota $ map convert $ shapeDims shape-            | otherwise =-              return $ MemArray bt shape u Nothing-          convert (Ext i) = LeafExp (Ext i) int32-          convert (Free v) = Free <$> primExpFromSubExp int32 v--arrayVarReturns :: (HasScope lore m, Monad m, Mem lore) =>-                   VName-                -> m (PrimType, Shape, VName, IxFun.IxFun (PrimExp VName))-arrayVarReturns v = do-  summary <- lookupMemInfo v-  case summary of-    MemArray et shape _ (ArrayIn mem ixfun) ->-      return (et, Shape $ shapeDims shape, mem, ixfun)-    _ ->-      error $ "arrayVarReturns: " ++ pretty v ++ " is not an array."--varReturns :: (HasScope lore m, Monad m, Mem lore) =>-              VName -> m ExpReturns-varReturns v = do-  summary <- lookupMemInfo v-  case summary of-    MemPrim bt ->-      return $ MemPrim bt-    MemArray et shape _ (ArrayIn mem ixfun) ->-      return $ MemArray et (fmap Free shape) NoUniqueness $-               Just $ ReturnsInBlock mem $ existentialiseIxFun [] ixfun-    MemMem space ->-      return $ MemMem space---- | The return information of an expression.  This can be seen as the--- "return type with memory annotations" of the expression.-expReturns :: (Monad m, HasScope lore m,-               Mem lore) =>-              Exp lore -> m [ExpReturns]--expReturns (BasicOp (SubExp (Var v))) =-  pure <$> varReturns v--expReturns (BasicOp (Opaque (Var v))) =-  pure <$> varReturns v--expReturns (BasicOp (Reshape newshape v)) = do-  (et, _, mem, ixfun) <- arrayVarReturns v-  return [MemArray et (Shape $ map (Free . newDim) newshape) NoUniqueness $-          Just $ ReturnsInBlock mem $ existentialiseIxFun [] $-          IxFun.reshape ixfun $ map (fmap $ primExpFromSubExp int32) newshape]--expReturns (BasicOp (Rearrange perm v)) = do-  (et, Shape dims, mem, ixfun) <- arrayVarReturns v-  let ixfun' = IxFun.permute ixfun perm-      dims'  = rearrangeShape perm dims-  return [MemArray et (Shape $ map Free dims') NoUniqueness $-          Just $ ReturnsInBlock mem $ existentialiseIxFun [] ixfun']--expReturns (BasicOp (Rotate offsets v)) = do-  (et, Shape dims, mem, ixfun) <- arrayVarReturns v-  let offsets' = map (primExpFromSubExp int32) offsets-      ixfun' = IxFun.rotate ixfun offsets'-  return [MemArray et (Shape $ map Free dims) NoUniqueness $-          Just $ ReturnsInBlock mem $ existentialiseIxFun [] ixfun']--expReturns (BasicOp (Index v slice)) = do-  info <- sliceInfo v slice-  case info of-    MemArray et shape u (ArrayIn mem ixfun) ->-      return [MemArray et (fmap Free shape) u $-              Just $ ReturnsInBlock mem $ existentialiseIxFun [] ixfun]-    MemPrim pt -> return [MemPrim pt]-    MemMem space -> return [MemMem space]--expReturns (BasicOp (Update v _ _)) =-  pure <$> varReturns v--expReturns (BasicOp op) =-  extReturns . staticShapes <$> primOpType op--expReturns e@(DoLoop ctx val _ _) = do-  t <- expExtType e-  zipWithM typeWithDec t $ map fst val-    where typeWithDec t p =-            case (t, paramDec p) of-              (Array bt shape u, MemArray _ _ _ (ArrayIn mem ixfun))-                | Just (i, mem_p) <- isMergeVar mem,-                  Mem space <- paramType mem_p ->-                    return $ MemArray bt shape u $ Just $ ReturnsNewBlock space i ixfun'-                | otherwise ->-                  return (MemArray bt shape u $-                          Just $ ReturnsInBlock mem ixfun')-                  where ixfun' = existentialiseIxFun (map paramName mergevars) ixfun-              (Array{}, _) ->-                error "expReturns: Array return type but not array merge variable."-              (Prim bt, _) ->-                return $ MemPrim bt-              (Mem{}, _) ->-                error "expReturns: loop returns memory block explicitly."-          isMergeVar v = find ((==v) . paramName . snd) $ zip [0..] mergevars-          mergevars = map fst $ ctx ++ val--expReturns (Apply _ _ ret _) =-  return $ map funReturnsToExpReturns ret--expReturns (If _ _ _ (IfDec ret _)) =-  return $ map bodyReturnsToExpReturns ret--expReturns (Op op) =-  opReturns op--sliceInfo :: (Monad m, HasScope lore m, Mem lore) =>-             VName-          -> Slice SubExp -> m (MemInfo SubExp NoUniqueness MemBind)-sliceInfo v slice = do-  (et, _, mem, ixfun) <- arrayVarReturns v-  case sliceDims slice of-    [] -> return $ MemPrim et-    dims ->-      return $ MemArray et (Shape dims) NoUniqueness $-      ArrayIn mem $ IxFun.slice ixfun-      (map (fmap (primExpFromSubExp int32)) slice)--class TypedOp (Op lore) => OpReturns lore where-  opReturns :: (Monad m, HasScope lore m) =>-               Op lore -> m [ExpReturns]-  opReturns op = extReturns <$> opType op--applyFunReturns :: Typed dec =>-                   [FunReturns]-                -> [Param dec]-                -> [(SubExp,Type)]-                -> Maybe [FunReturns]-applyFunReturns rets params args-  | Just _ <- applyRetType rettype params args =-      Just $ map correctDims rets-  | otherwise =-      Nothing-  where rettype = map declExtTypeOf rets-        parammap :: M.Map VName (SubExp, Type)-        parammap = M.fromList $-                   zip (map paramName params) args--        substSubExp (Var v)-          | Just (se,_) <- M.lookup v parammap = se-        substSubExp se = se--        correctDims (MemPrim t) =-          MemPrim t-        correctDims (MemMem space) =-          MemMem space-        correctDims (MemArray et shape u memsummary) =-          MemArray et (correctShape shape) u $-          correctSummary memsummary--        correctShape = Shape . map correctDim . shapeDims-        correctDim (Ext i)   = Ext i-        correctDim (Free se) = Free $ substSubExp se--        correctSummary (ReturnsNewBlock space i ixfun) =-          ReturnsNewBlock space i ixfun-        correctSummary (ReturnsInBlock mem ixfun) =-          -- FIXME: we should also do a replacement in ixfun here.-          ReturnsInBlock mem' ixfun-          where mem' = case M.lookup mem parammap of-                  Just (Var v, _) -> v-                  _               -> mem+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}++-- | Building blocks for defining representations where every array+-- is given information about which memory block is it based in, and+-- how array elements map to memory block offsets.+--+-- There are two primary concepts you will need to understand:+--+--  1. Memory blocks, which are Futhark values of type v'Mem'+--     (parametrized with their size).  These correspond to arbitrary+--     blocks of memory, and are created using the 'Alloc' operation.+--+--  2. Index functions, which describe a mapping from the index space+--     of an array (eg. a two-dimensional space for an array of type+--     @[[int]]@) to a one-dimensional offset into a memory block.+--     Thus, index functions describe how arbitrary-dimensional arrays+--     are mapped to the single-dimensional world of memory.+--+-- At a conceptual level, imagine that we have a two-dimensional array+-- @a@ of 32-bit integers, consisting of @n@ rows of @m@ elements+-- each.  This array could be represented in classic row-major format+-- with an index function like the following:+--+-- @+--   f(i,j) = i * m + j+-- @+--+-- When we want to know the location of element @a[2,3]@, we simply+-- call the index function as @f(2,3)@ and obtain @2*m+3@.  We could+-- also have chosen another index function, one that represents the+-- array in column-major (or "transposed") format:+--+-- @+--   f(i,j) = j * n + i+-- @+--+-- Index functions are not Futhark-level functions, but a special+-- construct that the final code generator will eventually use to+-- generate concrete access code.  By modifying the index functions we+-- can change how an array is represented in memory, which can permit+-- memory access pattern optimisations.+--+-- Every time we bind an array, whether in a @let@-binding, @loop@+-- merge parameter, or @lambda@ parameter, we have an annotation+-- specifying a memory block and an index function.  In some cases,+-- such as @let@-bindings for many expressions, we are free to specify+-- an arbitrary index function and memory block - for example, we get+-- to decide where 'Copy' stores its result - but in other cases the+-- type rules of the expression chooses for us.  For example, 'Index'+-- always produces an array in the same memory block as its input, and+-- with the same index function, except with some indices fixed.+module Futhark.IR.Mem+  ( LetDecMem,+    FParamMem,+    LParamMem,+    RetTypeMem,+    BranchTypeMem,+    MemOp (..),+    MemInfo (..),+    MemBound,+    MemBind (..),+    MemReturn (..),+    IxFun,+    ExtIxFun,+    isStaticIxFun,+    ExpReturns,+    BodyReturns,+    FunReturns,+    noUniquenessReturns,+    bodyReturnsToExpReturns,+    Mem,+    AllocOp (..),+    OpReturns (..),+    varReturns,+    expReturns,+    extReturns,+    lookupMemInfo,+    subExpMemInfo,+    lookupArraySummary,+    existentialiseIxFun,++    -- * Type checking parts+    matchBranchReturnType,+    matchPatternToExp,+    matchFunctionReturnType,+    matchLoopResultMem,+    bodyReturnsFromPattern,+    checkMemInfo,++    -- * Module re-exports+    module Futhark.IR.Prop,+    module Futhark.IR.Traversals,+    module Futhark.IR.Pretty,+    module Futhark.IR.Syntax,+    module Futhark.Analysis.PrimExp.Convert,+  )+where++import Control.Category+import Control.Monad.Except+import Control.Monad.Reader+import Control.Monad.State+import Data.Foldable (toList, traverse_)+import Data.List (elemIndex, find)+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Data.Set as S+import Futhark.Analysis.Metrics+import Futhark.Analysis.PrimExp.Convert+import Futhark.Analysis.PrimExp.Simplify+import qualified Futhark.Analysis.SymbolTable as ST+import Futhark.IR.Aliases+  ( Aliases,+    removeExpAliases,+    removePatternAliases,+    removeScopeAliases,+  )+import qualified Futhark.IR.Mem.IxFun as IxFun+import Futhark.IR.Pretty+import Futhark.IR.Prop+import Futhark.IR.Prop.Aliases+import Futhark.IR.Syntax+import Futhark.IR.Traversals+import qualified Futhark.Optimise.Simplify.Engine as Engine+import Futhark.Optimise.Simplify.Lore+import Futhark.Transform.Rename+import Futhark.Transform.Substitute+import qualified Futhark.TypeCheck as TC+import Futhark.Util+import qualified Futhark.Util.Pretty as PP+import GHC.Generics (Generic)+import Language.SexpGrammar as Sexp+import Language.SexpGrammar.Generic+import Prelude hiding (id, (.))++type LetDecMem = MemInfo SubExp NoUniqueness MemBind++type FParamMem = MemInfo SubExp Uniqueness MemBind++type LParamMem = MemInfo SubExp NoUniqueness MemBind++type RetTypeMem = FunReturns++type BranchTypeMem = BodyReturns++-- | The class of ops that have memory allocation.+class AllocOp op where+  allocOp :: SubExp -> Space -> op++type Mem lore =+  ( AllocOp (Op lore),+    FParamInfo lore ~ FParamMem,+    LParamInfo lore ~ LParamMem,+    LetDec lore ~ LetDecMem,+    RetType lore ~ RetTypeMem,+    BranchType lore ~ BranchTypeMem,+    ASTLore lore,+    Decorations lore,+    OpReturns lore+  )++instance IsRetType FunReturns where+  primRetType = MemPrim+  applyRetType = applyFunReturns++instance IsBodyType BodyReturns where+  primBodyType = MemPrim++data MemOp inner+  = -- | Allocate a memory block.  This really should not be an+    -- expression, but what are you gonna do...+    Alloc SubExp Space+  | Inner inner+  deriving (Eq, Ord, Show, Generic)++instance SexpIso inner => SexpIso (MemOp inner) where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "alloc") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+        With+          (. Sexp.list (Sexp.el (Sexp.sym "inner") >>> Sexp.el sexpIso))+          End++instance AllocOp (MemOp inner) where+  allocOp = Alloc++instance FreeIn inner => FreeIn (MemOp inner) where+  freeIn' (Alloc size _) = freeIn' size+  freeIn' (Inner k) = freeIn' k++instance TypedOp inner => TypedOp (MemOp inner) where+  opType (Alloc _ space) = pure [Mem space]+  opType (Inner k) = opType k++instance AliasedOp inner => AliasedOp (MemOp inner) where+  opAliases Alloc {} = [mempty]+  opAliases (Inner k) = opAliases k++  consumedInOp Alloc {} = mempty+  consumedInOp (Inner k) = consumedInOp k++instance CanBeAliased inner => CanBeAliased (MemOp inner) where+  type OpWithAliases (MemOp inner) = MemOp (OpWithAliases inner)+  removeOpAliases (Alloc se space) = Alloc se space+  removeOpAliases (Inner k) = Inner $ removeOpAliases k++  addOpAliases (Alloc se space) = Alloc se space+  addOpAliases (Inner k) = Inner $ addOpAliases k++instance Rename inner => Rename (MemOp inner) where+  rename (Alloc size space) = Alloc <$> rename size <*> pure space+  rename (Inner k) = Inner <$> rename k++instance Substitute inner => Substitute (MemOp inner) where+  substituteNames subst (Alloc size space) = Alloc (substituteNames subst size) space+  substituteNames subst (Inner k) = Inner $ substituteNames subst k++instance PP.Pretty inner => PP.Pretty (MemOp inner) where+  ppr (Alloc e DefaultSpace) = PP.text "alloc" <> PP.apply [PP.ppr e]+  ppr (Alloc e s) = PP.text "alloc" <> PP.apply [PP.ppr e, PP.ppr s]+  ppr (Inner k) = PP.ppr k++instance OpMetrics inner => OpMetrics (MemOp inner) where+  opMetrics Alloc {} = seen "Alloc"+  opMetrics (Inner k) = opMetrics k++instance IsOp inner => IsOp (MemOp inner) where+  safeOp (Alloc (Constant (IntValue (Int64Value k))) _) = k >= 0+  safeOp Alloc {} = False+  safeOp (Inner k) = safeOp k+  cheapOp (Inner k) = cheapOp k+  cheapOp Alloc {} = True++instance CanBeWise inner => CanBeWise (MemOp inner) where+  type OpWithWisdom (MemOp inner) = MemOp (OpWithWisdom inner)+  removeOpWisdom (Alloc size space) = Alloc size space+  removeOpWisdom (Inner k) = Inner $ removeOpWisdom k++instance ST.IndexOp inner => ST.IndexOp (MemOp inner) where+  indexOp vtable k (Inner op) is = ST.indexOp vtable k op is+  indexOp _ _ _ _ = Nothing++-- | The index function representation used for memory annotations.+type IxFun = IxFun.IxFun (TPrimExp Int64 VName)++-- | An index function that may contain existential variables.+type ExtIxFun = IxFun.IxFun (TPrimExp Int64 (Ext VName))++-- | A summary of the memory information for every let-bound+-- identifier, function parameter, and return value.  Parameterisered+-- over uniqueness, dimension, and auxiliary array information.+data MemInfo d u ret+  = -- | A primitive value.+    MemPrim PrimType+  | -- | A memory block.+    MemMem Space+  | -- | The array is stored in the named memory block, and with the+    -- given index function.  The index function maps indices in the+    -- array to /element/ offset, /not/ byte offsets!  To translate to+    -- byte offsets, multiply the offset with the size of the array+    -- element type.+    MemArray PrimType (ShapeBase d) u ret+  deriving (Eq, Show, Ord, Generic) --- XXX Ord?++instance (SexpIso d, SexpIso u, SexpIso ret) => SexpIso (MemInfo d u ret) where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "prim") >>> Sexp.el sexpIso)) $+        With (. Sexp.list (Sexp.el (Sexp.sym "mem") >>> Sexp.el sexpIso)) $+          With+            (. Sexp.list (Sexp.el (Sexp.sym "array") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso))+            End++type MemBound u = MemInfo SubExp u MemBind++instance FixExt ret => DeclExtTyped (MemInfo ExtSize Uniqueness ret) where+  declExtTypeOf (MemPrim pt) = Prim pt+  declExtTypeOf (MemMem space) = Mem space+  declExtTypeOf (MemArray pt shape u _) = Array pt shape u++instance FixExt ret => ExtTyped (MemInfo ExtSize NoUniqueness ret) where+  extTypeOf (MemPrim pt) = Prim pt+  extTypeOf (MemMem space) = Mem space+  extTypeOf (MemArray pt shape u _) = Array pt shape u++instance FixExt ret => FixExt (MemInfo ExtSize u ret) where+  fixExt _ _ (MemPrim pt) = MemPrim pt+  fixExt _ _ (MemMem space) = MemMem space+  fixExt i se (MemArray pt shape u ret) =+    MemArray pt (fixExt i se shape) u (fixExt i se ret)++instance Typed (MemInfo SubExp Uniqueness ret) where+  typeOf = fromDecl . declTypeOf++instance Typed (MemInfo SubExp NoUniqueness ret) where+  typeOf (MemPrim pt) = Prim pt+  typeOf (MemMem space) = Mem space+  typeOf (MemArray bt shape u _) = Array bt shape u++instance DeclTyped (MemInfo SubExp Uniqueness ret) where+  declTypeOf (MemPrim bt) = Prim bt+  declTypeOf (MemMem space) = Mem space+  declTypeOf (MemArray bt shape u _) = Array bt shape u++instance (FreeIn d, FreeIn ret) => FreeIn (MemInfo d u ret) where+  freeIn' (MemArray _ shape _ ret) = freeIn' shape <> freeIn' ret+  freeIn' (MemMem s) = freeIn' s+  freeIn' MemPrim {} = mempty++instance (Substitute d, Substitute ret) => Substitute (MemInfo d u ret) where+  substituteNames subst (MemArray bt shape u ret) =+    MemArray+      bt+      (substituteNames subst shape)+      u+      (substituteNames subst ret)+  substituteNames _ (MemMem space) =+    MemMem space+  substituteNames _ (MemPrim bt) =+    MemPrim bt++instance (Substitute d, Substitute ret) => Rename (MemInfo d u ret) where+  rename = substituteRename++simplifyIxFun ::+  Engine.SimplifiableLore lore =>+  IxFun ->+  Engine.SimpleM lore IxFun+simplifyIxFun = traverse $ fmap isInt64 . simplifyPrimExp . untyped++simplifyExtIxFun ::+  Engine.SimplifiableLore lore =>+  ExtIxFun ->+  Engine.SimpleM lore ExtIxFun+simplifyExtIxFun = traverse $ fmap isInt64 . simplifyExtPrimExp . untyped++isStaticIxFun :: ExtIxFun -> Maybe IxFun+isStaticIxFun = traverse $ traverse inst+  where+    inst Ext {} = Nothing+    inst (Free x) = Just x++instance+  (Engine.Simplifiable d, Engine.Simplifiable ret) =>+  Engine.Simplifiable (MemInfo d u ret)+  where+  simplify (MemPrim bt) =+    return $ MemPrim bt+  simplify (MemMem space) =+    pure $ MemMem space+  simplify (MemArray bt shape u ret) =+    MemArray bt <$> Engine.simplify shape <*> pure u <*> Engine.simplify ret++instance+  ( PP.Pretty (TypeBase (ShapeBase d) u),+    PP.Pretty d,+    PP.Pretty u,+    PP.Pretty ret+  ) =>+  PP.Pretty (MemInfo d u ret)+  where+  ppr (MemPrim bt) = PP.ppr bt+  ppr (MemMem DefaultSpace) = PP.text "mem"+  ppr (MemMem s) = PP.text "mem" <> PP.ppr s+  ppr (MemArray bt shape u ret) =+    PP.ppr (Array bt shape u) <> PP.text "@" <> PP.ppr ret++instance PP.Pretty (Param (MemInfo SubExp Uniqueness ret)) where+  ppr = PP.ppr . fmap declTypeOf++instance PP.Pretty (Param (MemInfo SubExp NoUniqueness ret)) where+  ppr = PP.ppr . fmap typeOf++instance PP.Pretty (PatElemT (MemInfo SubExp NoUniqueness ret)) where+  ppr = PP.ppr . fmap typeOf++-- | Memory information for an array bound somewhere in the program.+data MemBind+  = -- | Located in this memory block with this index+    -- function.+    ArrayIn VName IxFun+  deriving (Show, Generic)++instance SexpIso MemBind where+  sexpIso = with $ \membind ->+    Sexp.list (Sexp.el sexpIso >>> Sexp.el sexpIso)+      >>> membind++instance Eq MemBind where+  _ == _ = True++instance Ord MemBind where+  _ `compare` _ = EQ++instance Rename MemBind where+  rename = substituteRename++instance Substitute MemBind where+  substituteNames substs (ArrayIn ident ixfun) =+    ArrayIn (substituteNames substs ident) (substituteNames substs ixfun)++instance PP.Pretty MemBind where+  ppr (ArrayIn mem ixfun) =+    PP.text "@" <> PP.ppr mem <> PP.text "->" PP.</> PP.ppr ixfun++instance FreeIn MemBind where+  freeIn' (ArrayIn mem ixfun) = freeIn' mem <> freeIn' ixfun++-- | A description of the memory properties of an array being returned+-- by an operation.+data MemReturn+  = -- | The array is located in a memory block that is+    -- already in scope.+    ReturnsInBlock VName ExtIxFun+  | -- | The operation returns a new (existential) memory+    -- block.+    ReturnsNewBlock Space Int ExtIxFun+  deriving (Show, Generic)++instance SexpIso MemReturn where+  sexpIso =+    match $+      With+        ( .+            Sexp.list+              ( Sexp.el (Sexp.sym "returns-in-block")+                  >>> Sexp.el sexpIso+                  >>> Sexp.el sexpIso+              )+        )+        $ With+          ( .+              Sexp.list+                ( Sexp.el (Sexp.sym "returns-new-block")+                    >>> Sexp.el sexpIso+                    >>> Sexp.el sexpIso+                    >>> Sexp.el sexpIso+                )+          )+          End++instance Eq MemReturn where+  _ == _ = True++instance Ord MemReturn where+  _ `compare` _ = EQ++instance Rename MemReturn where+  rename = substituteRename++instance Substitute MemReturn where+  substituteNames substs (ReturnsInBlock ident ixfun) =+    ReturnsInBlock (substituteNames substs ident) (substituteNames substs ixfun)+  substituteNames substs (ReturnsNewBlock space i ixfun) =+    ReturnsNewBlock space i (substituteNames substs ixfun)++instance FixExt MemReturn where+  fixExt i (Var v) (ReturnsNewBlock _ j ixfun)+    | j == i =+      ReturnsInBlock v $+        fixExtIxFun+          i+          (primExpFromSubExp int64 (Var v))+          ixfun+  fixExt i se (ReturnsNewBlock space j ixfun) =+    ReturnsNewBlock+      space+      j'+      (fixExtIxFun i (primExpFromSubExp int64 se) ixfun)+    where+      j'+        | i < j = j -1+        | otherwise = j+  fixExt i se (ReturnsInBlock mem ixfun) =+    ReturnsInBlock mem (fixExtIxFun i (primExpFromSubExp int64 se) ixfun)++fixExtIxFun :: Int -> PrimExp VName -> ExtIxFun -> ExtIxFun+fixExtIxFun i e = fmap $ isInt64 . replaceInPrimExp update . untyped+  where+    update (Ext j) t+      | j > i = LeafExp (Ext $ j - 1) t+      | j == i = fmap Free e+      | otherwise = LeafExp (Ext j) t+    update (Free x) t = LeafExp (Free x) t++leafExp :: Int -> TPrimExp Int64 (Ext a)+leafExp i = isInt64 $ LeafExp (Ext i) int64++existentialiseIxFun :: [VName] -> IxFun -> ExtIxFun+existentialiseIxFun ctx = IxFun.substituteInIxFun ctx' . fmap (fmap Free)+  where+    ctx' = M.map leafExp $ M.fromList $ zip (map Free ctx) [0 ..]++instance PP.Pretty MemReturn where+  ppr (ReturnsInBlock v ixfun) =+    PP.parens $ PP.text (pretty v) <> PP.text "->" PP.</> PP.ppr ixfun+  ppr (ReturnsNewBlock space i ixfun) =+    PP.text ("?" ++ show i) <> PP.ppr space <> PP.text "->" PP.</> PP.ppr ixfun++instance FreeIn MemReturn where+  freeIn' (ReturnsInBlock v ixfun) = freeIn' v <> freeIn' ixfun+  freeIn' (ReturnsNewBlock space _ ixfun) = freeIn' space <> freeIn' ixfun++instance Engine.Simplifiable MemReturn where+  simplify (ReturnsNewBlock space i ixfun) =+    ReturnsNewBlock space i <$> simplifyExtIxFun ixfun+  simplify (ReturnsInBlock v ixfun) =+    ReturnsInBlock <$> Engine.simplify v <*> simplifyExtIxFun ixfun++instance Engine.Simplifiable MemBind where+  simplify (ArrayIn mem ixfun) =+    ArrayIn <$> Engine.simplify mem <*> simplifyIxFun ixfun++instance Engine.Simplifiable [FunReturns] where+  simplify = mapM Engine.simplify++-- | The memory return of an expression.  An array is annotated with+-- @Maybe MemReturn@, which can be interpreted as the expression+-- either dictating exactly where the array is located when it is+-- returned (if 'Just'), or able to put it whereever the binding+-- prefers (if 'Nothing').+--+-- This is necessary to capture the difference between an expression+-- that is just an array-typed variable, in which the array being+-- "returned" is located where it already is, and a @copy@ expression,+-- whose entire purpose is to store an existing array in some+-- arbitrary location.  This is a consequence of the design decision+-- never to have implicit memory copies.+type ExpReturns = MemInfo ExtSize NoUniqueness (Maybe MemReturn)++-- | The return of a body, which must always indicate where+-- returned arrays are located.+type BodyReturns = MemInfo ExtSize NoUniqueness MemReturn++-- | The memory return of a function, which must always indicate where+-- returned arrays are located.+type FunReturns = MemInfo ExtSize Uniqueness MemReturn++maybeReturns :: MemInfo d u r -> MemInfo d u (Maybe r)+maybeReturns (MemArray bt shape u ret) =+  MemArray bt shape u $ Just ret+maybeReturns (MemPrim bt) =+  MemPrim bt+maybeReturns (MemMem space) =+  MemMem space++noUniquenessReturns :: MemInfo d u r -> MemInfo d NoUniqueness r+noUniquenessReturns (MemArray bt shape _ r) =+  MemArray bt shape NoUniqueness r+noUniquenessReturns (MemPrim bt) =+  MemPrim bt+noUniquenessReturns (MemMem space) =+  MemMem space++funReturnsToExpReturns :: FunReturns -> ExpReturns+funReturnsToExpReturns = noUniquenessReturns . maybeReturns++bodyReturnsToExpReturns :: BodyReturns -> ExpReturns+bodyReturnsToExpReturns = noUniquenessReturns . maybeReturns++matchRetTypeToResult ::+  (Mem lore, TC.Checkable lore) =>+  [FunReturns] ->+  Result ->+  TC.TypeM lore ()+matchRetTypeToResult rettype result = do+  scope <- askScope+  result_ts <- runReaderT (mapM subExpMemInfo result) $ removeScopeAliases scope+  matchReturnType rettype result result_ts++matchFunctionReturnType ::+  (Mem lore, TC.Checkable lore) =>+  [FunReturns] ->+  Result ->+  TC.TypeM lore ()+matchFunctionReturnType rettype result = do+  matchRetTypeToResult rettype result+  mapM_ checkResultSubExp result+  where+    checkResultSubExp Constant {} =+      return ()+    checkResultSubExp (Var v) = do+      dec <- varMemInfo v+      case dec of+        MemPrim _ -> return ()+        MemMem {} -> return ()+        MemArray _ _ _ (ArrayIn _ ixfun)+          | IxFun.isLinear ixfun ->+            return ()+          | otherwise ->+            TC.bad $+              TC.TypeError $+                "Array " ++ pretty v+                  ++ " returned by function, but has nontrivial index function "+                  ++ pretty ixfun++matchLoopResultMem ::+  (Mem lore, TC.Checkable lore) =>+  [FParam (Aliases lore)] ->+  [FParam (Aliases lore)] ->+  [SubExp] ->+  TC.TypeM lore ()+matchLoopResultMem ctx val = matchRetTypeToResult rettype+  where+    ctx_names = map paramName ctx++    -- Invent a ReturnType so we can pretend that the loop body is+    -- actually returning from a function.+    rettype = map (toRet . paramDec) val++    toExtV v+      | Just i <- v `elemIndex` ctx_names = Ext i+      | otherwise = Free v++    toExtSE (Var v) = Var <$> toExtV v+    toExtSE (Constant v) = Free $ Constant v++    toRet (MemPrim t) =+      MemPrim t+    toRet (MemMem space) =+      MemMem space+    toRet (MemArray pt shape u (ArrayIn mem ixfun))+      | Just i <- mem `elemIndex` ctx_names,+        Param _ (MemMem space) : _ <- drop i ctx =+        MemArray pt shape' u $ ReturnsNewBlock space i ixfun'+      | otherwise =+        MemArray pt shape' u $ ReturnsInBlock mem ixfun'+      where+        shape' = fmap toExtSE shape+        ixfun' = existentialiseIxFun ctx_names ixfun++matchBranchReturnType ::+  (Mem lore, TC.Checkable lore) =>+  [BodyReturns] ->+  Body (Aliases lore) ->+  TC.TypeM lore ()+matchBranchReturnType rettype (Body _ stms res) = do+  scope <- askScope+  ts <- runReaderT (mapM subExpMemInfo res) $ removeScopeAliases (scope <> scopeOf stms)+  matchReturnType rettype res ts++-- | Helper function for index function unification.+--+-- The first return value maps a VName (wrapped in 'Free') to its Int+-- (wrapped in 'Ext').  In case of duplicates, it is mapped to the+-- *first* Int that occurs.+--+-- The second return value maps each Int (wrapped in an 'Ext') to a+-- 'LeafExp' 'Ext' with the Int at which its associated VName first+-- occurs.+getExtMaps ::+  [(VName, Int)] ->+  ( M.Map (Ext VName) (TPrimExp Int64 (Ext VName)),+    M.Map (Ext VName) (TPrimExp Int64 (Ext VName))+  )+getExtMaps ctx_lst_ids =+  ( M.map leafExp $ M.mapKeys Free $ M.fromListWith (flip const) ctx_lst_ids,+    M.fromList $+      mapMaybe+        ( traverse+            ( fmap (\i -> isInt64 $ LeafExp (Ext i) int64)+                . (`lookup` ctx_lst_ids)+            )+            . uncurry (flip (,))+            . fmap Ext+        )+        ctx_lst_ids+  )++matchReturnType ::+  PP.Pretty u =>+  [MemInfo ExtSize u MemReturn] ->+  [SubExp] ->+  [MemInfo SubExp NoUniqueness MemBind] ->+  TC.TypeM lore ()+matchReturnType rettype res ts = do+  let (ctx_ts, val_ts) = splitFromEnd (length rettype) ts+      (ctx_res, _val_res) = splitFromEnd (length rettype) res++      existentialiseIxFun0 :: IxFun -> ExtIxFun+      existentialiseIxFun0 = fmap $ fmap Free++      fetchCtx i = case maybeNth i $ zip ctx_res ctx_ts of+        Nothing ->+          throwError $+            "Cannot find context variable "+              ++ show i+              ++ " in context results: "+              ++ pretty ctx_res+        Just (se, t) -> return (se, t)++      checkReturn (MemPrim x) (MemPrim y)+        | x == y = return ()+      checkReturn (MemMem x) (MemMem y)+        | x == y = return ()+      checkReturn+        (MemArray x_pt x_shape _ x_ret)+        (MemArray y_pt y_shape _ y_ret)+          | x_pt == y_pt,+            shapeRank x_shape == shapeRank y_shape = do+            zipWithM_ checkDim (shapeDims x_shape) (shapeDims y_shape)+            checkMemReturn x_ret y_ret+      checkReturn x y =+        throwError $ unwords ["Expected", pretty x, "but got", pretty y]++      checkDim (Free x) y+        | x == y = return ()+        | otherwise =+          throwError $+            unwords+              [ "Expected dim",+                pretty x,+                "but got",+                pretty y+              ]+      checkDim (Ext i) y = do+        (x, _) <- fetchCtx i+        unless (x == y) $+          throwError $+            unwords+              [ "Expected ext dim",+                pretty i,+                "=>",+                pretty x,+                "but got",+                pretty y+              ]++      extsInMemInfo :: MemInfo ExtSize u MemReturn -> S.Set Int+      extsInMemInfo (MemArray _ shp _ ret) =+        extInShape shp <> extInMemReturn ret+      extsInMemInfo _ = S.empty++      checkMemReturn (ReturnsInBlock x_mem x_ixfun) (ArrayIn y_mem y_ixfun)+        | x_mem == y_mem =+          unless (IxFun.closeEnough x_ixfun $ existentialiseIxFun0 y_ixfun) $+            throwError $+              unwords+                [ "Index function unification failed (ReturnsInBlock)",+                  "\nixfun of body result: ",+                  pretty y_ixfun,+                  "\nixfun of return type: ",+                  pretty x_ixfun,+                  "\nand context elements: ",+                  pretty ctx_res+                ]+      checkMemReturn+        (ReturnsNewBlock x_space x_ext x_ixfun)+        (ArrayIn y_mem y_ixfun) = do+          (x_mem, x_mem_type) <- fetchCtx x_ext+          unless (IxFun.closeEnough x_ixfun $ existentialiseIxFun0 y_ixfun) $+            throwError $+              unwords+                [ "Index function unification failed (ReturnsNewBlock)",+                  "\nixfun of body result: ",+                  pretty y_ixfun,+                  "\nixfun of return type: ",+                  pretty x_ixfun,+                  "\nand context elements: ",+                  pretty ctx_res+                ]+          case x_mem_type of+            MemMem y_space ->+              unless (x_space == y_space) $+                throwError $+                  unwords+                    [ "Expected memory",+                      pretty y_mem,+                      "in space",+                      pretty x_space,+                      "but actually in space",+                      pretty y_space+                    ]+            t ->+              throwError $+                unwords+                  [ "Expected memory",+                    pretty x_ext,+                    "=>",+                    pretty x_mem,+                    "but but has type",+                    pretty t+                  ]+      checkMemReturn x y =+        throwError $+          unwords+            [ "Expected array in",+              pretty x,+              "but array returned in",+              pretty y+            ]++      bad :: String -> TC.TypeM lore a+      bad s =+        TC.bad $+          TC.TypeError $+            PP.pretty $+              "Return type"+                PP.</> PP.indent 2 (ppTuple' rettype)+                PP.</> "cannot match returns of results"+                PP.</> PP.indent 2 (ppTuple' ts)+                PP.</> PP.text s++  unless (length (S.unions $ map extsInMemInfo rettype) == length ctx_res) $+    TC.bad $+      TC.TypeError $+        "Too many context parameters for the number of "+          ++ "existentials in the return type! type:\n  "+          ++ prettyTuple rettype+          ++ "\ncannot match context parameters:\n  "+          ++ prettyTuple ctx_res++  either bad return =<< runExceptT (zipWithM_ checkReturn rettype val_ts)++matchPatternToExp ::+  (Mem lore, TC.Checkable lore) =>+  Pattern (Aliases lore) ->+  Exp (Aliases lore) ->+  TC.TypeM lore ()+matchPatternToExp pat e = do+  scope <- asksScope removeScopeAliases+  rt <- runReaderT (expReturns $ removeExpAliases e) scope++  let (ctxs, vals) = bodyReturnsFromPattern $ removePatternAliases pat+      (ctx_ids, _ctx_ts) = unzip ctxs+      (_val_ids, val_ts) = unzip vals+      (ctx_map_ids, ctx_map_exts) =+        getExtMaps $ zip ctx_ids [0 .. length ctx_ids - 1]++  let rt_exts = foldMap extInExpReturns rt++  unless+    ( length val_ts == length rt+        && and (zipWith (matches ctx_map_ids ctx_map_exts) val_ts rt)+        && M.keysSet ctx_map_exts `S.isSubsetOf` S.map Ext rt_exts+    )+    $ TC.bad $+      TC.TypeError $+        "Expression type:\n  " ++ prettyTuple rt+          ++ "\ncannot match pattern type:\n  "+          ++ prettyTuple val_ts+          ++ "\nwith context elements: "+          ++ pretty ctx_ids+  where+    matches _ _ (MemPrim x) (MemPrim y) = x == y+    matches _ _ (MemMem x_space) (MemMem y_space) =+      x_space == y_space+    matches ctxids ctxexts (MemArray x_pt x_shape _ x_ret) (MemArray y_pt y_shape _ y_ret) =+      x_pt == y_pt && x_shape == y_shape+        && case (x_ret, y_ret) of+          (ReturnsInBlock _ x_ixfun, Just (ReturnsInBlock _ y_ixfun)) ->+            let x_ixfun' = IxFun.substituteInIxFun ctxids x_ixfun+                y_ixfun' = IxFun.substituteInIxFun ctxexts y_ixfun+             in IxFun.closeEnough x_ixfun' y_ixfun'+          ( ReturnsInBlock _ x_ixfun,+            Just (ReturnsNewBlock _ _ y_ixfun)+            ) ->+              let x_ixfun' = IxFun.substituteInIxFun ctxids x_ixfun+                  y_ixfun' = IxFun.substituteInIxFun ctxexts y_ixfun+               in IxFun.closeEnough x_ixfun' y_ixfun'+          ( ReturnsNewBlock _ x_i x_ixfun,+            Just (ReturnsNewBlock _ y_i y_ixfun)+            ) ->+              let x_ixfun' = IxFun.substituteInIxFun ctxids x_ixfun+                  y_ixfun' = IxFun.substituteInIxFun ctxexts y_ixfun+               in x_i == y_i && IxFun.closeEnough x_ixfun' y_ixfun'+          (_, Nothing) -> True+          _ -> False+    matches _ _ _ _ = False++    extInExpReturns :: ExpReturns -> S.Set Int+    extInExpReturns (MemArray _ shape _ mem_return) =+      extInShape shape <> maybe S.empty extInMemReturn mem_return+    extInExpReturns _ = mempty++extInShape :: ShapeBase (Ext SubExp) -> S.Set Int+extInShape shape = S.fromList $ mapMaybe isExt $ shapeDims shape++extInMemReturn :: MemReturn -> S.Set Int+extInMemReturn (ReturnsInBlock _ extixfn) = extInIxFn extixfn+extInMemReturn (ReturnsNewBlock _ i extixfn) =+  S.singleton i <> extInIxFn extixfn++extInIxFn :: ExtIxFun -> S.Set Int+extInIxFn ixfun = S.fromList $ concatMap (mapMaybe isExt . toList) ixfun++varMemInfo ::+  Mem lore =>+  VName ->+  TC.TypeM lore (MemInfo SubExp NoUniqueness MemBind)+varMemInfo name = do+  dec <- TC.lookupVar name++  case dec of+    LetName (_, summary) -> return summary+    FParamName summary -> return $ noUniquenessReturns summary+    LParamName summary -> return summary+    IndexName it -> return $ MemPrim $ IntType it++nameInfoToMemInfo :: Mem lore => NameInfo lore -> MemBound NoUniqueness+nameInfoToMemInfo info =+  case info of+    FParamName summary -> noUniquenessReturns summary+    LParamName summary -> summary+    LetName summary -> summary+    IndexName it -> MemPrim $ IntType it++lookupMemInfo ::+  (HasScope lore m, Mem lore) =>+  VName ->+  m (MemInfo SubExp NoUniqueness MemBind)+lookupMemInfo = fmap nameInfoToMemInfo . lookupInfo++subExpMemInfo ::+  (HasScope lore m, Monad m, Mem lore) =>+  SubExp ->+  m (MemInfo SubExp NoUniqueness MemBind)+subExpMemInfo (Var v) = lookupMemInfo v+subExpMemInfo (Constant v) = return $ MemPrim $ primValueType v++lookupArraySummary ::+  (Mem lore, HasScope lore m, Monad m) =>+  VName ->+  m (VName, IxFun.IxFun (TPrimExp Int64 VName))+lookupArraySummary name = do+  summary <- lookupMemInfo name+  case summary of+    MemArray _ _ _ (ArrayIn mem ixfun) ->+      return (mem, ixfun)+    _ ->+      error $ "Variable " ++ pretty name ++ " does not look like an array."++checkMemInfo ::+  TC.Checkable lore =>+  VName ->+  MemInfo SubExp u MemBind ->+  TC.TypeM lore ()+checkMemInfo _ (MemPrim _) = return ()+checkMemInfo _ (MemMem (ScalarSpace d _)) = mapM_ (TC.require [Prim int64]) d+checkMemInfo _ (MemMem _) = return ()+checkMemInfo name (MemArray _ shape _ (ArrayIn v ixfun)) = do+  t <- lookupType v+  case t of+    Mem {} ->+      return ()+    _ ->+      TC.bad $+        TC.TypeError $+          "Variable " ++ pretty v+            ++ " used as memory block, but is of type "+            ++ pretty t+            ++ "."++  TC.context ("in index function " ++ pretty ixfun) $ do+    traverse_ (TC.requirePrimExp int64 . untyped) ixfun+    let ixfun_rank = IxFun.rank ixfun+        ident_rank = shapeRank shape+    unless (ixfun_rank == ident_rank) $+      TC.bad $+        TC.TypeError $+          "Arity of index function (" ++ pretty ixfun_rank+            ++ ") does not match rank of array "+            ++ pretty name+            ++ " ("+            ++ show ident_rank+            ++ ")"++bodyReturnsFromPattern ::+  PatternT (MemBound NoUniqueness) ->+  ([(VName, BodyReturns)], [(VName, BodyReturns)])+bodyReturnsFromPattern pat =+  ( map asReturns $ patternContextElements pat,+    map asReturns $ patternValueElements pat+  )+  where+    ctx = patternContextElements pat++    ext (Var v)+      | Just (i, _) <- find ((== v) . patElemName . snd) $ zip [0 ..] ctx =+        Ext i+    ext se = Free se++    asReturns pe =+      ( patElemName pe,+        case patElemDec pe of+          MemPrim pt -> MemPrim pt+          MemMem space -> MemMem space+          MemArray pt shape u (ArrayIn mem ixfun) ->+            MemArray pt (Shape $ map ext $ shapeDims shape) u $+              case find ((== mem) . patElemName . snd) $ zip [0 ..] ctx of+                Just (i, PatElem _ (MemMem space)) ->+                  ReturnsNewBlock space i $+                    existentialiseIxFun (map patElemName ctx) ixfun+                _ -> ReturnsInBlock mem $ existentialiseIxFun [] ixfun+      )++instance (PP.Pretty u, PP.Pretty r) => PrettyAnnot (PatElemT (MemInfo SubExp u r)) where+  ppAnnot = bindeeAnnot patElemName patElemDec++instance (PP.Pretty u, PP.Pretty r) => PrettyAnnot (Param (MemInfo SubExp u r)) where+  ppAnnot = bindeeAnnot paramName paramDec++bindeeAnnot ::+  (PP.Pretty u, PP.Pretty r) =>+  (a -> VName) ->+  (a -> MemInfo SubExp u r) ->+  a ->+  Maybe PP.Doc+bindeeAnnot bindeeName bindeeLore bindee =+  case bindeeLore bindee of+    dec@MemArray {} ->+      Just $+        PP.stack $+          map (("-- " <>) . PP.text) $+            lines $+              pretty (PP.ppr (bindeeName bindee) PP.<+> ":" PP.<+> PP.ppr dec)+    MemMem {} ->+      Nothing+    MemPrim _ ->+      Nothing++extReturns :: [ExtType] -> [ExpReturns]+extReturns ts =+  evalState (mapM addDec ts) 0+  where+    addDec (Prim bt) =+      return $ MemPrim bt+    addDec (Mem space) =+      return $ MemMem space+    addDec t@(Array bt shape u)+      | existential t = do+        i <- get <* modify (+ 1)+        return $+          MemArray bt shape u $+            Just $+              ReturnsNewBlock DefaultSpace i $+                IxFun.iota $ map convert $ shapeDims shape+      | otherwise =+        return $ MemArray bt shape u Nothing+    convert (Ext i) = le64 (Ext i)+    convert (Free v) = Free <$> pe64 v++arrayVarReturns ::+  (HasScope lore m, Monad m, Mem lore) =>+  VName ->+  m (PrimType, Shape, VName, IxFun)+arrayVarReturns v = do+  summary <- lookupMemInfo v+  case summary of+    MemArray et shape _ (ArrayIn mem ixfun) ->+      return (et, Shape $ shapeDims shape, mem, ixfun)+    _ ->+      error $ "arrayVarReturns: " ++ pretty v ++ " is not an array."++varReturns ::+  (HasScope lore m, Monad m, Mem lore) =>+  VName ->+  m ExpReturns+varReturns v = do+  summary <- lookupMemInfo v+  case summary of+    MemPrim bt ->+      return $ MemPrim bt+    MemArray et shape _ (ArrayIn mem ixfun) ->+      return $+        MemArray et (fmap Free shape) NoUniqueness $+          Just $ ReturnsInBlock mem $ existentialiseIxFun [] ixfun+    MemMem space ->+      return $ MemMem space++-- | The return information of an expression.  This can be seen as the+-- "return type with memory annotations" of the expression.+expReturns ::+  ( Monad m,+    HasScope lore m,+    Mem lore+  ) =>+  Exp lore ->+  m [ExpReturns]+expReturns (BasicOp (SubExp (Var v))) =+  pure <$> varReturns v+expReturns (BasicOp (Opaque (Var v))) =+  pure <$> varReturns v+expReturns (BasicOp (Reshape newshape v)) = do+  (et, _, mem, ixfun) <- arrayVarReturns v+  return+    [ MemArray et (Shape $ map (Free . newDim) newshape) NoUniqueness $+        Just $+          ReturnsInBlock mem $+            existentialiseIxFun [] $+              IxFun.reshape ixfun $ map (fmap pe64) newshape+    ]+expReturns (BasicOp (Rearrange perm v)) = do+  (et, Shape dims, mem, ixfun) <- arrayVarReturns v+  let ixfun' = IxFun.permute ixfun perm+      dims' = rearrangeShape perm dims+  return+    [ MemArray et (Shape $ map Free dims') NoUniqueness $+        Just $ ReturnsInBlock mem $ existentialiseIxFun [] ixfun'+    ]+expReturns (BasicOp (Rotate offsets v)) = do+  (et, Shape dims, mem, ixfun) <- arrayVarReturns v+  let offsets' = map pe64 offsets+      ixfun' = IxFun.rotate ixfun offsets'+  return+    [ MemArray et (Shape $ map Free dims) NoUniqueness $+        Just $ ReturnsInBlock mem $ existentialiseIxFun [] ixfun'+    ]+expReturns (BasicOp (Index v slice)) = do+  info <- sliceInfo v slice+  case info of+    MemArray et shape u (ArrayIn mem ixfun) ->+      return+        [ MemArray et (fmap Free shape) u $+            Just $ ReturnsInBlock mem $ existentialiseIxFun [] ixfun+        ]+    MemPrim pt -> return [MemPrim pt]+    MemMem space -> return [MemMem space]+expReturns (BasicOp (Update v _ _)) =+  pure <$> varReturns v+expReturns (BasicOp op) =+  extReturns . staticShapes <$> primOpType op+expReturns e@(DoLoop ctx val _ _) = do+  t <- expExtType e+  zipWithM typeWithDec t $ map fst val+  where+    typeWithDec t p =+      case (t, paramDec p) of+        ( Array bt shape u,+          MemArray _ _ _ (ArrayIn mem ixfun)+          )+            | Just (i, mem_p) <- isMergeVar mem,+              Mem space <- paramType mem_p ->+              return $ MemArray bt shape u $ Just $ ReturnsNewBlock space i ixfun'+            | otherwise ->+              return+                ( MemArray bt shape u $+                    Just $ ReturnsInBlock mem ixfun'+                )+            where+              ixfun' = existentialiseIxFun (map paramName mergevars) ixfun+        (Array {}, _) ->+          error "expReturns: Array return type but not array merge variable."+        (Prim bt, _) ->+          return $ MemPrim bt+        (Mem {}, _) ->+          error "expReturns: loop returns memory block explicitly."+    isMergeVar v = find ((== v) . paramName . snd) $ zip [0 ..] mergevars+    mergevars = map fst $ ctx ++ val+expReturns (Apply _ _ ret _) =+  return $ map funReturnsToExpReturns ret+expReturns (If _ _ _ (IfDec ret _)) =+  return $ map bodyReturnsToExpReturns ret+expReturns (Op op) =+  opReturns op++sliceInfo ::+  (Monad m, HasScope lore m, Mem lore) =>+  VName ->+  Slice SubExp ->+  m (MemInfo SubExp NoUniqueness MemBind)+sliceInfo v slice = do+  (et, _, mem, ixfun) <- arrayVarReturns v+  case sliceDims slice of+    [] -> return $ MemPrim et+    dims ->+      return $+        MemArray et (Shape dims) NoUniqueness $+          ArrayIn mem $+            IxFun.slice+              ixfun+              (map (fmap (isInt64 . primExpFromSubExp int64)) slice)++class TypedOp (Op lore) => OpReturns lore where+  opReturns ::+    (Monad m, HasScope lore m) =>+    Op lore ->+    m [ExpReturns]+  opReturns op = extReturns <$> opType op++applyFunReturns ::+  Typed dec =>+  [FunReturns] ->+  [Param dec] ->+  [(SubExp, Type)] ->+  Maybe [FunReturns]+applyFunReturns rets params args+  | Just _ <- applyRetType rettype params args =+    Just $ map correctDims rets+  | otherwise =+    Nothing+  where+    rettype = map declExtTypeOf rets+    parammap :: M.Map VName (SubExp, Type)+    parammap =+      M.fromList $+        zip (map paramName params) args++    substSubExp (Var v)+      | Just (se, _) <- M.lookup v parammap = se+    substSubExp se = se++    correctDims (MemPrim t) =+      MemPrim t+    correctDims (MemMem space) =+      MemMem space+    correctDims (MemArray et shape u memsummary) =+      MemArray et (correctShape shape) u $+        correctSummary memsummary++    correctShape = Shape . map correctDim . shapeDims+    correctDim (Ext i) = Ext i+    correctDim (Free se) = Free $ substSubExp se++    correctSummary (ReturnsNewBlock space i ixfun) =+      ReturnsNewBlock space i ixfun+    correctSummary (ReturnsInBlock mem ixfun) =+      -- FIXME: we should also do a replacement in ixfun here.+      ReturnsInBlock mem' ixfun+      where+        mem' = case M.lookup mem parammap of+          Just (Var v, _) -> v+          _ -> mem
src/Futhark/IR/Mem/IxFun.hs view
@@ -1,828 +1,1066 @@-{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}--- | This module contains a representation for the index function based on--- linear-memory accessor descriptors; see Zhu, Hoeflinger and David work.-module Futhark.IR.Mem.IxFun-       ( IxFun(..)-       , index-       , iota-       , permute-       , rotate-       , reshape-       , slice-       , rebase-       , shape-       , rank-       , linearWithOffset-       , rearrangeWithOffset-       , isDirect-       , isLinear-       , substituteInIxFun-       , leastGeneralGeneralization-       , existentialize-       , closeEnough-       )-       where--import Prelude hiding (mod)-import Data.List (sort, sortBy, zip4, zip5, zipWith5)-import qualified Data.List.NonEmpty as NE-import Data.List.NonEmpty (NonEmpty(..))-import Data.Function (on)-import Data.Maybe (isJust)-import Control.Monad.Identity-import Control.Monad.State-import Control.Monad.Writer-import qualified Data.Map.Strict as M--import Futhark.Analysis.PrimExp (PrimExp(..), primExpType)-import Futhark.IR.Syntax.Core (Ext(..))-import Futhark.Transform.Substitute-import Futhark.Transform.Rename-import Futhark.IR.Syntax-  (ShapeChange, DimChange(..), DimIndex(..), Slice, unitSlice, dimFix)-import Futhark.IR.Prop-import Futhark.Util.IntegralExp-import Futhark.Util.Pretty-import Futhark.Analysis.PrimExp.Convert (substituteInPrimExp)-import qualified Futhark.Analysis.PrimExp.Generalize as PEG--type Shape num   = [num]-type Indices num = [num]-type Permutation = [Int]--data Monotonicity = Inc | Dec | Unknown-               -- ^ monotonously increasing, decreasing or unknown-             deriving (Show, Eq)--data LMADDim num = LMADDim { ldStride :: num-                           , ldRotate :: num-                           , ldShape :: num-                           , ldPerm :: Int-                           , ldMon :: Monotonicity-                           }-                 deriving (Show, Eq)---- | LMAD's representation consists of a general offset and for each dimension a--- stride, rotate factor, number of elements (or shape), permutation, and--- monotonicity. Note that the permutation is not strictly necessary in that the--- permutation can be performed directly on LMAD dimensions, but then it is--- difficult to extract the permutation back from an LMAD.------ LMAD algebra is closed under composition w.r.t. operators such as--- permute, index and slice.  However, other operations, such as--- reshape, cannot always be represented inside the LMAD algebra.------ It follows that the general representation of an index function is a list of--- LMADS, in which each following LMAD in the list implicitly corresponds to an--- irregular reshaping operation.------ However, we expect that the common case is when the index function is one--- LMAD -- we call this the "nice" representation.------ Finally, the list of LMADs is kept in an @IxFun@ together with the shape of--- the original array, and a bit to indicate whether the index function is--- contiguous, i.e., if we instantiate all the points of the current index--- function, do we get a contiguous memory interval?------ By definition, the LMAD denotes the set of points (simplified):------   \{ o + \Sigma_{j=0}^{k} ((i_j+r_j) `mod` n_j)*s_j,---      \forall i_j such that 0<=i_j<n_j, j=1..k \}-data LMAD num = LMAD { lmadOffset :: num-                     , lmadDims :: [LMADDim num]-                     }-                deriving (Show, Eq)---- | An index function is a mapping from a multidimensional array--- index space (the domain) to a one-dimensional memory index space.--- Essentially, it explains where the element at position @[i,j,p]@ of--- some array is stored inside the flat one-dimensional array that--- constitutes its memory.  For example, we can use this to--- distinguish row-major and column-major representations.------ An index function is represented as a sequence of 'LMAD's.-data IxFun num = IxFun { ixfunLMADs :: NonEmpty (LMAD num)-                       , base :: Shape num-                       , ixfunContig :: Bool-                       -- ^ ignoring permutations, is the index function contiguous?-                       }-                 deriving (Show, Eq)---instance Pretty Monotonicity where-  ppr = text . show--instance Pretty num => Pretty (LMAD num) where-  ppr (LMAD offset dims) =-    braces $ semisep [ text "offset: " <> oneLine (ppr offset)-                     , text "strides: " <> p ldStride-                     , text "rotates: " <> p ldRotate-                     , text "shape: " <> p ldShape-                     , text "permutation: " <> p ldPerm-                     , text "monotonicity: " <> p ldMon-                     ]-    where p f = oneLine $ brackets $ commasep $ map (ppr . f) dims--instance Pretty num => Pretty (IxFun num) where-  ppr (IxFun lmads oshp cg) =-    braces $ semisep [ text "base: " <> brackets (commasep $ map ppr oshp)-                     , text "contiguous: " <> text (show cg)-                     , text "LMADs: " <> brackets (commasep $ NE.toList $ NE.map ppr lmads)-                     ]---instance Substitute num => Substitute (LMAD num) where-  substituteNames substs = fmap $ substituteNames substs--instance Substitute num => Substitute (IxFun num) where-  substituteNames substs = fmap $ substituteNames substs--instance Substitute num => Rename (LMAD num) where-  rename = substituteRename--instance Substitute num => Rename (IxFun num) where-  rename = substituteRename---instance FreeIn num => FreeIn (LMAD num) where-  freeIn' = foldMap freeIn'--instance FreeIn num => FreeIn (IxFun num) where-  freeIn' = foldMap freeIn'--instance Functor LMAD where-  fmap f = runIdentity . traverse (return . f)--instance Functor IxFun where-  fmap f = runIdentity . traverse (return . f)---instance Foldable LMAD where-  foldMap f = execWriter . traverse (tell . f)--instance Foldable IxFun where-  foldMap f = execWriter . traverse (tell . f)---instance Traversable LMAD where-  traverse f (LMAD offset dims) =-    LMAD <$> f offset <*> traverse f' dims-    where f' (LMADDim s r n p m) =-             LMADDim <$> f s <*> f r <*> f n <*> pure p <*> pure m--instance Traversable IxFun where-  traverse f (IxFun lmads oshp cg) =-    IxFun  <$> traverse (traverse f) lmads <*> traverse f oshp <*> pure cg--(++@) :: [a] -> NonEmpty a -> NonEmpty a-es ++@ (ne :| nes) = case es of-  e : es' -> e :| es' ++ [ne] ++ nes-  [] -> ne :| nes--(@++@) :: NonEmpty a -> NonEmpty a -> NonEmpty a-(x :| xs) @++@ (y :| ys) = x :| xs ++ [y] ++ ys--invertMonotonicity :: Monotonicity -> Monotonicity-invertMonotonicity Inc = Dec-invertMonotonicity Dec = Inc-invertMonotonicity Unknown = Unknown--lmadPermutation :: LMAD num -> Permutation-lmadPermutation = map ldPerm . lmadDims--setLMADPermutation :: Permutation -> LMAD num -> LMAD num-setLMADPermutation perm lmad =-  lmad { lmadDims = zipWith (\dim p -> dim { ldPerm = p }) (lmadDims lmad) perm }--setLMADShape :: Shape num -> LMAD num -> LMAD num-setLMADShape shp lmad = lmad { lmadDims = zipWith (\dim s -> dim { ldShape = s }) (lmadDims lmad) shp }---- | Substitute a name with a PrimExp in an LMAD.-substituteInLMAD :: Ord a => M.Map a (PrimExp a) -> LMAD (PrimExp a)-                 -> LMAD (PrimExp a)-substituteInLMAD tab (LMAD offset dims) =-  let offset' = substituteInPrimExp tab offset-      dims' = map (\(LMADDim s r n p m) ->-                     LMADDim-                     (substituteInPrimExp tab s)-                     (substituteInPrimExp tab r)-                     (substituteInPrimExp tab n)-                     p m)-              dims-  in LMAD offset' dims'---- | Substitute a name with a PrimExp in an index function.-substituteInIxFun :: (Ord a) => M.Map a (PrimExp a) -> IxFun (PrimExp a)-                  -> IxFun (PrimExp a)-substituteInIxFun tab (IxFun lmads oshp cg) =-  IxFun (NE.map (substituteInLMAD tab) lmads)-        (map (substituteInPrimExp tab) oshp)-        cg---- | Is this is a row-major array?-isDirect :: (Eq num, IntegralExp num) => IxFun num -> Bool-isDirect ixfun@(IxFun (LMAD offset dims :| []) oshp True) =-  let strides_expected = reverse $ scanl (*) 1 (reverse (tail oshp))-  in hasContiguousPerm ixfun &&-     length oshp == length dims &&-     offset == 0 &&-     all (\(LMADDim s r n p _, m, d, se) ->-            s == se && r == 0 && n == d && p == m)-     (zip4 dims [0..length dims - 1] oshp strides_expected)-isDirect _ = False---- | Does the index function have an ascending permutation?-hasContiguousPerm :: IxFun num -> Bool-hasContiguousPerm (IxFun (lmad :| []) _ _) =-  let perm = lmadPermutation lmad-  in perm == sort perm-hasContiguousPerm _ = False---- | Shape of an index function.-shape :: (Eq num, IntegralExp num) => IxFun num -> Shape num-shape (IxFun (lmad :| _) _ _) = lmadShape lmad---- | Shape of an LMAD.-lmadShape :: (Eq num, IntegralExp num) => LMAD num -> Shape num-lmadShape lmad = permuteInv (lmadPermutation lmad) $ lmadShapeBase lmad---- | Shape of an LMAD, ignoring permutations.-lmadShapeBase :: (Eq num, IntegralExp num) => LMAD num -> Shape num-lmadShapeBase = map ldShape . lmadDims---- | Compute the flat memory index for a complete set @inds@ of array indices--- and a certain element size @elem_size@.-index :: (IntegralExp num, Eq num) =>-          IxFun num -> Indices num -> num-index = indexFromLMADs . ixfunLMADs-  where indexFromLMADs :: (IntegralExp num, Eq num) =>-                          NonEmpty (LMAD num) -> Indices num -> num-        indexFromLMADs (lmad :| []) inds = indexLMAD lmad inds-        indexFromLMADs (lmad1 :| lmad2 : lmads) inds =-          let i_flat   = indexLMAD lmad1 inds-              new_inds = unflattenIndex (permuteFwd (lmadPermutation lmad2) $ lmadShapeBase lmad2) i_flat-          in indexFromLMADs (lmad2 :| lmads) new_inds--        -- | Compute the flat index of an LMAD.-        indexLMAD :: (IntegralExp num, Eq num) =>-                     LMAD num -> Indices num -> num-        indexLMAD lmad@(LMAD off dims) inds =-          let prod = sum $ zipWith flatOneDim-                             (map (\(LMADDim s r n _ _) -> (s, r, n)) dims)-                             (permuteInv (lmadPermutation lmad) inds)-          in off + prod---- | iota.-iota :: IntegralExp num => Shape num -> IxFun num-iota ns =-  let rs = replicate (length ns) 0-  in IxFun (makeRotIota Inc 0 (zip rs ns) :| []) ns True---- | Permute dimensions.-permute :: IntegralExp num =>-           IxFun num -> Permutation -> IxFun num-permute (IxFun (lmad :| lmads) oshp cg) perm_new =-  let perm_cur = lmadPermutation lmad-      perm = map (perm_cur !!) perm_new-  in IxFun (setLMADPermutation perm lmad :| lmads) oshp cg---- | Rotate an index function.-rotate :: (Eq num, IntegralExp num) =>-          IxFun num -> Indices num -> IxFun num-rotate  (IxFun (lmad@(LMAD off dims) :| lmads) oshp cg) offs =-  let dims' = zipWith (\(LMADDim s r n p f) o ->-                          if s == 0 then LMADDim 0 0 n p Unknown-                          else LMADDim s (r + o) n p f-                      ) dims (permuteInv (lmadPermutation lmad) offs)-  in IxFun (LMAD off dims' :| lmads) oshp cg---- | Handle the case where a slice can stay within a single LMAD.-sliceOneLMAD :: (Eq num, IntegralExp num) =>-                IxFun num -> Slice num -> Maybe (IxFun num)-sliceOneLMAD (IxFun (lmad@(LMAD _ ldims) :| lmads) oshp cg) is = do-  let perm = lmadPermutation lmad-      is' = permuteInv perm is-      cg' = cg && slicePreservesContiguous lmad is'-  guard $ harmlessRotation lmad is'-  let lmad' = foldl sliceOne (LMAD (lmadOffset lmad) []) $ zip is' ldims-      -- need to remove the fixed dims from the permutation-      perm' = updatePerm perm $ map fst $ filter (isJust . dimFix . snd) $-              zip [0..length is' - 1] is'--  return $ IxFun (setLMADPermutation perm' lmad' :| lmads) oshp cg'-  where updatePerm ps inds = foldl (\acc p -> acc ++ decrease p) [] ps-          where decrease p =-                  let d = foldl (\n i -> if i == p then -1-                                         else if i > p-                                              then n-                                              else if n /= -1 then n + 1-                                                   else n-                                ) 0 inds-                  in [p - d | d /= -1]--        harmlessRotation' :: (Eq num, IntegralExp num) =>-                             LMADDim num -> DimIndex num -> Bool-        harmlessRotation' _ (DimFix _)   = True-        harmlessRotation' (LMADDim 0 _ _ _ _) _  = True-        harmlessRotation' (LMADDim _ 0 _ _ _) _  = True-        harmlessRotation' (LMADDim _ _ n _ _) dslc-            | dslc == DimSlice (n - 1) n (-1) ||-              dslc == unitSlice 0 n      = True-        harmlessRotation' _ _            = False--        harmlessRotation :: (Eq num, IntegralExp num) =>-                             LMAD num -> Slice num -> Bool-        harmlessRotation (LMAD _ dims) iss =-            and $ zipWith harmlessRotation' dims iss--        -- XXX: TODO: what happens to r on a negative-stride slice; is there-        -- such a case?-        sliceOne :: (Eq num, IntegralExp num) =>-                    LMAD num -> (DimIndex num, LMADDim num) -> LMAD num-        sliceOne (LMAD off dims) (DimFix i, LMADDim s r n _ _) =-            LMAD (off + flatOneDim (s, r, n) i) dims-        sliceOne (LMAD off dims) (DimSlice _ ne _, LMADDim 0 _ _ p _) =-            LMAD off (dims ++ [LMADDim 0 0 ne p Unknown])-        sliceOne (LMAD off dims) (dmind, dim@(LMADDim _ _ n _ _))-            | dmind == unitSlice 0 n = LMAD off (dims ++ [dim])-        sliceOne (LMAD off dims) (dmind, LMADDim s r n p m)-            | dmind == DimSlice (n - 1) n (-1) =-              let r' = if r == 0 then 0 else n - r-                  off' = off + flatOneDim (s, 0, n) (n - 1)-              in  LMAD off' (dims ++ [LMADDim (s * (-1)) r' n p (invertMonotonicity m)])-        sliceOne (LMAD off dims) (DimSlice b ne 0, LMADDim s r n p _) =-            LMAD (off + flatOneDim (s, r, n) b) (dims ++ [LMADDim 0 0 ne p Unknown])-        sliceOne (LMAD off dims) (DimSlice bs ns ss, LMADDim s 0 _ p m) =-            let m' = case sgn ss of-                       Just 1    -> m-                       Just (-1) -> invertMonotonicity m-                       _         -> Unknown-            in  LMAD (off + s * bs) (dims ++ [LMADDim (ss * s) 0 ns p m'])-        sliceOne _ _ = error "slice: reached impossible case"--        slicePreservesContiguous :: (Eq num, IntegralExp num) =>-                                    LMAD num -> Slice num -> Bool-        slicePreservesContiguous (LMAD _ dims) slc =-          -- remove from the slice the LMAD dimensions that have stride 0.-          -- If the LMAD was contiguous in mem, then these dims will not-          -- influence the contiguousness of the result.-          -- Also normalize the input slice, i.e., 0-stride and size-1-          -- slices are rewritten as DimFixed.-          let (dims', slc') = unzip $-                filter ((/= 0) . ldStride . fst) $-                       zip dims $ map normIndex slc-              -- Check that:-              -- 1. a clean split point exists between Fixed and Sliced dims-              -- 2. the outermost sliced dim has +/- 1 stride AND is unrotated or full.-              -- 3. the rest of inner sliced dims are full.-              (_, success) =-                foldl (\(found, res) (slcdim, LMADDim _ r n _ _) ->-                        case (slcdim, found) of-                          (DimFix{},   True ) -> (found, False)-                          (DimFix{},   False) -> (found, res)-                          (DimSlice _ ne ds, False) -> -- outermost sliced dim: +/-1 stride-                            let res' = (r == 0 || n == ne) && (ds == 1 || ds == -1)-                            in (True, res && res')-                          (DimSlice _ ne ds, True) ->  -- inner sliced dim: needs to be full-                            let res' = (n == ne) && (ds == 1 || ds == -1)-                            in (found, res && res')-                      ) (False, True) $ zip slc' dims'-          in success--        normIndex :: (Eq num, IntegralExp num) =>-                     DimIndex num -> DimIndex num-        normIndex (DimSlice b 1 _) = DimFix b-        normIndex (DimSlice b _ 0) = DimFix b-        normIndex d = d---- | Slice an index function.-slice :: (Eq num, IntegralExp num) =>-         IxFun num -> Slice num -> IxFun num-slice _ [] = error "slice: empty slice"-slice ixfun@(IxFun (lmad@(LMAD _ _) :| lmads) oshp cg) dim_slices-  -- Avoid identity slicing.-  | dim_slices == map (unitSlice 0) (shape ixfun) = ixfun-  | Just ixfun' <- sliceOneLMAD ixfun dim_slices = ixfun'-  | otherwise =-    case sliceOneLMAD (iota (lmadShape lmad)) dim_slices of-      Just (IxFun (lmad' :| []) _ cg') ->-        IxFun (lmad' :| lmad : lmads) oshp (cg && cg')-      _ -> error "slice: reached impossible case"---- | Handle the simple case where all reshape dimensions are coercions.-reshapeCoercion :: (Eq num, IntegralExp num) =>-                   IxFun num -> ShapeChange num -> Maybe (IxFun num)-reshapeCoercion (IxFun (lmad@(LMAD off dims) :| lmads) _ cg) newshape = do-  let perm = lmadPermutation lmad-  (head_coercions, reshapes, tail_coercions) <- splitCoercions newshape-  let hd_len = length head_coercions-      num_coercions = hd_len + length tail_coercions-      dims' = permuteFwd perm dims-      mid_dims = take (length dims - num_coercions) $ drop hd_len dims'-      num_rshps = length reshapes-  guard (num_rshps == 0 || (num_rshps == 1 && length mid_dims == 1))-  let dims'' = permuteInv perm $-               zipWith (\ld n -> ld { ldShape = n })-               dims' (newDims newshape)-      lmad' = LMAD off dims''-  return $ IxFun (lmad' :| lmads) (newDims newshape) cg---- | Handle the case where a reshape operation can stay inside a single LMAD.------ There are four conditions that all must hold for the result of a reshape--- operation to remain in the one-LMAD domain:------   (1) the permutation of the underlying LMAD must leave unchanged---       the LMAD dimensions that were *not* reshape coercions.---   (2) the repetition of dimensions of the underlying LMAD must---       refer only to the coerced-dimensions of the reshape operation.---   (3) similarly, the rotated dimensions must refer only to---       dimensions that are coerced by the reshape operation.---   (4) finally, the underlying memory is contiguous (and monotonous).------ If any of these conditions do not hold, then the reshape operation will--- conservatively add a new LMAD to the list, leading to a representation that--- provides less opportunities for further analysis.-reshapeOneLMAD :: (Eq num, IntegralExp num) =>-                   IxFun num -> ShapeChange num -> Maybe (IxFun num)-reshapeOneLMAD ixfun@(IxFun (lmad@(LMAD off dims) :| lmads) _ cg) newshape = do-  let perm = lmadPermutation lmad-  (head_coercions, reshapes, tail_coercions) <- splitCoercions newshape-  let hd_len = length head_coercions-      num_coercions = hd_len + length tail_coercions-      dims_perm = permuteFwd perm dims-      mid_dims = take (length dims - num_coercions) $ drop hd_len dims_perm-      -- Ignore rotates, as we only care about not having rotates in the-      -- dimensions that aren't coercions (@mid_dims@), which we check-      -- separately.-      mon = ixfunMonotonicityRots True ixfun--  guard $-    -- checking conditions (2) and (3)-    all (\ (LMADDim s r _ _ _) -> s /= 0 && r == 0) mid_dims &&-    -- checking condition (1)-    consecutive hd_len (map ldPerm mid_dims) &&-    -- checking condition (4)-    hasContiguousPerm ixfun && cg && (mon == Inc || mon == Dec)--  -- make new permutation-  let rsh_len = length reshapes-      diff = length newshape - length dims-      iota_shape = [0..length newshape-1]-      perm' = map (\i -> let ind = if i < hd_len-                                   then i else i - diff-                         in if (i >= hd_len) && (i < hd_len + rsh_len)-                            then i -- already checked mid_dims not affected-                            else let p = ldPerm (dims !! ind)-                                 in if p < hd_len-                                    then p-                                    else p + diff-                  ) iota_shape-      -- split the dimensions-      (support_inds, repeat_inds) =-        foldl (\(sup, rpt) (i, shpdim, ip) ->-                case (i < hd_len, i >= hd_len + rsh_len, shpdim) of-                  (True,  _, DimCoercion n) ->-                    case dims_perm !! i of-                      (LMADDim 0 _ _ _ _) -> ( sup, (ip, n) : rpt )-                      (LMADDim _ r _ _ _) -> ( (ip, (r, n)) : sup, rpt )-                  (_,  True, DimCoercion n) ->-                    case dims_perm !! (i-diff) of-                      (LMADDim 0 _ _ _ _) -> ( sup, (ip, n) : rpt )-                      (LMADDim _ r _ _ _) -> ( (ip, (r, n)) : sup, rpt )-                  (False, False, _) ->-                      ( (ip, (0, newDim shpdim)) : sup, rpt )-                      -- already checked that the reshaped-                      -- dims cannot be rotates-                  _ -> error "reshape: reached impossible case"-              ) ([], []) $ reverse $ zip3 iota_shape newshape perm'--      (sup_inds, support) = unzip $ sortBy (compare `on` fst) support_inds-      (rpt_inds, repeats) = unzip repeat_inds-      LMAD off' dims_sup = makeRotIota mon off support-      repeats' = map (\n -> LMADDim 0 0 n 0 Unknown) repeats-      dims' = map snd $ sortBy (compare `on` fst)-              $ zip sup_inds dims_sup ++ zip rpt_inds repeats'-      lmad' = LMAD off' dims'-  return $ IxFun (setLMADPermutation perm' lmad' :| lmads) (newDims newshape) cg-  where consecutive _ [] = True-        consecutive i [p]= i == p-        consecutive i ps = and $ zipWith (==) ps [i, i+1..]--splitCoercions :: (Eq num, IntegralExp num) =>-                  ShapeChange num -> Maybe (ShapeChange num, ShapeChange num, ShapeChange num)-splitCoercions newshape' = do-  let (head_coercions, newshape'') = span isCoercion newshape'-      (reshapes, tail_coercions) = break isCoercion newshape''-  guard (all isCoercion tail_coercions)-  return (head_coercions, reshapes, tail_coercions)-  where isCoercion DimCoercion{} = True-        isCoercion _ = False---- | Reshape an index function.-reshape :: (Eq num, IntegralExp num) =>-           IxFun num -> ShapeChange num -> IxFun num-reshape ixfun new_shape-  | Just ixfun' <- reshapeCoercion ixfun new_shape = ixfun'-  | Just ixfun' <- reshapeOneLMAD ixfun new_shape = ixfun'-reshape (IxFun (lmad0 :| lmad0s) oshp cg) new_shape =-  case iota (newDims new_shape) of-    IxFun (lmad :| []) _ _ -> IxFun (lmad :| lmad0 : lmad0s) oshp cg-    _ -> error "reshape: reached impossible case"---- | The number of dimensions in the domain of the input function.-rank :: IntegralExp num =>-        IxFun num -> Int-rank (IxFun (LMAD _ sss :| _) _ _) = length sss---- | Handle the case where a rebase operation can stay within m + n - 1 LMADs,--- where m is the number of LMADs in the index function, and n is the number of--- LMADs in the new base.  If both index function have only on LMAD, this means--- that we stay within the single-LMAD domain.------ We can often stay in that domain if the original ixfun is essentially a--- slice, e.g. `x[i, (k1,m,s1), (k2,n,s2)] = orig`.------ XXX: TODO: handle repetitions in both lmads.------ How to handle repeated dimensions in the original?------   (a) Shave them off of the last lmad of original---   (b) Compose the result from (a) with the first---       lmad of the new base---   (c) apply a repeat operation on the result of (b).------ However, I strongly suspect that for in-place update what we need is actually--- the INVERSE of the rebase function, i.e., given an index function new-base--- and another one orig, compute the index function ixfun0 such that:------   new-base == rebase ixfun0 ixfun, or equivalently:---   new-base == ixfun o ixfun0------ because then I can go bottom up and compose with ixfun0 all the index--- functions corresponding to the memory block associated with ixfun.-rebaseNice :: (Eq num, IntegralExp num) =>-              IxFun num -> IxFun num -> Maybe (IxFun num)-rebaseNice-  new_base@(IxFun (lmad_base :| lmads_base) _ cg_base)-  ixfun@(IxFun lmads shp cg) = do-  let (lmad :| lmads') = NE.reverse lmads-      dims = lmadDims lmad-      perm = lmadPermutation lmad-      perm_base = lmadPermutation lmad_base--  guard $-    -- Core rebase condition.-    base ixfun == shape new_base-    -- Conservative safety conditions: ixfun is contiguous and has known-    -- monotonicity for all dimensions.-    && cg && all ((/= Unknown) . ldMon) dims-    -- XXX: We should be able to handle some basic cases where both index-    -- functions have non-trivial permutations.-    && (hasContiguousPerm ixfun || hasContiguousPerm new_base)-    -- We need the permutations to be of the same size if we want to compose-    -- them.  They don't have to be of the same size if the ixfun has a trivial-    -- permutation.  Supporting this latter case allows us to rebase when ixfun-    -- has been created by slicing with fixed dimensions.-    && (length perm == length perm_base || hasContiguousPerm ixfun)-    -- To not have to worry about ixfun having non-1 strides, we also check that-    -- it is a row-major array (modulo permutation, which is handled-    -- separately).  Accept a non-full innermost dimension.  XXX: Maybe this can-    -- be less conservative?-    && and (zipWith3 (\sn ld inner -> sn == ldShape ld || (inner && ldStride ld == 1))-            shp dims (replicate (length dims - 1) False ++ [True]))--  -- Compose permutations, reverse strides and adjust offset if necessary.-  let perm_base' = if hasContiguousPerm ixfun-                   then perm_base-                   else map (perm !!) perm_base-      lmad_base' = setLMADPermutation perm_base' lmad_base-      dims_base = lmadDims lmad_base'-      n_fewer_dims = length dims_base - length dims-      (dims_base', offs_contrib) = unzip $-        zipWith (\(LMADDim s1 r1 n1 p1 _) (LMADDim _ r2 _ _ m2) ->-                   let (s', off') | m2 == Inc = (s1, 0)-                                  | otherwise = (s1 * (-1), s1 * (n1 - 1))-                       r' | m2 == Inc = if r2 == 0 then r1 else r1 + r2-                          | r1 == 0 = r2-                          | r2 == 0 = n1 - r1-                          | otherwise = n1 - r1 + r2-                   in (LMADDim s' r' n1 (p1 - n_fewer_dims) Inc, off'))-        -- If @dims@ is morally a slice, it might have fewer dimensions than-        -- @dims_base@.  Drop extraneous outer dimensions.-        (drop n_fewer_dims dims_base) dims-      off_base = lmadOffset lmad_base' + sum offs_contrib-      lmad_base''-        | lmadOffset lmad == 0 = LMAD off_base dims_base'-        | otherwise =-            -- If the innermost dimension of the ixfun was not full (but still-            -- had a stride of 1), add its offset relative to the new base.-            setLMADShape (lmadShape lmad)-            (LMAD (off_base + ldStride (last dims_base) * lmadOffset lmad)-             dims_base')-      new_base' = IxFun (lmad_base'' :| lmads_base) shp cg_base-      IxFun lmads_base' _ _ = new_base'-      lmads'' = lmads' ++@ lmads_base'-  return $ IxFun lmads'' shp (cg && cg_base)---- | Rebase an index function on top of a new base.-rebase :: (Eq num, IntegralExp num) =>-          IxFun num -> IxFun num -> IxFun num-rebase new_base@(IxFun lmads_base shp_base cg_base) ixfun@(IxFun lmads shp cg)-  | Just ixfun' <- rebaseNice new_base ixfun = ixfun'-  -- In the general case just concatenate LMADs since this refers to index-  -- function composition, which is always safe.-  | otherwise =-      let (lmads_base', shp_base') =-            if base ixfun == shape new_base-            then (lmads_base, shp_base)-            else let IxFun lmads' shp_base'' _ = reshape new_base $ map DimCoercion shp-                 in (lmads', shp_base'')-      in IxFun (lmads @++@ lmads_base') shp_base' (cg && cg_base)--ixfunMonotonicity :: (Eq num, IntegralExp num) => IxFun num -> Monotonicity-ixfunMonotonicity = ixfunMonotonicityRots False---- | If the memory support of the index function is contiguous and row-major--- (i.e., no transpositions, repetitions, rotates, etc.), then this should--- return the offset from which the memory-support of this index function--- starts.-linearWithOffset :: (Eq num, IntegralExp num) =>-                    IxFun num -> num -> Maybe num-linearWithOffset ixfun@(IxFun (lmad :| []) _ cg) elem_size-  | hasContiguousPerm ixfun && cg && ixfunMonotonicity ixfun == Inc =-    Just $ lmadOffset lmad * elem_size-linearWithOffset _ _ = Nothing---- | Similar restrictions to @linearWithOffset@ except for transpositions, which--- are returned together with the offset.-rearrangeWithOffset :: (Eq num, IntegralExp num) =>-                       IxFun num -> num -> Maybe (num, [(Int,num)])-rearrangeWithOffset (IxFun (lmad :| []) oshp cg) elem_size = do-  -- Note that @cg@ describes whether the index function is-  -- contiguous, *ignoring permutations*.  This function requires that-  -- functionality.-  let perm = lmadPermutation lmad-      perm_contig = [0..length perm-1]-  offset <- linearWithOffset-            (IxFun (setLMADPermutation perm_contig lmad :| []) oshp cg) elem_size-  return (offset, zip perm (permuteFwd perm (lmadShapeBase lmad)))-rearrangeWithOffset _ _ = Nothing---- | Is this a row-major array starting at offset zero?-isLinear :: (Eq num, IntegralExp num) => IxFun num -> Bool-isLinear = (== Just 0) . flip linearWithOffset 1--permuteFwd :: Permutation -> [a] -> [a]-permuteFwd ps elems = map (elems !!) ps--permuteInv :: Permutation -> [a] -> [a]-permuteInv ps elems = map snd $ sortBy (compare `on` fst) $ zip ps elems--flatOneDim :: (Eq num, IntegralExp num) =>-              (num, num, num) -> num -> num-flatOneDim (s, r, n) i-  | s == 0 = 0-  | r == 0 = i * s-  | otherwise = ((i + r) `mod` n) * s---- | Generalised iota with user-specified offset and strides.-makeRotIota :: IntegralExp num =>-               Monotonicity -> num -> [(num, num)] -> LMAD num-makeRotIota mon off support-  | mon == Inc || mon == Dec =-    let rk = length support-        (rs, ns) = unzip support-        ss0 = reverse $ take rk $ scanl (*) 1 $ reverse ns-        ss = if mon == Inc-             then ss0-             else map (* (-1)) ss0-        ps = map fromIntegral [0..rk-1]-        fi = replicate rk mon-    in LMAD off $ zipWith5 LMADDim ss rs ns ps fi-  | otherwise = error "makeRotIota: requires Inc or Dec"---- | Check monotonicity of an index function.-ixfunMonotonicityRots :: (Eq num, IntegralExp num) =>-                         Bool -> IxFun num -> Monotonicity-ixfunMonotonicityRots ignore_rots (IxFun (lmad :| lmads) _ _) =-  let mon0 = lmadMonotonicityRots lmad-  in if all ((== mon0) . lmadMonotonicityRots) lmads-     then mon0-     else Unknown-  where lmadMonotonicityRots :: (Eq num, IntegralExp num) =>-                                LMAD num -> Monotonicity-        lmadMonotonicityRots (LMAD _ dims)-          | all (isMonDim Inc) dims = Inc-          | all (isMonDim Dec) dims = Dec-          | otherwise = Unknown--        isMonDim :: (Eq num, IntegralExp num) =>-                    Monotonicity -> LMADDim num -> Bool-        isMonDim mon (LMADDim s r _ _ ldmon) =-          s == 0 || ((ignore_rots || r == 0) && mon == ldmon)---- | Generalization (anti-unification)------ Anti-unification of two index functions is supported under the following conditions:---   0. Both index functions are represented by ONE lmad (assumed common case!)---   1. The support array of the two indexfuns have the same dimensionality---      (we can relax this condition if we use a 1D support, as we probably should!)---   2. The contiguous property and the per-dimension monotonicity are the same---      (otherwise we might loose important information; this can be relaxed!)---   3. Most importantly, both index functions correspond to the same permutation---      (since the permutation is represented by INTs, this restriction cannot---       be relaxed, unless we move to a gated-LMAD representation!)-leastGeneralGeneralization :: Eq v => IxFun (PrimExp v) -> IxFun (PrimExp v) ->-                              Maybe (IxFun (PrimExp (Ext v)), [(PrimExp v, PrimExp v)])-leastGeneralGeneralization (IxFun (lmad1 :| []) oshp1 ctg1) (IxFun (lmad2 :| []) oshp2 ctg2) = do-  guard $-    length oshp1 == length oshp2 &&-    ctg1 == ctg2 &&-    map ldPerm (lmadDims lmad1) == map ldPerm (lmadDims lmad2) &&-    lmadDMon lmad1 == lmadDMon lmad2-  let (ctg, dperm, dmon) = (ctg1, lmadPermutation lmad1, lmadDMon lmad1)-  (dshp, m1) <- generalize [] (lmadDShp lmad1) (lmadDShp lmad2)-  (oshp, m2) <- generalize m1 oshp1 oshp2-  (dstd, m3) <- generalize m2 (lmadDSrd lmad1) (lmadDSrd lmad2)-  (drot, m4) <- generalize m3 (lmadDRot lmad1) (lmadDRot lmad2)-  let (offt, m5) = PEG.leastGeneralGeneralization m4 (lmadOffset lmad1) (lmadOffset lmad2)-  let lmad_dims = map (\(a,b,c,d,e) -> LMADDim a b c d e) $-        zip5 dstd drot dshp dperm dmon-      lmad = LMAD offt lmad_dims-  return (IxFun (lmad :| []) oshp ctg, m5)-  where lmadDMon = map ldMon    . lmadDims-        lmadDSrd = map ldStride . lmadDims-        lmadDShp = map ldShape  . lmadDims-        lmadDRot = map ldRotate . lmadDims-        generalize m l1 l2 =-          foldM (\(l_acc, m') (pe1,pe2) -> do-                    let (e, m'') = PEG.leastGeneralGeneralization m' pe1 pe2-                    return (l_acc++[e], m'')-                ) ([], m) (zip l1 l2)-leastGeneralGeneralization _ _ = Nothing--isSequential :: [Int] -> Bool-isSequential xs =-  all (uncurry (==)) $ zip xs [0..]--existentializeExp :: PrimExp v -> State [PrimExp v] (PrimExp (Ext v))-existentializeExp e = do-  i <- gets length-  modify (++ [e])-  let t = primExpType e-  return $ LeafExp (Ext i) t---- We require that there's only one lmad, and that the index function is contiguous, and the base shape has only one dimension-existentialize :: (Eq v, Pretty v) =>-                  IxFun (PrimExp v) -> State [PrimExp v] (Maybe (IxFun (PrimExp (Ext v))))-existentialize (IxFun (lmad :| []) oshp True)-  | all ((== 0) . ldRotate) (lmadDims lmad),-    length (lmadShape lmad) == length oshp,-    isSequential (map ldPerm $ lmadDims lmad) = do-      oshp' <- mapM existentializeExp oshp-      lmadOffset' <- existentializeExp $ lmadOffset lmad-      lmadDims' <- mapM existentializeLMADDim $ lmadDims lmad-      let lmad' = LMAD lmadOffset' lmadDims'-      return $ Just $ IxFun (lmad' :| []) oshp' True-        where-          existentializeLMADDim :: LMADDim (PrimExp v) -> State [PrimExp v] (LMADDim (PrimExp (Ext v)))-          existentializeLMADDim (LMADDim str rot shp perm mon) = do-            stride' <- existentializeExp str-            shape' <- existentializeExp shp-            return $ LMADDim stride' (fmap Free rot) shape' perm mon--    -- oshp' = LeafExp (Ext 0)-    -- lmad' = LMAD lmadOffset' lmadDims'-    -- lmadOffset' = LeafExp (Ext 1)-    -- (_, lmadDims', lmadDimSubsts) = foldr generalizeDim (2, [], []) $ lmadDims lmad-    -- substs = oshp : lmadOffset lmad' : lmadDimSubsts--    -- generalizeDim :: (Int, [LMADDim num]) -> LMADDim num -> (Int, [LMADDim num])-    -- generalizeDim (i, acc) (LMADDim stride rotate shape perm mon) =-    --   (i + 3,-    --    LMADDim (LeafExp $ Ext i) (LeafExp $ Ext $ i + 1) (LeafExp $ Ext $ i + 2) perm mon,-    --    [stride, rotate, shape])-existentialize _ = return Nothing---- | When comparing index functions as part of the type check in KernelsMem,--- we may run into problems caused by the simplifier. As index functions can be--- generalized over if-then-else expressions, the simplifier might hoist some of--- the code from inside the if-then-else (computing the offset of an array, for--- instance), but now the type checker cannot verify that the generalized index--- function is valid, because some of the existentials are computed somewhere--- else. To Work around this, we've had to relax the KernelsMem type-checker--- a bit, specifically, we've introduced this function to verify whether two--- index functions are "close enough" that we can assume that they match. We use--- this instead of `ixfun1 == ixfun2` and hope that it's good enough.-closeEnough :: IxFun num -> IxFun num -> Bool-closeEnough ixf1 ixf2 =-  (length (base ixf1) == length (base ixf2)) &&-  (NE.length (ixfunLMADs ixf1) == NE.length (ixfunLMADs ixf2)) &&-  all closeEnoughLMADs (NE.zip (ixfunLMADs ixf1) (ixfunLMADs ixf2))-  where-    closeEnoughLMADs :: (LMAD num, LMAD num) -> Bool-    closeEnoughLMADs (lmad1, lmad2) =-      length (lmadDims lmad1) == length (lmadDims lmad2) &&-      map ldPerm (lmadDims lmad1) ==-      map ldPerm (lmadDims lmad2)+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}++-- | This module contains a representation for the index function based on+-- linear-memory accessor descriptors; see Zhu, Hoeflinger and David work.+module Futhark.IR.Mem.IxFun+  ( IxFun (..),+    index,+    iota,+    permute,+    rotate,+    reshape,+    slice,+    rebase,+    shape,+    rank,+    linearWithOffset,+    rearrangeWithOffset,+    isDirect,+    isLinear,+    substituteInIxFun,+    leastGeneralGeneralization,+    existentialize,+    closeEnough,+  )+where++import Control.Category+import Control.Monad.Identity+import Control.Monad.State+import Control.Monad.Writer+import Data.Function (on)+import Data.List (sort, sortBy, zip4, zip5, zipWith5)+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NE+import qualified Data.Map.Strict as M+import Data.Maybe (isJust)+import Futhark.Analysis.PrimExp+  ( IntExp,+    PrimExp (..),+    TPrimExp (..),+    primExpType,+  )+import Futhark.Analysis.PrimExp.Convert (substituteInPrimExp)+import qualified Futhark.Analysis.PrimExp.Generalize as PEG+import Futhark.IR.Prop+import Futhark.IR.Syntax+  ( DimChange (..),+    DimIndex (..),+    ShapeChange,+    Slice,+    dimFix,+    unitSlice,+  )+import Futhark.IR.Syntax.Core (Ext (..))+import Futhark.Transform.Rename+import Futhark.Transform.Substitute+import Futhark.Util.IntegralExp+import Futhark.Util.Pretty+import GHC.Generics (Generic)+import Language.SexpGrammar as Sexp+import Language.SexpGrammar.Generic+import Prelude hiding (id, mod, (.))++type Shape num = [num]++type Indices num = [num]++type Permutation = [Int]++data Monotonicity+  = Inc+  | Dec+  | -- | monotonously increasing, decreasing or unknown+    Unknown+  deriving (Show, Eq, Generic)++instance SexpIso Monotonicity where+  sexpIso =+    match $+      With (. Sexp.sym "inc") $+        With (. Sexp.sym "dec") $+          With+            (. Sexp.sym "unknown")+            End++data LMADDim num = LMADDim+  { ldStride :: num,+    ldRotate :: num,+    ldShape :: num,+    ldPerm :: Int,+    ldMon :: Monotonicity+  }+  deriving (Show, Eq, Generic)++instance SexpIso num => SexpIso (LMADDim num) where+  sexpIso = with $ \lmaddim ->+    Sexp.list+      ( Sexp.el (Sexp.sym "dim")+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> lmaddim++-- | LMAD's representation consists of a general offset and for each dimension a+-- stride, rotate factor, number of elements (or shape), permutation, and+-- monotonicity. Note that the permutation is not strictly necessary in that the+-- permutation can be performed directly on LMAD dimensions, but then it is+-- difficult to extract the permutation back from an LMAD.+--+-- LMAD algebra is closed under composition w.r.t. operators such as+-- permute, index and slice.  However, other operations, such as+-- reshape, cannot always be represented inside the LMAD algebra.+--+-- It follows that the general representation of an index function is a list of+-- LMADS, in which each following LMAD in the list implicitly corresponds to an+-- irregular reshaping operation.+--+-- However, we expect that the common case is when the index function is one+-- LMAD -- we call this the "nice" representation.+--+-- Finally, the list of LMADs is kept in an @IxFun@ together with the shape of+-- the original array, and a bit to indicate whether the index function is+-- contiguous, i.e., if we instantiate all the points of the current index+-- function, do we get a contiguous memory interval?+--+-- By definition, the LMAD denotes the set of points (simplified):+--+--   \{ o + \Sigma_{j=0}^{k} ((i_j+r_j) `mod` n_j)*s_j,+--      \forall i_j such that 0<=i_j<n_j, j=1..k \}+data LMAD num = LMAD+  { lmadOffset :: num,+    lmadDims :: [LMADDim num]+  }+  deriving (Show, Eq, Generic)++instance SexpIso num => SexpIso (LMAD num) where+  sexpIso = with $ \lmad ->+    Sexp.list+      ( Sexp.el (Sexp.sym "lmad")+          >>> Sexp.el sexpIso+          >>> Sexp.rest sexpIso+      )+      >>> lmad++-- | An index function is a mapping from a multidimensional array+-- index space (the domain) to a one-dimensional memory index space.+-- Essentially, it explains where the element at position @[i,j,p]@ of+-- some array is stored inside the flat one-dimensional array that+-- constitutes its memory.  For example, we can use this to+-- distinguish row-major and column-major representations.+--+-- An index function is represented as a sequence of 'LMAD's.+data IxFun num = IxFun+  { ixfunLMADs :: NonEmpty (LMAD num),+    base :: Shape num,+    -- | ignoring permutations, is the index function contiguous?+    ixfunContig :: Bool+  }+  deriving (Show, Eq, Generic)++instance SexpIso num => SexpIso (IxFun num) where+  sexpIso = with $ \ixfun ->+    Sexp.list+      ( Sexp.el (Sexp.sym "ixfun")+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> ixfun++instance Pretty Monotonicity where+  ppr = text . show++instance Pretty num => Pretty (LMAD num) where+  ppr (LMAD offset dims) =+    braces $+      semisep+        [ text "offset: " <> oneLine (ppr offset),+          text "strides: " <> p ldStride,+          text "rotates: " <> p ldRotate,+          text "shape: " <> p ldShape,+          text "permutation: " <> p ldPerm,+          text "monotonicity: " <> p ldMon+        ]+    where+      p f = oneLine $ brackets $ commasep $ map (ppr . f) dims++instance Pretty num => Pretty (IxFun num) where+  ppr (IxFun lmads oshp cg) =+    braces $+      semisep+        [ text "base: " <> brackets (commasep $ map ppr oshp),+          text "contiguous: " <> text (show cg),+          text "LMADs: " <> brackets (commasep $ NE.toList $ NE.map ppr lmads)+        ]++instance Substitute num => Substitute (LMAD num) where+  substituteNames substs = fmap $ substituteNames substs++instance Substitute num => Substitute (IxFun num) where+  substituteNames substs = fmap $ substituteNames substs++instance Substitute num => Rename (LMAD num) where+  rename = substituteRename++instance Substitute num => Rename (IxFun num) where+  rename = substituteRename++instance FreeIn num => FreeIn (LMAD num) where+  freeIn' = foldMap freeIn'++instance FreeIn num => FreeIn (IxFun num) where+  freeIn' = foldMap freeIn'++instance Functor LMAD where+  fmap f = runIdentity . traverse (return . f)++instance Functor IxFun where+  fmap f = runIdentity . traverse (return . f)++instance Foldable LMAD where+  foldMap f = execWriter . traverse (tell . f)++instance Foldable IxFun where+  foldMap f = execWriter . traverse (tell . f)++instance Traversable LMAD where+  traverse f (LMAD offset dims) =+    LMAD <$> f offset <*> traverse f' dims+    where+      f' (LMADDim s r n p m) =+        LMADDim <$> f s <*> f r <*> f n <*> pure p <*> pure m++instance Traversable IxFun where+  traverse f (IxFun lmads oshp cg) =+    IxFun <$> traverse (traverse f) lmads <*> traverse f oshp <*> pure cg++(++@) :: [a] -> NonEmpty a -> NonEmpty a+es ++@ (ne :| nes) = case es of+  e : es' -> e :| es' ++ [ne] ++ nes+  [] -> ne :| nes++(@++@) :: NonEmpty a -> NonEmpty a -> NonEmpty a+(x :| xs) @++@ (y :| ys) = x :| xs ++ [y] ++ ys++invertMonotonicity :: Monotonicity -> Monotonicity+invertMonotonicity Inc = Dec+invertMonotonicity Dec = Inc+invertMonotonicity Unknown = Unknown++lmadPermutation :: LMAD num -> Permutation+lmadPermutation = map ldPerm . lmadDims++setLMADPermutation :: Permutation -> LMAD num -> LMAD num+setLMADPermutation perm lmad =+  lmad {lmadDims = zipWith (\dim p -> dim {ldPerm = p}) (lmadDims lmad) perm}++setLMADShape :: Shape num -> LMAD num -> LMAD num+setLMADShape shp lmad = lmad {lmadDims = zipWith (\dim s -> dim {ldShape = s}) (lmadDims lmad) shp}++-- | Substitute a name with a PrimExp in an LMAD.+substituteInLMAD ::+  Ord a =>+  M.Map a (PrimExp a) ->+  LMAD (PrimExp a) ->+  LMAD (PrimExp a)+substituteInLMAD tab (LMAD offset dims) =+  let offset' = substituteInPrimExp tab offset+      dims' =+        map+          ( \(LMADDim s r n p m) ->+              LMADDim+                (substituteInPrimExp tab s)+                (substituteInPrimExp tab r)+                (substituteInPrimExp tab n)+                p+                m+          )+          dims+   in LMAD offset' dims'++-- | Substitute a name with a PrimExp in an index function.+substituteInIxFun ::+  Ord a =>+  M.Map a (TPrimExp t a) ->+  IxFun (TPrimExp t a) ->+  IxFun (TPrimExp t a)+substituteInIxFun tab (IxFun lmads oshp cg) =+  IxFun+    (NE.map (fmap TPrimExp . substituteInLMAD tab' . fmap untyped) lmads)+    (map (TPrimExp . substituteInPrimExp tab' . untyped) oshp)+    cg+  where+    tab' = fmap untyped tab++-- | Is this is a row-major array?+isDirect :: (Eq num, IntegralExp num) => IxFun num -> Bool+isDirect ixfun@(IxFun (LMAD offset dims :| []) oshp True) =+  let strides_expected = reverse $ scanl (*) 1 (reverse (tail oshp))+   in hasContiguousPerm ixfun+        && length oshp == length dims+        && offset == 0+        && all+          ( \(LMADDim s r n p _, m, d, se) ->+              s == se && r == 0 && n == d && p == m+          )+          (zip4 dims [0 .. length dims - 1] oshp strides_expected)+isDirect _ = False++-- | Does the index function have an ascending permutation?+hasContiguousPerm :: IxFun num -> Bool+hasContiguousPerm (IxFun (lmad :| []) _ _) =+  let perm = lmadPermutation lmad+   in perm == sort perm+hasContiguousPerm _ = False++-- | Shape of an index function.+shape :: (Eq num, IntegralExp num) => IxFun num -> Shape num+shape (IxFun (lmad :| _) _ _) = lmadShape lmad++-- | Shape of an LMAD.+lmadShape :: (Eq num, IntegralExp num) => LMAD num -> Shape num+lmadShape lmad = permuteInv (lmadPermutation lmad) $ lmadShapeBase lmad++-- | Shape of an LMAD, ignoring permutations.+lmadShapeBase :: (Eq num, IntegralExp num) => LMAD num -> Shape num+lmadShapeBase = map ldShape . lmadDims++-- | Compute the flat memory index for a complete set @inds@ of array indices+-- and a certain element size @elem_size@.+index ::+  (IntegralExp num, Eq num) =>+  IxFun num ->+  Indices num ->+  num+index = indexFromLMADs . ixfunLMADs+  where+    indexFromLMADs ::+      (IntegralExp num, Eq num) =>+      NonEmpty (LMAD num) ->+      Indices num ->+      num+    indexFromLMADs (lmad :| []) inds = indexLMAD lmad inds+    indexFromLMADs (lmad1 :| lmad2 : lmads) inds =+      let i_flat = indexLMAD lmad1 inds+          new_inds = unflattenIndex (permuteFwd (lmadPermutation lmad2) $ lmadShapeBase lmad2) i_flat+       in indexFromLMADs (lmad2 :| lmads) new_inds+    indexLMAD ::+      (IntegralExp num, Eq num) =>+      LMAD num ->+      Indices num ->+      num+    indexLMAD lmad@(LMAD off dims) inds =+      let prod =+            sum $+              zipWith+                flatOneDim+                (map (\(LMADDim s r n _ _) -> (s, r, n)) dims)+                (permuteInv (lmadPermutation lmad) inds)+       in off + prod++-- | iota.+iota :: IntegralExp num => Shape num -> IxFun num+iota ns =+  let rs = replicate (length ns) 0+   in IxFun (makeRotIota Inc 0 (zip rs ns) :| []) ns True++-- | Permute dimensions.+permute ::+  IntegralExp num =>+  IxFun num ->+  Permutation ->+  IxFun num+permute (IxFun (lmad :| lmads) oshp cg) perm_new =+  let perm_cur = lmadPermutation lmad+      perm = map (perm_cur !!) perm_new+   in IxFun (setLMADPermutation perm lmad :| lmads) oshp cg++-- | Rotate an index function.+rotate ::+  (Eq num, IntegralExp num) =>+  IxFun num ->+  Indices num ->+  IxFun num+rotate (IxFun (lmad@(LMAD off dims) :| lmads) oshp cg) offs =+  let dims' =+        zipWith+          ( \(LMADDim s r n p f) o ->+              if s == 0+                then LMADDim 0 0 n p Unknown+                else LMADDim s (r + o) n p f+          )+          dims+          (permuteInv (lmadPermutation lmad) offs)+   in IxFun (LMAD off dims' :| lmads) oshp cg++-- | Handle the case where a slice can stay within a single LMAD.+sliceOneLMAD ::+  (Eq num, IntegralExp num) =>+  IxFun num ->+  Slice num ->+  Maybe (IxFun num)+sliceOneLMAD (IxFun (lmad@(LMAD _ ldims) :| lmads) oshp cg) is = do+  let perm = lmadPermutation lmad+      is' = permuteInv perm is+      cg' = cg && slicePreservesContiguous lmad is'+  guard $ harmlessRotation lmad is'+  let lmad' = foldl sliceOne (LMAD (lmadOffset lmad) []) $ zip is' ldims+      -- need to remove the fixed dims from the permutation+      perm' =+        updatePerm perm $+          map fst $+            filter (isJust . dimFix . snd) $+              zip [0 .. length is' - 1] is'++  return $ IxFun (setLMADPermutation perm' lmad' :| lmads) oshp cg'+  where+    updatePerm ps inds = foldl (\acc p -> acc ++ decrease p) [] ps+      where+        decrease p =+          let d =+                foldl+                  ( \n i ->+                      if i == p+                        then -1+                        else+                          if i > p+                            then n+                            else+                              if n /= -1+                                then n + 1+                                else n+                  )+                  0+                  inds+           in [p - d | d /= -1]++    harmlessRotation' ::+      (Eq num, IntegralExp num) =>+      LMADDim num ->+      DimIndex num ->+      Bool+    harmlessRotation' _ (DimFix _) = True+    harmlessRotation' (LMADDim 0 _ _ _ _) _ = True+    harmlessRotation' (LMADDim _ 0 _ _ _) _ = True+    harmlessRotation' (LMADDim _ _ n _ _) dslc+      | dslc == DimSlice (n - 1) n (-1)+          || dslc == unitSlice 0 n =+        True+    harmlessRotation' _ _ = False++    harmlessRotation ::+      (Eq num, IntegralExp num) =>+      LMAD num ->+      Slice num ->+      Bool+    harmlessRotation (LMAD _ dims) iss =+      and $ zipWith harmlessRotation' dims iss++    -- XXX: TODO: what happens to r on a negative-stride slice; is there+    -- such a case?+    sliceOne ::+      (Eq num, IntegralExp num) =>+      LMAD num ->+      (DimIndex num, LMADDim num) ->+      LMAD num+    sliceOne (LMAD off dims) (DimFix i, LMADDim s r n _ _) =+      LMAD (off + flatOneDim (s, r, n) i) dims+    sliceOne (LMAD off dims) (DimSlice _ ne _, LMADDim 0 _ _ p _) =+      LMAD off (dims ++ [LMADDim 0 0 ne p Unknown])+    sliceOne (LMAD off dims) (dmind, dim@(LMADDim _ _ n _ _))+      | dmind == unitSlice 0 n = LMAD off (dims ++ [dim])+    sliceOne (LMAD off dims) (dmind, LMADDim s r n p m)+      | dmind == DimSlice (n - 1) n (-1) =+        let r' = if r == 0 then 0 else n - r+            off' = off + flatOneDim (s, 0, n) (n - 1)+         in LMAD off' (dims ++ [LMADDim (s * (-1)) r' n p (invertMonotonicity m)])+    sliceOne (LMAD off dims) (DimSlice b ne 0, LMADDim s r n p _) =+      LMAD (off + flatOneDim (s, r, n) b) (dims ++ [LMADDim 0 0 ne p Unknown])+    sliceOne (LMAD off dims) (DimSlice bs ns ss, LMADDim s 0 _ p m) =+      let m' = case sgn ss of+            Just 1 -> m+            Just (-1) -> invertMonotonicity m+            _ -> Unknown+       in LMAD (off + s * bs) (dims ++ [LMADDim (ss * s) 0 ns p m'])+    sliceOne _ _ = error "slice: reached impossible case"++    slicePreservesContiguous ::+      (Eq num, IntegralExp num) =>+      LMAD num ->+      Slice num ->+      Bool+    slicePreservesContiguous (LMAD _ dims) slc =+      -- remove from the slice the LMAD dimensions that have stride 0.+      -- If the LMAD was contiguous in mem, then these dims will not+      -- influence the contiguousness of the result.+      -- Also normalize the input slice, i.e., 0-stride and size-1+      -- slices are rewritten as DimFixed.+      let (dims', slc') =+            unzip $+              filter ((/= 0) . ldStride . fst) $+                zip dims $ map normIndex slc+          -- Check that:+          -- 1. a clean split point exists between Fixed and Sliced dims+          -- 2. the outermost sliced dim has +/- 1 stride AND is unrotated or full.+          -- 3. the rest of inner sliced dims are full.+          (_, success) =+            foldl+              ( \(found, res) (slcdim, LMADDim _ r n _ _) ->+                  case (slcdim, found) of+                    (DimFix {}, True) -> (found, False)+                    (DimFix {}, False) -> (found, res)+                    (DimSlice _ ne ds, False) ->+                      -- outermost sliced dim: +/-1 stride+                      let res' = (r == 0 || n == ne) && (ds == 1 || ds == -1)+                       in (True, res && res')+                    (DimSlice _ ne ds, True) ->+                      -- inner sliced dim: needs to be full+                      let res' = (n == ne) && (ds == 1 || ds == -1)+                       in (found, res && res')+              )+              (False, True)+              $ zip slc' dims'+       in success++    normIndex ::+      (Eq num, IntegralExp num) =>+      DimIndex num ->+      DimIndex num+    normIndex (DimSlice b 1 _) = DimFix b+    normIndex (DimSlice b _ 0) = DimFix b+    normIndex d = d++-- | Slice an index function.+slice ::+  (Eq num, IntegralExp num) =>+  IxFun num ->+  Slice num ->+  IxFun num+slice _ [] = error "slice: empty slice"+slice ixfun@(IxFun (lmad@(LMAD _ _) :| lmads) oshp cg) dim_slices+  -- Avoid identity slicing.+  | dim_slices == map (unitSlice 0) (shape ixfun) = ixfun+  | Just ixfun' <- sliceOneLMAD ixfun dim_slices = ixfun'+  | otherwise =+    case sliceOneLMAD (iota (lmadShape lmad)) dim_slices of+      Just (IxFun (lmad' :| []) _ cg') ->+        IxFun (lmad' :| lmad : lmads) oshp (cg && cg')+      _ -> error "slice: reached impossible case"++-- | Handle the simple case where all reshape dimensions are coercions.+reshapeCoercion ::+  (Eq num, IntegralExp num) =>+  IxFun num ->+  ShapeChange num ->+  Maybe (IxFun num)+reshapeCoercion (IxFun (lmad@(LMAD off dims) :| lmads) _ cg) newshape = do+  let perm = lmadPermutation lmad+  (head_coercions, reshapes, tail_coercions) <- splitCoercions newshape+  let hd_len = length head_coercions+      num_coercions = hd_len + length tail_coercions+      dims' = permuteFwd perm dims+      mid_dims = take (length dims - num_coercions) $ drop hd_len dims'+      num_rshps = length reshapes+  guard (num_rshps == 0 || (num_rshps == 1 && length mid_dims == 1))+  let dims'' =+        permuteInv perm $+          zipWith+            (\ld n -> ld {ldShape = n})+            dims'+            (newDims newshape)+      lmad' = LMAD off dims''+  return $ IxFun (lmad' :| lmads) (newDims newshape) cg++-- | Handle the case where a reshape operation can stay inside a single LMAD.+--+-- There are four conditions that all must hold for the result of a reshape+-- operation to remain in the one-LMAD domain:+--+--   (1) the permutation of the underlying LMAD must leave unchanged+--       the LMAD dimensions that were *not* reshape coercions.+--   (2) the repetition of dimensions of the underlying LMAD must+--       refer only to the coerced-dimensions of the reshape operation.+--   (3) similarly, the rotated dimensions must refer only to+--       dimensions that are coerced by the reshape operation.+--   (4) finally, the underlying memory is contiguous (and monotonous).+--+-- If any of these conditions do not hold, then the reshape operation will+-- conservatively add a new LMAD to the list, leading to a representation that+-- provides less opportunities for further analysis.+reshapeOneLMAD ::+  (Eq num, IntegralExp num) =>+  IxFun num ->+  ShapeChange num ->+  Maybe (IxFun num)+reshapeOneLMAD ixfun@(IxFun (lmad@(LMAD off dims) :| lmads) _ cg) newshape = do+  let perm = lmadPermutation lmad+  (head_coercions, reshapes, tail_coercions) <- splitCoercions newshape+  let hd_len = length head_coercions+      num_coercions = hd_len + length tail_coercions+      dims_perm = permuteFwd perm dims+      mid_dims = take (length dims - num_coercions) $ drop hd_len dims_perm+      -- Ignore rotates, as we only care about not having rotates in the+      -- dimensions that aren't coercions (@mid_dims@), which we check+      -- separately.+      mon = ixfunMonotonicityRots True ixfun++  guard $+    -- checking conditions (2) and (3)+    all (\(LMADDim s r _ _ _) -> s /= 0 && r == 0) mid_dims+      &&+      -- checking condition (1)+      consecutive hd_len (map ldPerm mid_dims)+      &&+      -- checking condition (4)+      hasContiguousPerm ixfun+      && cg+      && (mon == Inc || mon == Dec)++  -- make new permutation+  let rsh_len = length reshapes+      diff = length newshape - length dims+      iota_shape = [0 .. length newshape -1]+      perm' =+        map+          ( \i ->+              let ind =+                    if i < hd_len+                      then i+                      else i - diff+               in if (i >= hd_len) && (i < hd_len + rsh_len)+                    then i -- already checked mid_dims not affected+                    else+                      let p = ldPerm (dims !! ind)+                       in if p < hd_len+                            then p+                            else p + diff+          )+          iota_shape+      -- split the dimensions+      (support_inds, repeat_inds) =+        foldl+          ( \(sup, rpt) (i, shpdim, ip) ->+              case (i < hd_len, i >= hd_len + rsh_len, shpdim) of+                (True, _, DimCoercion n) ->+                  case dims_perm !! i of+                    (LMADDim 0 _ _ _ _) -> (sup, (ip, n) : rpt)+                    (LMADDim _ r _ _ _) -> ((ip, (r, n)) : sup, rpt)+                (_, True, DimCoercion n) ->+                  case dims_perm !! (i - diff) of+                    (LMADDim 0 _ _ _ _) -> (sup, (ip, n) : rpt)+                    (LMADDim _ r _ _ _) -> ((ip, (r, n)) : sup, rpt)+                (False, False, _) ->+                  ((ip, (0, newDim shpdim)) : sup, rpt)+                -- already checked that the reshaped+                -- dims cannot be rotates+                _ -> error "reshape: reached impossible case"+          )+          ([], [])+          $ reverse $ zip3 iota_shape newshape perm'++      (sup_inds, support) = unzip $ sortBy (compare `on` fst) support_inds+      (rpt_inds, repeats) = unzip repeat_inds+      LMAD off' dims_sup = makeRotIota mon off support+      repeats' = map (\n -> LMADDim 0 0 n 0 Unknown) repeats+      dims' =+        map snd $+          sortBy (compare `on` fst) $+            zip sup_inds dims_sup ++ zip rpt_inds repeats'+      lmad' = LMAD off' dims'+  return $ IxFun (setLMADPermutation perm' lmad' :| lmads) (newDims newshape) cg+  where+    consecutive _ [] = True+    consecutive i [p] = i == p+    consecutive i ps = and $ zipWith (==) ps [i, i + 1 ..]++splitCoercions ::+  (Eq num, IntegralExp num) =>+  ShapeChange num ->+  Maybe (ShapeChange num, ShapeChange num, ShapeChange num)+splitCoercions newshape' = do+  let (head_coercions, newshape'') = span isCoercion newshape'+      (reshapes, tail_coercions) = break isCoercion newshape''+  guard (all isCoercion tail_coercions)+  return (head_coercions, reshapes, tail_coercions)+  where+    isCoercion DimCoercion {} = True+    isCoercion _ = False++-- | Reshape an index function.+reshape ::+  (Eq num, IntegralExp num) =>+  IxFun num ->+  ShapeChange num ->+  IxFun num+reshape ixfun new_shape+  | Just ixfun' <- reshapeCoercion ixfun new_shape = ixfun'+  | Just ixfun' <- reshapeOneLMAD ixfun new_shape = ixfun'+reshape (IxFun (lmad0 :| lmad0s) oshp cg) new_shape =+  case iota (newDims new_shape) of+    IxFun (lmad :| []) _ _ -> IxFun (lmad :| lmad0 : lmad0s) oshp cg+    _ -> error "reshape: reached impossible case"++-- | The number of dimensions in the domain of the input function.+rank ::+  IntegralExp num =>+  IxFun num ->+  Int+rank (IxFun (LMAD _ sss :| _) _ _) = length sss++-- | Handle the case where a rebase operation can stay within m + n - 1 LMADs,+-- where m is the number of LMADs in the index function, and n is the number of+-- LMADs in the new base.  If both index function have only on LMAD, this means+-- that we stay within the single-LMAD domain.+--+-- We can often stay in that domain if the original ixfun is essentially a+-- slice, e.g. `x[i, (k1,m,s1), (k2,n,s2)] = orig`.+--+-- XXX: TODO: handle repetitions in both lmads.+--+-- How to handle repeated dimensions in the original?+--+--   (a) Shave them off of the last lmad of original+--   (b) Compose the result from (a) with the first+--       lmad of the new base+--   (c) apply a repeat operation on the result of (b).+--+-- However, I strongly suspect that for in-place update what we need is actually+-- the INVERSE of the rebase function, i.e., given an index function new-base+-- and another one orig, compute the index function ixfun0 such that:+--+--   new-base == rebase ixfun0 ixfun, or equivalently:+--   new-base == ixfun o ixfun0+--+-- because then I can go bottom up and compose with ixfun0 all the index+-- functions corresponding to the memory block associated with ixfun.+rebaseNice ::+  (Eq num, IntegralExp num) =>+  IxFun num ->+  IxFun num ->+  Maybe (IxFun num)+rebaseNice+  new_base@(IxFun (lmad_base :| lmads_base) _ cg_base)+  ixfun@(IxFun lmads shp cg) = do+    let (lmad :| lmads') = NE.reverse lmads+        dims = lmadDims lmad+        perm = lmadPermutation lmad+        perm_base = lmadPermutation lmad_base++    guard $+      -- Core rebase condition.+      base ixfun == shape new_base+        -- Conservative safety conditions: ixfun is contiguous and has known+        -- monotonicity for all dimensions.+        && cg+        && all ((/= Unknown) . ldMon) dims+        -- XXX: We should be able to handle some basic cases where both index+        -- functions have non-trivial permutations.+        && (hasContiguousPerm ixfun || hasContiguousPerm new_base)+        -- We need the permutations to be of the same size if we want to compose+        -- them.  They don't have to be of the same size if the ixfun has a trivial+        -- permutation.  Supporting this latter case allows us to rebase when ixfun+        -- has been created by slicing with fixed dimensions.+        && (length perm == length perm_base || hasContiguousPerm ixfun)+        -- To not have to worry about ixfun having non-1 strides, we also check that+        -- it is a row-major array (modulo permutation, which is handled+        -- separately).  Accept a non-full innermost dimension.  XXX: Maybe this can+        -- be less conservative?+        && and+          ( zipWith3+              (\sn ld inner -> sn == ldShape ld || (inner && ldStride ld == 1))+              shp+              dims+              (replicate (length dims - 1) False ++ [True])+          )++    -- Compose permutations, reverse strides and adjust offset if necessary.+    let perm_base' =+          if hasContiguousPerm ixfun+            then perm_base+            else map (perm !!) perm_base+        lmad_base' = setLMADPermutation perm_base' lmad_base+        dims_base = lmadDims lmad_base'+        n_fewer_dims = length dims_base - length dims+        (dims_base', offs_contrib) =+          unzip $+            zipWith+              ( \(LMADDim s1 r1 n1 p1 _) (LMADDim _ r2 _ _ m2) ->+                  let (s', off')+                        | m2 == Inc = (s1, 0)+                        | otherwise = (s1 * (-1), s1 * (n1 - 1))+                      r'+                        | m2 == Inc = if r2 == 0 then r1 else r1 + r2+                        | r1 == 0 = r2+                        | r2 == 0 = n1 - r1+                        | otherwise = n1 - r1 + r2+                   in (LMADDim s' r' n1 (p1 - n_fewer_dims) Inc, off')+              )+              -- If @dims@ is morally a slice, it might have fewer dimensions than+              -- @dims_base@.  Drop extraneous outer dimensions.+              (drop n_fewer_dims dims_base)+              dims+        off_base = lmadOffset lmad_base' + sum offs_contrib+        lmad_base''+          | lmadOffset lmad == 0 = LMAD off_base dims_base'+          | otherwise =+            -- If the innermost dimension of the ixfun was not full (but still+            -- had a stride of 1), add its offset relative to the new base.+            setLMADShape+              (lmadShape lmad)+              ( LMAD+                  (off_base + ldStride (last dims_base) * lmadOffset lmad)+                  dims_base'+              )+        new_base' = IxFun (lmad_base'' :| lmads_base) shp cg_base+        IxFun lmads_base' _ _ = new_base'+        lmads'' = lmads' ++@ lmads_base'+    return $ IxFun lmads'' shp (cg && cg_base)++-- | Rebase an index function on top of a new base.+rebase ::+  (Eq num, IntegralExp num) =>+  IxFun num ->+  IxFun num ->+  IxFun num+rebase new_base@(IxFun lmads_base shp_base cg_base) ixfun@(IxFun lmads shp cg)+  | Just ixfun' <- rebaseNice new_base ixfun = ixfun'+  -- In the general case just concatenate LMADs since this refers to index+  -- function composition, which is always safe.+  | otherwise =+    let (lmads_base', shp_base') =+          if base ixfun == shape new_base+            then (lmads_base, shp_base)+            else+              let IxFun lmads' shp_base'' _ = reshape new_base $ map DimCoercion shp+               in (lmads', shp_base'')+     in IxFun (lmads @++@ lmads_base') shp_base' (cg && cg_base)++ixfunMonotonicity :: (Eq num, IntegralExp num) => IxFun num -> Monotonicity+ixfunMonotonicity = ixfunMonotonicityRots False++-- | If the memory support of the index function is contiguous and row-major+-- (i.e., no transpositions, repetitions, rotates, etc.), then this should+-- return the offset from which the memory-support of this index function+-- starts.+linearWithOffset ::+  (Eq num, IntegralExp num) =>+  IxFun num ->+  num ->+  Maybe num+linearWithOffset ixfun@(IxFun (lmad :| []) _ cg) elem_size+  | hasContiguousPerm ixfun && cg && ixfunMonotonicity ixfun == Inc =+    Just $ lmadOffset lmad * elem_size+linearWithOffset _ _ = Nothing++-- | Similar restrictions to @linearWithOffset@ except for transpositions, which+-- are returned together with the offset.+rearrangeWithOffset ::+  (Eq num, IntegralExp num) =>+  IxFun num ->+  num ->+  Maybe (num, [(Int, num)])+rearrangeWithOffset (IxFun (lmad :| []) oshp cg) elem_size = do+  -- Note that @cg@ describes whether the index function is+  -- contiguous, *ignoring permutations*.  This function requires that+  -- functionality.+  let perm = lmadPermutation lmad+      perm_contig = [0 .. length perm -1]+  offset <-+    linearWithOffset+      (IxFun (setLMADPermutation perm_contig lmad :| []) oshp cg)+      elem_size+  return (offset, zip perm (permuteFwd perm (lmadShapeBase lmad)))+rearrangeWithOffset _ _ = Nothing++-- | Is this a row-major array starting at offset zero?+isLinear :: (Eq num, IntegralExp num) => IxFun num -> Bool+isLinear = (== Just 0) . flip linearWithOffset 1++permuteFwd :: Permutation -> [a] -> [a]+permuteFwd ps elems = map (elems !!) ps++permuteInv :: Permutation -> [a] -> [a]+permuteInv ps elems = map snd $ sortBy (compare `on` fst) $ zip ps elems++flatOneDim ::+  (Eq num, IntegralExp num) =>+  (num, num, num) ->+  num ->+  num+flatOneDim (s, r, n) i+  | s == 0 = 0+  | r == 0 = i * s+  | otherwise = ((i + r) `mod` n) * s++-- | Generalised iota with user-specified offset and strides.+makeRotIota ::+  IntegralExp num =>+  Monotonicity ->+  num ->+  [(num, num)] ->+  LMAD num+makeRotIota mon off support+  | mon == Inc || mon == Dec =+    let rk = length support+        (rs, ns) = unzip support+        ss0 = reverse $ take rk $ scanl (*) 1 $ reverse ns+        ss =+          if mon == Inc+            then ss0+            else map (* (-1)) ss0+        ps = map fromIntegral [0 .. rk -1]+        fi = replicate rk mon+     in LMAD off $ zipWith5 LMADDim ss rs ns ps fi+  | otherwise = error "makeRotIota: requires Inc or Dec"++-- | Check monotonicity of an index function.+ixfunMonotonicityRots ::+  (Eq num, IntegralExp num) =>+  Bool ->+  IxFun num ->+  Monotonicity+ixfunMonotonicityRots ignore_rots (IxFun (lmad :| lmads) _ _) =+  let mon0 = lmadMonotonicityRots lmad+   in if all ((== mon0) . lmadMonotonicityRots) lmads+        then mon0+        else Unknown+  where+    lmadMonotonicityRots ::+      (Eq num, IntegralExp num) =>+      LMAD num ->+      Monotonicity+    lmadMonotonicityRots (LMAD _ dims)+      | all (isMonDim Inc) dims = Inc+      | all (isMonDim Dec) dims = Dec+      | otherwise = Unknown++    isMonDim ::+      (Eq num, IntegralExp num) =>+      Monotonicity ->+      LMADDim num ->+      Bool+    isMonDim mon (LMADDim s r _ _ ldmon) =+      s == 0 || ((ignore_rots || r == 0) && mon == ldmon)++-- | Generalization (anti-unification)+--+-- Anti-unification of two index functions is supported under the following conditions:+--   0. Both index functions are represented by ONE lmad (assumed common case!)+--   1. The support array of the two indexfuns have the same dimensionality+--      (we can relax this condition if we use a 1D support, as we probably should!)+--   2. The contiguous property and the per-dimension monotonicity are the same+--      (otherwise we might loose important information; this can be relaxed!)+--   3. Most importantly, both index functions correspond to the same permutation+--      (since the permutation is represented by INTs, this restriction cannot+--       be relaxed, unless we move to a gated-LMAD representation!)+leastGeneralGeneralization ::+  Eq v =>+  IxFun (PrimExp v) ->+  IxFun (PrimExp v) ->+  Maybe (IxFun (PrimExp (Ext v)), [(PrimExp v, PrimExp v)])+leastGeneralGeneralization (IxFun (lmad1 :| []) oshp1 ctg1) (IxFun (lmad2 :| []) oshp2 ctg2) = do+  guard $+    length oshp1 == length oshp2+      && ctg1 == ctg2+      && map ldPerm (lmadDims lmad1) == map ldPerm (lmadDims lmad2)+      && lmadDMon lmad1 == lmadDMon lmad2+  let (ctg, dperm, dmon) = (ctg1, lmadPermutation lmad1, lmadDMon lmad1)+  (dshp, m1) <- generalize [] (lmadDShp lmad1) (lmadDShp lmad2)+  (oshp, m2) <- generalize m1 oshp1 oshp2+  (dstd, m3) <- generalize m2 (lmadDSrd lmad1) (lmadDSrd lmad2)+  (drot, m4) <- generalize m3 (lmadDRot lmad1) (lmadDRot lmad2)+  let (offt, m5) = PEG.leastGeneralGeneralization m4 (lmadOffset lmad1) (lmadOffset lmad2)+  let lmad_dims =+        map (\(a, b, c, d, e) -> LMADDim a b c d e) $+          zip5 dstd drot dshp dperm dmon+      lmad = LMAD offt lmad_dims+  return (IxFun (lmad :| []) oshp ctg, m5)+  where+    lmadDMon = map ldMon . lmadDims+    lmadDSrd = map ldStride . lmadDims+    lmadDShp = map ldShape . lmadDims+    lmadDRot = map ldRotate . lmadDims+    generalize m l1 l2 =+      foldM+        ( \(l_acc, m') (pe1, pe2) -> do+            let (e, m'') = PEG.leastGeneralGeneralization m' pe1 pe2+            return (l_acc ++ [e], m'')+        )+        ([], m)+        (zip l1 l2)+leastGeneralGeneralization _ _ = Nothing++isSequential :: [Int] -> Bool+isSequential xs =+  all (uncurry (==)) $ zip xs [0 ..]++existentializeExp :: TPrimExp t v -> State [TPrimExp t v] (TPrimExp t (Ext v))+existentializeExp e = do+  i <- gets length+  modify (++ [e])+  let t = primExpType $ untyped e+  return $ TPrimExp $ LeafExp (Ext i) t++-- We require that there's only one lmad, and that the index function is contiguous, and the base shape has only one dimension+existentialize ::+  (IntExp t, Eq v, Pretty v) =>+  IxFun (TPrimExp t v) ->+  State [TPrimExp t v] (Maybe (IxFun (TPrimExp t (Ext v))))+existentialize (IxFun (lmad :| []) oshp True)+  | all ((== 0) . ldRotate) (lmadDims lmad),+    length (lmadShape lmad) == length oshp,+    isSequential (map ldPerm $ lmadDims lmad) = do+    oshp' <- mapM existentializeExp oshp+    lmadOffset' <- existentializeExp $ lmadOffset lmad+    lmadDims' <- mapM existentializeLMADDim $ lmadDims lmad+    let lmad' = LMAD lmadOffset' lmadDims'+    return $ Just $ IxFun (lmad' :| []) oshp' True+  where+    existentializeLMADDim ::+      LMADDim (TPrimExp t v) ->+      State [TPrimExp t v] (LMADDim (TPrimExp t (Ext v)))+    existentializeLMADDim (LMADDim str rot shp perm mon) = do+      stride' <- existentializeExp str+      shape' <- existentializeExp shp+      return $ LMADDim stride' (fmap Free rot) shape' perm mon++-- oshp' = LeafExp (Ext 0)+-- lmad' = LMAD lmadOffset' lmadDims'+-- lmadOffset' = LeafExp (Ext 1)+-- (_, lmadDims', lmadDimSubsts) = foldr generalizeDim (2, [], []) $ lmadDims lmad+-- substs = oshp : lmadOffset lmad' : lmadDimSubsts++-- generalizeDim :: (Int, [LMADDim num]) -> LMADDim num -> (Int, [LMADDim num])+-- generalizeDim (i, acc) (LMADDim stride rotate shape perm mon) =+--   (i + 3,+--    LMADDim (LeafExp $ Ext i) (LeafExp $ Ext $ i + 1) (LeafExp $ Ext $ i + 2) perm mon,+--    [stride, rotate, shape])+existentialize _ = return Nothing++-- | When comparing index functions as part of the type check in KernelsMem,+-- we may run into problems caused by the simplifier. As index functions can be+-- generalized over if-then-else expressions, the simplifier might hoist some of+-- the code from inside the if-then-else (computing the offset of an array, for+-- instance), but now the type checker cannot verify that the generalized index+-- function is valid, because some of the existentials are computed somewhere+-- else. To Work around this, we've had to relax the KernelsMem type-checker+-- a bit, specifically, we've introduced this function to verify whether two+-- index functions are "close enough" that we can assume that they match. We use+-- this instead of `ixfun1 == ixfun2` and hope that it's good enough.+closeEnough :: IxFun num -> IxFun num -> Bool+closeEnough ixf1 ixf2 =+  (length (base ixf1) == length (base ixf2))+    && (NE.length (ixfunLMADs ixf1) == NE.length (ixfunLMADs ixf2))+    && all closeEnoughLMADs (NE.zip (ixfunLMADs ixf1) (ixfunLMADs ixf2))+  where+    closeEnoughLMADs :: (LMAD num, LMAD num) -> Bool+    closeEnoughLMADs (lmad1, lmad2) =+      length (lmadDims lmad1) == length (lmadDims lmad2)+        && map ldPerm (lmadDims lmad1)+        == map ldPerm (lmadDims lmad2)
src/Futhark/IR/Mem/Simplify.hs view
@@ -1,101 +1,121 @@+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE TypeFamilies #-}+ module Futhark.IR.Mem.Simplify-       ( simplifyProgGeneric-       , simplifyStmsGeneric-       , simpleGeneric-       , SimplifyMemory-       )+  ( simplifyProgGeneric,+    simplifyStmsGeneric,+    simpleGeneric,+    SimplifyMemory,+  ) where  import Control.Monad import Data.List (find)--import qualified Futhark.IR.Syntax as AST-import Futhark.IR.Syntax-  hiding (Prog, BasicOp, Exp, Body, Stm,-          Pattern, PatElem, Lambda, FunDef, FParam, LParam, RetType)-import Futhark.IR.Mem-import qualified Futhark.IR.Mem.IxFun as IxFun-import Futhark.Pass.ExplicitAllocations (simplifiable) import qualified Futhark.Analysis.SymbolTable as ST import qualified Futhark.Analysis.UsageTable as UT-import qualified Futhark.Optimise.Simplify.Engine as Engine-import qualified Futhark.Optimise.Simplify as Simplify import Futhark.Construct-import Futhark.Pass-import Futhark.Optimise.Simplify.Rules-import Futhark.Optimise.Simplify.Rule+import Futhark.IR.Mem+import qualified Futhark.IR.Mem.IxFun as IxFun+import qualified Futhark.IR.Syntax as AST+import qualified Futhark.Optimise.Simplify as Simplify+import qualified Futhark.Optimise.Simplify.Engine as Engine import Futhark.Optimise.Simplify.Lore+import Futhark.Optimise.Simplify.Rule+import Futhark.Optimise.Simplify.Rules+import Futhark.Pass+import Futhark.Pass.ExplicitAllocations (simplifiable) import Futhark.Util -simpleGeneric :: (SimplifyMemory lore, Op lore ~ MemOp inner) =>-                 (OpWithWisdom inner -> UT.UsageTable)-              -> Simplify.SimplifyOp lore inner-              -> Simplify.SimpleOps lore+simpleGeneric ::+  (SimplifyMemory lore, Op lore ~ MemOp inner) =>+  (OpWithWisdom inner -> UT.UsageTable) ->+  Simplify.SimplifyOp lore inner ->+  Simplify.SimpleOps lore simpleGeneric = simplifiable -simplifyProgGeneric :: (SimplifyMemory lore, Op lore ~ MemOp inner) =>-                       Simplify.SimpleOps lore-                    -> Prog lore -> PassM (Prog lore)+simplifyProgGeneric ::+  (SimplifyMemory lore, Op lore ~ MemOp inner) =>+  Simplify.SimpleOps lore ->+  Prog lore ->+  PassM (Prog lore) simplifyProgGeneric ops =-  Simplify.simplifyProg ops callKernelRules-  blockers { Engine.blockHoistBranch = blockAllocs }-  where blockAllocs vtable _ (Let _ _ (Op Alloc{})) =-          not $ ST.simplifyMemory vtable-        -- Do not hoist statements that produce arrays.  This is-        -- because in the KernelsMem representation, multiple-        -- arrays can be located in the same memory block, and moving-        -- their creation out of a branch can thus cause memory-        -- corruption.  At this point in the compiler we have probably-        -- already moved all the array creations that matter.-        blockAllocs _ _ (Let pat _ _) =-          not $ all primType $ patternTypes pat+  Simplify.simplifyProg+    ops+    callKernelRules+    blockers {Engine.blockHoistBranch = blockAllocs}+  where+    blockAllocs vtable _ (Let _ _ (Op Alloc {})) =+      not $ ST.simplifyMemory vtable+    -- Do not hoist statements that produce arrays.  This is+    -- because in the KernelsMem representation, multiple+    -- arrays can be located in the same memory block, and moving+    -- their creation out of a branch can thus cause memory+    -- corruption.  At this point in the compiler we have probably+    -- already moved all the array creations that matter.+    blockAllocs _ _ (Let pat _ _) =+      not $ all primType $ patternTypes pat -simplifyStmsGeneric :: (HasScope lore m, MonadFreshNames m,-                        SimplifyMemory lore, Op lore ~ MemOp inner) =>-                       Simplify.SimpleOps lore -> Stms lore-                    -> m (ST.SymbolTable (Wise lore), Stms lore)+simplifyStmsGeneric ::+  ( HasScope lore m,+    MonadFreshNames m,+    SimplifyMemory lore,+    Op lore ~ MemOp inner+  ) =>+  Simplify.SimpleOps lore ->+  Stms lore ->+  m (ST.SymbolTable (Wise lore), Stms lore) simplifyStmsGeneric ops stms = do   scope <- askScope-  Simplify.simplifyStms ops callKernelRules blockers-    scope stms+  Simplify.simplifyStms+    ops+    callKernelRules+    blockers+    scope+    stms  isResultAlloc :: Op lore ~ MemOp op => Engine.BlockPred lore-isResultAlloc _ usage (Let (AST.Pattern [] [bindee]) _ (Op Alloc{})) =+isResultAlloc _ usage (Let (AST.Pattern [] [bindee]) _ (Op Alloc {})) =   UT.isInResult (patElemName bindee) usage isResultAlloc _ _ _ = False  isAlloc :: Op lore ~ MemOp op => Engine.BlockPred lore-isAlloc _ _ (Let _ _ (Op Alloc{})) = True-isAlloc _ _ _                      = False+isAlloc _ _ (Let _ _ (Op Alloc {})) = True+isAlloc _ _ _ = False -blockers :: (Op lore ~ MemOp inner) =>-            Simplify.HoistBlockers lore-blockers = Engine.noExtraHoistBlockers {-    Engine.blockHoistPar    = isAlloc-  , Engine.blockHoistSeq    = isResultAlloc-  , Engine.isAllocation     = isAlloc mempty mempty-  }+blockers ::+  (Op lore ~ MemOp inner) =>+  Simplify.HoistBlockers lore+blockers =+  Engine.noExtraHoistBlockers+    { Engine.blockHoistPar = isAlloc,+      Engine.blockHoistSeq = isResultAlloc,+      Engine.isAllocation = isAlloc mempty mempty+    }  -- | Some constraints that must hold for the simplification rules to work. type SimplifyMemory lore =-  (Simplify.SimplifiableLore lore,-   ExpDec lore ~ (),-   BodyDec lore ~ (),-   AllocOp (Op (Wise lore)),-   CanBeWise (Op lore),-   BinderOps (Wise lore),-   Mem lore)+  ( Simplify.SimplifiableLore lore,+    ExpDec lore ~ (),+    BodyDec lore ~ (),+    AllocOp (Op (Wise lore)),+    CanBeWise (Op lore),+    BinderOps (Wise lore),+    Mem lore+  )  callKernelRules :: SimplifyMemory lore => RuleBook (Wise lore)-callKernelRules = standardRules <>-                  ruleBook [RuleBasicOp copyCopyToCopy,-                            RuleBasicOp removeIdentityCopy,-                            RuleIf unExistentialiseMemory] []+callKernelRules =+  standardRules+    <> ruleBook+      [ RuleBasicOp copyCopyToCopy,+        RuleBasicOp removeIdentityCopy,+        RuleIf unExistentialiseMemory,+        RuleOp decertifySafeAlloc+      ]+      []  -- | If a branch is returning some existential memory, but the size of -- the array is not existential, and the index function of the array@@ -106,101 +126,119 @@   | ST.simplifyMemory vtable,     fixable <- foldl hasConcretisableMemory mempty $ patternElements pat,     not $ null fixable = Simplify $ do--      -- Create non-existential memory blocks big enough to hold the-      -- arrays.-      (arr_to_mem, oldmem_to_mem) <--        fmap unzip $ forM fixable $ \(arr_pe, mem_size, oldmem, space) -> do+    -- Create non-existential memory blocks big enough to hold the+    -- arrays.+    (arr_to_mem, oldmem_to_mem) <-+      fmap unzip $+        forM fixable $ \(arr_pe, mem_size, oldmem, space) -> do           size <- toSubExp "size" mem_size           mem <- letExp "mem" $ Op $ allocOp size space           return ((patElemName arr_pe, mem), (oldmem, mem)) -      -- Update the branches to contain Copy expressions putting the-      -- arrays where they are expected.-      let updateBody body = insertStmsM $ do-            res <- bodyBind body-            resultBodyM =<<-              zipWithM updateResult (patternElements pat) res-          updateResult pat_elem (Var v)-            | Just mem <- lookup (patElemName pat_elem) arr_to_mem,-              (_, MemArray pt shape u (ArrayIn _ ixfun)) <- patElemDec pat_elem = do-                v_copy <- newVName $ baseString v <> "_nonext_copy"-                let v_pat = Pattern [] [PatElem v_copy $-                                        MemArray pt shape u $ ArrayIn mem ixfun]-                addStm $ mkWiseLetStm v_pat (defAux ()) $ BasicOp (Copy v)-                return $ Var v_copy-            | Just mem <- lookup (patElemName pat_elem) oldmem_to_mem =-                return $ Var mem-          updateResult _ se =-            return se-      tbranch' <- updateBody tbranch-      fbranch' <- updateBody fbranch-      letBind pat $ If cond tbranch' fbranch' ifdec-  where onlyUsedIn name here = not $ any ((name `nameIn`) . freeIn) $-                                          filter ((/=here) . patElemName) $-                                          patternValueElements pat-        knownSize Constant{} = True-        knownSize (Var v) = not $ inContext v-        inContext = (`elem` patternContextNames pat)+    -- Update the branches to contain Copy expressions putting the+    -- arrays where they are expected.+    let updateBody body = insertStmsM $ do+          res <- bodyBind body+          resultBodyM+            =<< zipWithM updateResult (patternElements pat) res+        updateResult pat_elem (Var v)+          | Just mem <- lookup (patElemName pat_elem) arr_to_mem,+            (_, MemArray pt shape u (ArrayIn _ ixfun)) <- patElemDec pat_elem = do+            v_copy <- newVName $ baseString v <> "_nonext_copy"+            let v_pat =+                  Pattern+                    []+                    [ PatElem v_copy $+                        MemArray pt shape u $ ArrayIn mem ixfun+                    ]+            addStm $ mkWiseLetStm v_pat (defAux ()) $ BasicOp (Copy v)+            return $ Var v_copy+          | Just mem <- lookup (patElemName pat_elem) oldmem_to_mem =+            return $ Var mem+        updateResult _ se =+          return se+    tbranch' <- updateBody tbranch+    fbranch' <- updateBody fbranch+    letBind pat $ If cond tbranch' fbranch' ifdec+  where+    onlyUsedIn name here =+      not $+        any ((name `nameIn`) . freeIn) $+          filter ((/= here) . patElemName) $+            patternValueElements pat+    knownSize Constant {} = True+    knownSize (Var v) = not $ inContext v+    inContext = (`elem` patternContextNames pat) -        hasConcretisableMemory fixable pat_elem-          | (_, MemArray pt shape _ (ArrayIn mem ixfun)) <- patElemDec pat_elem,-            Just (j, Mem space) <--              fmap patElemType <$> find ((mem==) . patElemName . snd)-                                        (zip [(0::Int)..] $ patternElements pat),-            Just tse <- maybeNth j $ bodyResult tbranch,-            Just fse <- maybeNth j $ bodyResult fbranch,-            mem `onlyUsedIn` patElemName pat_elem,-            all knownSize (shapeDims shape),-            not $ freeIn ixfun `namesIntersect` namesFromList (patternNames pat),-            fse /= tse =-              let mem_size =-                    sExt Int64 $ product $ primByteSize pt : IxFun.base ixfun-              in (pat_elem, mem_size, mem, space) : fixable-          | otherwise =-              fixable+    hasConcretisableMemory fixable pat_elem+      | (_, MemArray pt shape _ (ArrayIn mem ixfun)) <- patElemDec pat_elem,+        Just (j, Mem space) <-+          fmap patElemType+            <$> find+              ((mem ==) . patElemName . snd)+              (zip [(0 :: Int) ..] $ patternElements pat),+        Just tse <- maybeNth j $ bodyResult tbranch,+        Just fse <- maybeNth j $ bodyResult fbranch,+        mem `onlyUsedIn` patElemName pat_elem,+        all knownSize (shapeDims shape),+        not $ freeIn ixfun `namesIntersect` namesFromList (patternNames pat),+        fse /= tse =+        let mem_size =+              untyped $ product $ primByteSize pt : map sExt64 (IxFun.base ixfun)+         in (pat_elem, mem_size, mem, space) : fixable+      | otherwise =+        fixable unExistentialiseMemory _ _ _ _ = Skip  -- | If we are copying something that is itself a copy, just copy the -- original one instead.-copyCopyToCopy :: (BinderOps lore,-                   LetDec lore ~ (VarWisdom, MemBound u)) =>-                  TopDownRuleBasicOp lore+copyCopyToCopy ::+  ( BinderOps lore,+    LetDec lore ~ (VarWisdom, MemBound u)+  ) =>+  TopDownRuleBasicOp lore copyCopyToCopy vtable pat@(Pattern [] [pat_elem]) _ (Copy v1)   | Just (BasicOp (Copy v2), v1_cs) <- ST.lookupExp v1 vtable,-     Just (_, MemArray _ _ _ (ArrayIn srcmem src_ixfun)) <-       ST.entryLetBoundDec =<< ST.lookup v1 vtable,-     Just (Mem src_space) <- ST.lookupType srcmem vtable,-     (_, MemArray _ _ _ (ArrayIn destmem dest_ixfun)) <- patElemDec pat_elem,-     Just (Mem dest_space) <- ST.lookupType destmem vtable,--    src_space == dest_space, dest_ixfun == src_ixfun =--      Simplify $ certifying v1_cs $ letBind pat $ BasicOp $ Copy v2-+    src_space == dest_space,+    dest_ixfun == src_ixfun =+    Simplify $ certifying v1_cs $ letBind pat $ BasicOp $ Copy v2 copyCopyToCopy vtable pat _ (Copy v0)   | Just (BasicOp (Rearrange perm v1), v0_cs) <- ST.lookupExp v0 vtable,     Just (BasicOp (Copy v2), v1_cs) <- ST.lookupExp v1 vtable = Simplify $ do-      v0' <- certifying (v0_cs<>v1_cs) $-             letExp "rearrange_v0" $ BasicOp $ Rearrange perm v2-      letBind pat $ BasicOp $ Copy v0'-+    v0' <-+      certifying (v0_cs <> v1_cs) $+        letExp "rearrange_v0" $ BasicOp $ Rearrange perm v2+    letBind pat $ BasicOp $ Copy v0' copyCopyToCopy _ _ _ _ = Skip  -- | If the destination of a copy is the same as the source, just -- remove it.-removeIdentityCopy :: (BinderOps lore,-                       LetDec lore ~ (VarWisdom, MemBound u)) =>-                      TopDownRuleBasicOp lore+removeIdentityCopy ::+  ( BinderOps lore,+    LetDec lore ~ (VarWisdom, MemBound u)+  ) =>+  TopDownRuleBasicOp lore removeIdentityCopy vtable pat@(Pattern [] [pe]) _ (Copy v)   | (_, MemArray _ _ _ (ArrayIn dest_mem dest_ixfun)) <- patElemDec pe,     Just (_, MemArray _ _ _ (ArrayIn src_mem src_ixfun)) <-       ST.entryLetBoundDec =<< ST.lookup v vtable,-    dest_mem == src_mem, dest_ixfun == src_ixfun =-      Simplify $ letBind pat $ BasicOp $ SubExp $ Var v-+    dest_mem == src_mem,+    dest_ixfun == src_ixfun =+    Simplify $ letBind pat $ BasicOp $ SubExp $ Var v removeIdentityCopy _ _ _ _ = Skip++-- If an allocation is statically known to be safe, then we can remove+-- the certificates on it.  This can help hoist things that would+-- otherwise be stuck inside loops or branches.+decertifySafeAlloc :: SimplifyMemory lore => TopDownRuleOp (Wise lore)+decertifySafeAlloc _ pat (StmAux cs attrs _) op+  | cs /= mempty,+    [Mem _] <- patternTypes pat,+    safeOp op =+    Simplify $ attributing attrs $ letBind pat $ Op op+decertifySafeAlloc _ _ _ _ = Skip
src/Futhark/IR/Pretty.hs view
@@ -1,26 +1,26 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE FlexibleContexts     #-}-{-# LANGUAGE FlexibleInstances    #-}+ -- | Futhark prettyprinter.  This module defines 'Pretty' instances -- for the AST defined in "Futhark.IR.Syntax", -- but also a number of convenience functions if you don't want to use -- the interface from 'Pretty'. module Futhark.IR.Pretty-  ( prettyTuple-  , pretty-  , PrettyAnnot (..)-  , PrettyLore (..)-  , ppTuple'+  ( prettyTuple,+    pretty,+    PrettyAnnot (..),+    PrettyLore (..),+    ppTuple',   )-  where--import           Data.Foldable (toList)-import           Data.Maybe+where -import           Futhark.Util.Pretty-import           Futhark.IR.Prop.Patterns-import           Futhark.IR.Syntax+import Data.Foldable (toList)+import Data.Maybe+import Futhark.IR.Prop.Patterns+import Futhark.IR.Syntax+import Futhark.Util.Pretty  -- | Class for values that may have some prettyprinted annotation. class PrettyAnnot a where@@ -36,20 +36,28 @@   ppAnnot = const Nothing  -- | The class of lores whose annotations can be prettyprinted.-class (Decorations lore,-       Pretty (RetType lore),-       Pretty (BranchType lore),-       Pretty (Param (FParamInfo lore)),-       Pretty (Param (LParamInfo lore)),-       Pretty (PatElemT (LetDec lore)),-       PrettyAnnot (PatElem lore),-       PrettyAnnot (FParam lore),-       PrettyAnnot (LParam lore),-       Pretty (Op lore)) => PrettyLore lore where+class+  ( Decorations lore,+    Pretty (RetType lore),+    Pretty (BranchType lore),+    Pretty (Param (FParamInfo lore)),+    Pretty (Param (LParamInfo lore)),+    Pretty (PatElemT (LetDec lore)),+    PrettyAnnot (PatElem lore),+    PrettyAnnot (FParam lore),+    PrettyAnnot (LParam lore),+    Pretty (Op lore)+  ) =>+  PrettyLore lore+  where   ppExpLore :: ExpDec lore -> Exp lore -> Maybe Doc   ppExpLore _ (If _ _ _ (IfDec ts _)) =-    Just $ stack $ map (text . ("-- "++)) $ lines $ pretty $-    text "Branch returns:" <+> ppTuple' ts+    Just $+      stack $+        map (text . ("-- " ++)) $+          lines $+            pretty $+              text "Branch returns:" <+> ppTuple' ts   ppExpLore _ _ = Nothing  commastack :: [Doc] -> Doc@@ -62,7 +70,7 @@   ppr _ = mempty  instance Pretty Commutativity where-  ppr Commutative    = text "commutative"+  ppr Commutative = text "commutative"   ppr Noncommutative = text "noncommutative"  instance Pretty Shape where@@ -70,7 +78,7 @@  instance Pretty a => Pretty (Ext a) where   ppr (Free e) = ppr e-  ppr (Ext x)  = text "?" <> text (show x)+  ppr (Ext x) = text "?" <> text (show x)  instance Pretty ExtShape where   ppr = brackets . commasep . map ppr . shapeDims@@ -102,7 +110,7 @@   ppr ident = ppr (identType ident) <+> ppr (identName ident)  instance Pretty SubExp where-  ppr (Var v)      = ppr v+  ppr (Var v) = ppr v   ppr (Constant v) = ppr v  instance Pretty Certificates where@@ -115,8 +123,9 @@ instance PrettyLore lore => Pretty (Body lore) where   ppr (Body _ stms res)     | null stms = braces (commasep $ map ppr res)-    | otherwise = stack (map ppr $ stmsToList stms) </>-                  text "in" <+> braces (commasep $ map ppr res)+    | otherwise =+      stack (map ppr $ stmsToList stms)+        </> text "in" <+> braces (commasep $ map ppr res)  instance Pretty Attr where   ppr (AttrAtom v) = ppr v@@ -124,7 +133,8 @@  attrAnnots :: Attrs -> [Doc] attrAnnots = map f . toList . unAttrs-  where f v = text "#[" <> ppr v <> text "]"+  where+    f v = text "#[" <> ppr v <> text "]"  stmAttrAnnots :: Stm lore -> [Doc] stmAttrAnnots = attrAnnots . stmAuxAttrs . stmAux@@ -132,49 +142,53 @@ instance Pretty (PatElemT dec) => Pretty (PatternT dec) where   ppr pat = ppPattern (patternContextElements pat) (patternValueElements pat) -instance Pretty (PatElemT b) => Pretty (PatElemT (a,b)) where+instance Pretty (PatElemT b) => Pretty (PatElemT (a, b)) where   ppr = ppr . fmap snd  instance Pretty (PatElemT Type) where   ppr (PatElem name t) = ppr t <+> ppr name -instance Pretty (Param b) => Pretty (Param (a,b)) where+instance Pretty (Param b) => Pretty (Param (a, b)) where   ppr = ppr . fmap snd  instance Pretty (Param DeclType) where   ppr (Param name t) =-    ppr t <+>-    ppr name+    ppr t+      <+> ppr name  instance Pretty (Param Type) where   ppr (Param name t) =-    ppr t <+>-    ppr name+    ppr t+      <+> ppr name  instance PrettyLore lore => Pretty (Stm lore) where   ppr bnd@(Let pat (StmAux cs _ dec) e) =-    stmannot $ align $ hang 2 $-    text "let" <+> align (ppr pat) <+>-    case (linebreak, ppExpLore dec e) of-      (True, Nothing) -> equals </> e'-      (_, Just ann) -> equals </> (ann </> e')-      (False, Nothing) -> equals <+/> e'-    where e' | linebreak = ppr cs </> ppr e-             | otherwise = ppr cs <> ppr e-          linebreak = case e of-                        DoLoop{}           -> True-                        Op{}               -> True-                        If{}               -> True-                        Apply{}            -> True-                        BasicOp ArrayLit{} -> False-                        BasicOp Assert{}   -> True-                        _                  -> cs /= mempty+    stmannot $+      align $+        hang 2 $+          text "let" <+> align (ppr pat)+            <+> case (linebreak, ppExpLore dec e) of+              (True, Nothing) -> equals </> e'+              (_, Just ann) -> equals </> (ann </> e')+              (False, Nothing) -> equals <+/> e'+    where+      e'+        | linebreak = ppr cs </> ppr e+        | otherwise = ppr cs <> ppr e+      linebreak = case e of+        DoLoop {} -> True+        Op {} -> True+        If {} -> True+        Apply {} -> True+        BasicOp ArrayLit {} -> False+        BasicOp Assert {} -> True+        _ -> cs /= mempty -          stmannot =-            case stmAttrAnnots bnd <>-                 mapMaybe ppAnnot (patternElements $ stmPattern bnd) of-              []     -> id-              annots -> (align (stack annots) </>)+      stmannot =+        case stmAttrAnnots bnd+          <> mapMaybe ppAnnot (patternElements $ stmPattern bnd) of+          [] -> id+          annots -> (align (stack annots) </>)  instance Pretty BasicOp where   ppr (SubExp se) = ppr se@@ -184,20 +198,23 @@   ppr (ArrayLit es rt) =     case rt of       Array {} -> brackets $ commastack $ map ppr es-      _        -> brackets $ commasep   $ map ppr es+      _ -> brackets $ commasep $ map ppr es   ppr (BinOp bop x y) = ppr bop <> parens (ppr x <> comma <+> ppr y)   ppr (CmpOp op x y) = ppr op <> parens (ppr x <> comma <+> ppr y)   ppr (ConvOp conv x) =     text (convOpFun conv) <+> ppr fromtype <+> ppr x <+> text "to" <+> ppr totype-    where (fromtype, totype) = convOpType conv+    where+      (fromtype, totype) = convOpType conv   ppr (UnOp op e) = ppr op <+> pprPrec 9 e   ppr (Index v idxs) =     ppr v <> brackets (commasep (map ppr idxs))   ppr (Update src idxs se) =-    ppr src <+> text "with" <+> brackets (commasep (map ppr idxs)) <+>-    text "<-" <+> ppr se+    ppr src <+> text "with" <+> brackets (commasep (map ppr idxs))+      <+> text "<-"+      <+> ppr se   ppr (Iota e x s et) = text "iota" <> et' <> apply [ppr e, ppr x, ppr s]-    where et' = text $ show $ primBitSize $ IntType et+    where+      et' = text $ show $ primBitSize $ IntType et   ppr (Replicate ne ve) =     text "replicate" <> apply [ppr ne, align (ppr ve)]   ppr (Scratch t shape) =@@ -217,64 +234,88 @@  instance Pretty a => Pretty (ErrorMsg a) where   ppr (ErrorMsg parts) = commasep $ map p parts-    where p (ErrorString s) = text $ show s-          p (ErrorInt32 x) = ppr x+    where+      p (ErrorString s) = text $ show s+      p (ErrorInt32 x) = ppr x+      p (ErrorInt64 x) = ppr x  instance PrettyLore lore => Pretty (Exp lore) where   ppr (If c t f (IfDec _ ifsort)) =-    text "if" <+> info' <+> ppr c </>-    text "then" <+> maybeNest t <+>-    text "else" <+> maybeNest f-    where info' = case ifsort of IfNormal -> mempty-                                 IfFallback -> text "<fallback>"-                                 IfEquiv -> text "<equiv>"-          maybeNest b | null $ bodyStms b = ppr b-                      | otherwise         = nestedBlock "{" "}" $ ppr b+    text "if" <+> info' <+> ppr c+      </> text "then"+      <+> maybeNest t+      <+> text "else"+      <+> maybeNest f+    where+      info' = case ifsort of+        IfNormal -> mempty+        IfFallback -> text "<fallback>"+        IfEquiv -> text "<equiv>"+      maybeNest b+        | null $ bodyStms b = ppr b+        | otherwise = nestedBlock "{" "}" $ ppr b   ppr (BasicOp op) = ppr op   ppr (Apply fname args _ (safety, _, _)) =     text (nameToString fname) <> safety' <> apply (map (align . pprArg) args)-    where pprArg (arg, Consume) = text "*" <> ppr arg-          pprArg (arg, _)       = ppr arg-          safety' = case safety of Unsafe -> text "<unsafe>"-                                   Safe   -> mempty+    where+      pprArg (arg, Consume) = text "*" <> ppr arg+      pprArg (arg, _) = ppr arg+      safety' = case safety of+        Unsafe -> text "<unsafe>"+        Safe -> mempty   ppr (Op op) = ppr op   ppr (DoLoop ctx val form loopbody) =-    annot (mapMaybe ppAnnot (ctxparams++valparams)) $-    text "loop" <+> ppPattern ctxparams valparams <+>-    equals <+> ppTuple' (ctxinit++valinit) </>-    (case form of-      ForLoop i it bound [] ->-        text "for" <+> align (ppr i <> text ":" <> ppr it <+>-                              text "<" <+> align (ppr bound))-      ForLoop i it bound loop_vars ->-        annot (mapMaybe (ppAnnot . fst) loop_vars) $-        text "for" <+> align (ppr i <> text ":" <> ppr it <+>-                              text "<" <+> align (ppr bound) </>-                             stack (map pprLoopVar loop_vars))-      WhileLoop cond ->-        text "while" <+> ppr cond-    ) <+> text "do" <+> nestedBlock "{" "}" (ppr loopbody)-    where (ctxparams, ctxinit) = unzip ctx-          (valparams, valinit) = unzip val-          pprLoopVar (p,a) = ppr p <+> text "in" <+> ppr a+    annot (mapMaybe ppAnnot (ctxparams ++ valparams)) $+      text "loop" <+> ppPattern ctxparams valparams+        <+> equals+        <+> ppTuple' (ctxinit ++ valinit)+        </> ( case form of+                ForLoop i it bound [] ->+                  text "for"+                    <+> align+                      ( ppr i <> text ":" <> ppr it+                          <+> text "<"+                          <+> align (ppr bound)+                      )+                ForLoop i it bound loop_vars ->+                  annot (mapMaybe (ppAnnot . fst) loop_vars) $+                    text "for"+                      <+> align+                        ( ppr i <> text ":" <> ppr it+                            <+> text "<"+                            <+> align (ppr bound)+                            </> stack (map pprLoopVar loop_vars)+                        )+                WhileLoop cond ->+                  text "while" <+> ppr cond+            )+        <+> text "do"+        <+> nestedBlock "{" "}" (ppr loopbody)+    where+      (ctxparams, ctxinit) = unzip ctx+      (valparams, valinit) = unzip val+      pprLoopVar (p, a) = ppr p <+> text "in" <+> ppr a  instance PrettyLore lore => Pretty (Lambda lore) where   ppr (Lambda [] _ []) = text "nilFn"   ppr (Lambda params body rettype) =     annot (mapMaybe ppAnnot params) $-    text "fn" <+> ppTuple' rettype <+/>-    align (parens (commasep (map ppr params))) <+>-    text "=>" </> indent 2 (ppr body)+      text "fn" <+> ppTuple' rettype+        <+/> align (parens (commasep (map ppr params)))+        <+> text "=>" </> indent 2 (ppr body)  instance PrettyLore lore => Pretty (FunDef lore) where   ppr (FunDef entry attrs name rettype fparams body) =     annot (mapMaybe ppAnnot fparams <> attrAnnots attrs) $-    text fun <+> ppTuple' rettype <+/>-    text (nameToString name) <+>-    apply (map ppr fparams) <+>-    equals <+> nestedBlock "{" "}" (ppr body)-    where fun | isJust entry = "entry"-              | otherwise    = "fun"+      text fun <+> ppTuple' rettype+        <+/> text (nameToString name)+        <+> apply (map ppr fparams)+        <+> equals+        <+> nestedBlock "{" "}" (ppr body)+    where+      fun+        | isJust entry = "entry"+        | otherwise = "fun"  instance PrettyLore lore => Pretty (Prog lore) where   ppr (Prog consts funs) =@@ -282,10 +323,10 @@  instance Pretty d => Pretty (DimChange d) where   ppr (DimCoercion se) = text "~" <> ppr se-  ppr (DimNew      se) = ppr se+  ppr (DimNew se) = ppr se  instance Pretty d => Pretty (DimIndex d) where-  ppr (DimFix i)       = ppr i+  ppr (DimFix i) = ppr i   ppr (DimSlice i n s) = ppr i <> text ":+" <> ppr n <> text "*" <> ppr s  ppPattern :: (Pretty a, Pretty b) => [a] -> [b] -> Doc
src/Futhark/IR/Primitive.hs view
@@ -1,1369 +1,1717 @@-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE Safe #-}--- | Definitions of primitive types, the values that inhabit these--- types, and operations on these values.  A primitive value can also--- be called a scalar.------ Essentially, this module describes the subset of the (internal)--- Futhark language that operates on primitive types.-module Futhark.IR.Primitive-       ( -- * Types-         IntType (..), allIntTypes-       , FloatType (..), allFloatTypes-       , PrimType (..), allPrimTypes--         -- * Values-       , IntValue(..)-       , intValue, intValueType, valueIntegral-       , FloatValue(..)-       , floatValue, floatValueType-       , PrimValue(..)-       , primValueType-       , blankPrimValue--         -- * Operations-       , Overflow (..)-       , Safety(..)-       , UnOp (..), allUnOps-       , BinOp (..), allBinOps-       , ConvOp (..), allConvOps-       , CmpOp (..), allCmpOps--         -- ** Unary Operations-       , doUnOp-       , doComplement-       , doAbs, doFAbs-       , doSSignum, doUSignum--         -- ** Binary Operations-       , doBinOp-       , doAdd, doMul, doSDiv, doSMod-       , doPow--         -- ** Conversion Operations-       , doConvOp-       , doZExt, doSExt-       , doFPConv-       , doFPToUI, doFPToSI-       , doUIToFP, doSIToFP-       , intToInt64, intToWord64--         -- * Comparison Operations-       , doCmpOp-       , doCmpEq-       , doCmpUlt, doCmpUle-       , doCmpSlt, doCmpSle-       , doFCmpLt, doFCmpLe--        -- * Type Of-       , binOpType-       , unOpType-       , cmpOpType-       , convOpType--       -- * Primitive functions-       , primFuns--       -- * Utility-       , zeroIsh-       , zeroIshInt-       , oneIsh-       , oneIshInt-       , negativeIsh-       , primBitSize-       , primByteSize-       , intByteSize-       , floatByteSize-       , commutativeBinOp--       -- * Prettyprinting-       , convOpFun-       , prettySigned-       )-       where--import           Control.Applicative-import qualified Data.Binary.Get as G-import qualified Data.Binary.Put as P-import           Data.Bits-import           Data.Fixed (mod') -- Weird location.-import           Data.Int            (Int16, Int32, Int64, Int8)-import qualified Data.Map as M-import           Data.Word--import           Prelude--import           Futhark.Util.Pretty-import           Futhark.Util (roundFloat, ceilFloat, floorFloat,-                               roundDouble, ceilDouble, floorDouble,-                               lgamma, lgammaf, tgamma, tgammaf)---- | An integer type, ordered by size.  Note that signedness is not a--- property of the type, but a property of the operations performed on--- values of these types.-data IntType = Int8-             | Int16-             | Int32-             | Int64-             deriving (Eq, Ord, Show, Enum, Bounded)--instance Pretty IntType where-  ppr Int8  = text "i8"-  ppr Int16 = text "i16"-  ppr Int32 = text "i32"-  ppr Int64 = text "i64"---- | A list of all integer types.-allIntTypes :: [IntType]-allIntTypes = [minBound..maxBound]---- | A floating point type.-data FloatType = Float32-               | Float64-               deriving (Eq, Ord, Show, Enum, Bounded)--instance Pretty FloatType where-  ppr Float32 = text "f32"-  ppr Float64 = text "f64"---- | A list of all floating-point types.-allFloatTypes :: [FloatType]-allFloatTypes = [minBound..maxBound]---- | Low-level primitive types.-data PrimType = IntType IntType-              | FloatType FloatType-              | Bool-              | Cert-              deriving (Eq, Ord, Show)--instance Enum PrimType where-  toEnum 0 = IntType Int8-  toEnum 1 = IntType Int16-  toEnum 2 = IntType Int32-  toEnum 3 = IntType Int64-  toEnum 4 = FloatType Float32-  toEnum 5 = FloatType Float64-  toEnum 6 = Bool-  toEnum _ = Cert--  fromEnum (IntType Int8)      = 0-  fromEnum (IntType Int16)     = 1-  fromEnum (IntType Int32)     = 2-  fromEnum (IntType Int64)     = 3-  fromEnum (FloatType Float32) = 4-  fromEnum (FloatType Float64) = 5-  fromEnum Bool                = 6-  fromEnum Cert                = 7--instance Bounded PrimType where-  minBound = IntType Int8-  maxBound = Cert--instance Pretty PrimType where-  ppr (IntType t)   = ppr t-  ppr (FloatType t) = ppr t-  ppr Bool          = text "bool"-  ppr Cert          = text "cert"---- | A list of all primitive types.-allPrimTypes :: [PrimType]-allPrimTypes = map IntType allIntTypes ++-               map FloatType allFloatTypes ++-               [Bool, Cert]---- | An integer value.-data IntValue = Int8Value !Int8-              | Int16Value !Int16-              | Int32Value !Int32-              | Int64Value !Int64-               deriving (Eq, Ord, Show)--instance Pretty IntValue where-  ppr (Int8Value v)  = text $ show v ++ "i8"-  ppr (Int16Value v) = text $ show v ++ "i16"-  ppr (Int32Value v) = text $ show v ++ "i32"-  ppr (Int64Value v) = text $ show v ++ "i64"---- | Create an t'IntValue' from a type and an 'Integer'.-intValue :: Integral int => IntType -> int -> IntValue-intValue Int8  = Int8Value . fromIntegral-intValue Int16 = Int16Value . fromIntegral-intValue Int32 = Int32Value . fromIntegral-intValue Int64 = Int64Value . fromIntegral---- | The type of an integer value.-intValueType :: IntValue -> IntType-intValueType Int8Value{}  = Int8-intValueType Int16Value{} = Int16-intValueType Int32Value{} = Int32-intValueType Int64Value{} = Int64---- | Convert an t'IntValue' to any 'Integral' type.-valueIntegral :: Integral int => IntValue -> int-valueIntegral (Int8Value  v) = fromIntegral v-valueIntegral (Int16Value v) = fromIntegral v-valueIntegral (Int32Value v) = fromIntegral v-valueIntegral (Int64Value v) = fromIntegral v---- | A floating-point value.-data FloatValue = Float32Value !Float-                | Float64Value !Double-               deriving (Show)--instance Eq FloatValue where-  Float32Value x == Float32Value y = isNaN x && isNaN y || x == y-  Float64Value x == Float64Value y = isNaN x && isNaN y || x == y-  Float32Value _ == Float64Value _ = False-  Float64Value _ == Float32Value _ = False---- The derived Ord instance does not handle NaNs correctly.-instance Ord FloatValue where-  Float32Value x <= Float32Value y = x <= y-  Float64Value x <= Float64Value y = x <= y-  Float32Value _ <= Float64Value _ = True-  Float64Value _ <= Float32Value _ = False--  Float32Value x < Float32Value y = x < y-  Float64Value x < Float64Value y = x < y-  Float32Value _ < Float64Value _ = True-  Float64Value _ < Float32Value _ = False--  (>) = flip (<)-  (>=) = flip (<=)--instance Pretty FloatValue where-  ppr (Float32Value v)-    | isInfinite v, v >= 0 = text "f32.inf"-    | isInfinite v, v <  0 = text "-f32.inf"-    | isNaN v = text "f32.nan"-    | otherwise = text $ show v ++ "f32"-  ppr (Float64Value v)-    | isInfinite v, v >= 0 = text "f64.inf"-    | isInfinite v, v <  0 = text "-f64.inf"-    | isNaN v = text "f64.nan"-    | otherwise = text $ show v ++ "f64"---- | Create a t'FloatValue' from a type and a 'Rational'.-floatValue :: Real num => FloatType -> num -> FloatValue-floatValue Float32 = Float32Value . fromRational . toRational-floatValue Float64 = Float64Value . fromRational . toRational---- | The type of a floating-point value.-floatValueType :: FloatValue -> FloatType-floatValueType Float32Value{} = Float32-floatValueType Float64Value{} = Float64---- | Non-array values.-data PrimValue = IntValue !IntValue-               | FloatValue !FloatValue-               | BoolValue !Bool-               | Checked -- ^ The only value of type @cert@.-               deriving (Eq, Ord, Show)--instance Pretty PrimValue where-  ppr (IntValue v)      = ppr v-  ppr (BoolValue True)  = text "true"-  ppr (BoolValue False) = text "false"-  ppr (FloatValue v)    = ppr v-  ppr Checked           = text "checked"---- | The type of a basic value.-primValueType :: PrimValue -> PrimType-primValueType (IntValue v)   = IntType $ intValueType v-primValueType (FloatValue v) = FloatType $ floatValueType v-primValueType BoolValue{}    = Bool-primValueType Checked        = Cert---- | A "blank" value of the given primitive type - this is zero, or--- whatever is close to it.  Don't depend on this value, but use it--- for e.g. creating arrays to be populated by do-loops.-blankPrimValue :: PrimType -> PrimValue-blankPrimValue (IntType Int8)      = IntValue $ Int8Value 0-blankPrimValue (IntType Int16)     = IntValue $ Int16Value 0-blankPrimValue (IntType Int32)     = IntValue $ Int32Value 0-blankPrimValue (IntType Int64)     = IntValue $ Int64Value 0-blankPrimValue (FloatType Float32) = FloatValue $ Float32Value 0.0-blankPrimValue (FloatType Float64) = FloatValue $ Float64Value 0.0-blankPrimValue Bool                = BoolValue False-blankPrimValue Cert                = Checked---- | Various unary operators.  It is a bit ad-hoc what is a unary--- operator and what is a built-in function.  Perhaps these should all--- go away eventually.-data UnOp = Not -- ^ E.g., @! True == False@.-          | Complement IntType -- ^ E.g., @~(~1) = 1@.-          | Abs IntType -- ^ @abs(-2) = 2@.-          | FAbs FloatType -- ^ @fabs(-2.0) = 2.0@.-          | SSignum IntType -- ^ Signed sign function: @ssignum(-2)@ = -1.-          | USignum IntType -- ^ Unsigned sign function: @usignum(2)@ = 1.-             deriving (Eq, Ord, Show)---- | What to do in case of arithmetic overflow.  Futhark's semantics--- are that overflow does wraparound, but for generated code (like--- address arithmetic), it can be beneficial for overflow to be--- undefined behaviour, as it allows better optimisation of things--- such as GPU kernels.------ Note that all values of this type are considered equal for 'Eq' and--- 'Ord'.-data Overflow = OverflowWrap | OverflowUndef-              deriving (Show)--instance Eq Overflow where-  _ == _ = True--instance Ord Overflow where-  _ `compare` _ = EQ---- | Whether something is safe or unsafe (mostly function calls, and--- in the context of whether operations are dynamically checked).--- When we inline an 'Unsafe' function, we remove all safety checks in--- its body.  The 'Ord' instance picks 'Unsafe' as being less than--- 'Safe'.------ For operations like integer division, a safe division will not--- explode the computer in case of division by zero, but instead--- return some unspecified value.  This always involves a run-time--- check, so generally the unsafe variant is what the compiler will--- insert, but guarded by an explicit assertion elsewhere.  Safe--- operations are useful when the optimiser wants to move e.g. a--- division to a location where the divisor may be zero, but where the--- result will only be used when it is non-zero (so it doesn't matter--- what result is provided with a zero divisor, as long as the program--- keeps running).-data Safety = Unsafe | Safe deriving (Eq, Ord, Show)---- | Binary operators.  These correspond closely to the binary operators in--- LLVM.  Most are parametrised by their expected input and output--- types.-data BinOp = Add IntType Overflow -- ^ Integer addition.-           | FAdd FloatType -- ^ Floating-point addition.--           | Sub IntType Overflow -- ^ Integer subtraction.-           | FSub FloatType -- ^ Floating-point subtraction.--           | Mul IntType Overflow -- ^ Integer multiplication.-           | FMul FloatType -- ^ Floating-point multiplication.--           | UDiv IntType Safety-             -- ^ Unsigned integer division.  Rounds towards-             -- negativity infinity.  Note: this is different-             -- from LLVM.-           | UDivUp IntType Safety-             -- ^ Unsigned integer division.  Rounds towards positive-             -- infinity.--           | SDiv IntType Safety-             -- ^ Signed integer division.  Rounds towards-             -- negativity infinity.  Note: this is different-             -- from LLVM.-           | SDivUp IntType Safety-             -- ^ Signed integer division.  Rounds towards positive-             -- infinity.--           | FDiv FloatType -- ^ Floating-point division.-           | FMod FloatType -- ^ Floating-point modulus.--           | UMod IntType Safety-             -- ^ Unsigned integer modulus; the countepart to 'UDiv'.-           | SMod IntType Safety-             -- ^ Signed integer modulus; the countepart to 'SDiv'.--           | SQuot IntType Safety-             -- ^ Signed integer division.  Rounds towards zero.  This-             -- corresponds to the @sdiv@ instruction in LLVM and-             -- integer division in C.-           | SRem IntType Safety-             -- ^ Signed integer division.  Rounds towards zero.  This-             -- corresponds to the @srem@ instruction in LLVM and-             -- integer modulo in C.--           | SMin IntType-             -- ^ Returns the smallest of two signed integers.-           | UMin IntType-             -- ^ Returns the smallest of two unsigned integers.-           | FMin FloatType-             -- ^ Returns the smallest of two floating-point numbers.-           | SMax IntType-             -- ^ Returns the greatest of two signed integers.-           | UMax IntType-             -- ^ Returns the greatest of two unsigned integers.-           | FMax FloatType-             -- ^ Returns the greatest of two floating-point numbers.--           | Shl IntType -- ^ Left-shift.-           | LShr IntType -- ^ Logical right-shift, zero-extended.-           | AShr IntType -- ^ Arithmetic right-shift, sign-extended.--           | And IntType -- ^ Bitwise and.-           | Or IntType -- ^ Bitwise or.-           | Xor IntType -- ^ Bitwise exclusive-or.--           | Pow IntType -- ^ Integer exponentiation.-           | FPow FloatType -- ^ Floating-point exponentiation.--           | LogAnd -- ^ Boolean and - not short-circuiting.-           | LogOr -- ^ Boolean or - not short-circuiting.-             deriving (Eq, Ord, Show)---- | Comparison operators are like 'BinOp's, but they always return a--- boolean value.  The somewhat ugly constructor names are straight--- out of LLVM.-data CmpOp = CmpEq PrimType -- ^ All types equality.-           | CmpUlt IntType -- ^ Unsigned less than.-           | CmpUle IntType -- ^ Unsigned less than or equal.-           | CmpSlt IntType -- ^ Signed less than.-           | CmpSle IntType -- ^ Signed less than or equal.--             -- Comparison operators for floating-point values.  TODO: extend-             -- this to handle NaNs and such, like the LLVM fcmp instruction.-           | FCmpLt FloatType -- ^ Floating-point less than.-           | FCmpLe FloatType -- ^ Floating-point less than or equal.--           -- Boolean comparison.-           | CmpLlt -- ^ Boolean less than.-           | CmpLle -- ^ Boolean less than or equal.-             deriving (Eq, Ord, Show)---- | Conversion operators try to generalise the @from t0 x to t1@--- instructions from LLVM.-data ConvOp = ZExt IntType IntType-              -- ^ Zero-extend the former integer type to the latter.-              -- If the new type is smaller, the result is a-              -- truncation.-            | SExt IntType IntType-              -- ^ Sign-extend the former integer type to the latter.-              -- If the new type is smaller, the result is a-              -- truncation.-            | FPConv FloatType FloatType-              -- ^ Convert value of the former floating-point type to-              -- the latter.  If the new type is smaller, the result-              -- is a truncation.-            | FPToUI FloatType IntType-              -- ^ Convert a floating-point value to the nearest-              -- unsigned integer (rounding towards zero).-            | FPToSI FloatType IntType-              -- ^ Convert a floating-point value to the nearest-              -- signed integer (rounding towards zero).-            | UIToFP IntType FloatType-              -- ^ Convert an unsigned integer to a floating-point value.-            | SIToFP IntType FloatType-              -- ^ Convert a signed integer to a floating-point value.-            | IToB IntType-              -- ^ Convert an integer to a boolean value.  Zero-              -- becomes false; anything else is true.-            | BToI IntType-              -- ^ Convert a boolean to an integer.  True is converted-              -- to 1 and False to 0.-             deriving (Eq, Ord, Show)---- | A list of all unary operators for all types.-allUnOps :: [UnOp]-allUnOps = Not :-           map Complement [minBound..maxBound] ++-           map Abs [minBound..maxBound] ++-           map FAbs [minBound..maxBound] ++-           map SSignum [minBound..maxBound] ++-           map USignum [minBound..maxBound]---- | A list of all binary operators for all types.-allBinOps :: [BinOp]-allBinOps = concat [ map (`Add` OverflowWrap) allIntTypes-                   , map FAdd allFloatTypes-                   , map (`Sub` OverflowWrap) allIntTypes-                   , map FSub allFloatTypes-                   , map (`Mul` OverflowWrap) allIntTypes-                   , map FMul allFloatTypes-                   , map (`UDiv` Unsafe) allIntTypes-                   , map (`UDivUp` Unsafe) allIntTypes-                   , map (`SDiv` Unsafe) allIntTypes-                   , map (`SDivUp` Unsafe) allIntTypes-                   , map FDiv allFloatTypes-                   , map FMod allFloatTypes-                   , map (`UMod` Unsafe) allIntTypes-                   , map (`SMod` Unsafe) allIntTypes-                   , map (`SQuot` Unsafe) allIntTypes-                   , map (`SRem` Unsafe) allIntTypes-                   , map SMin allIntTypes-                   , map UMin allIntTypes-                   , map FMin allFloatTypes-                   , map SMax allIntTypes-                   , map UMax allIntTypes-                   , map FMax allFloatTypes-                   , map Shl allIntTypes-                   , map LShr allIntTypes-                   , map AShr allIntTypes-                   , map And allIntTypes-                   , map Or allIntTypes-                   , map Xor allIntTypes-                   , map Pow allIntTypes-                   , map FPow allFloatTypes-                   , [LogAnd, LogOr]-                   ]---- | A list of all comparison operators for all types.-allCmpOps :: [CmpOp]-allCmpOps = concat [ map CmpEq allPrimTypes-                   , map CmpUlt allIntTypes-                   , map CmpUle allIntTypes-                   , map CmpSlt allIntTypes-                   , map CmpSle allIntTypes-                   , map FCmpLt allFloatTypes-                   , map FCmpLe allFloatTypes-                   ]---- | A list of all conversion operators for all types.-allConvOps :: [ConvOp]-allConvOps = concat [ ZExt <$> allIntTypes <*> allIntTypes-                    , SExt <$> allIntTypes <*> allIntTypes-                    , FPConv <$> allFloatTypes <*> allFloatTypes-                    , FPToUI <$> allFloatTypes <*> allIntTypes-                    , FPToSI <$> allFloatTypes <*> allIntTypes-                    , UIToFP <$> allIntTypes <*> allFloatTypes-                    , SIToFP <$> allIntTypes <*> allFloatTypes-                    , IToB <$> allIntTypes-                    , BToI <$> allIntTypes-                    ]---- | Apply an 'UnOp' to an operand.  Returns 'Nothing' if the--- application is mistyped.-doUnOp :: UnOp -> PrimValue -> Maybe PrimValue-doUnOp Not (BoolValue b)         = Just $ BoolValue $ not b-doUnOp Complement{} (IntValue v) = Just $ IntValue $ doComplement v-doUnOp Abs{} (IntValue v)        = Just $ IntValue $ doAbs v-doUnOp FAbs{} (FloatValue v)     = Just $ FloatValue $ doFAbs v-doUnOp SSignum{} (IntValue v)    = Just $ IntValue $ doSSignum v-doUnOp USignum{} (IntValue v)    = Just $ IntValue $ doUSignum v-doUnOp _ _                       = Nothing---- | E.g., @~(~1) = 1@.-doComplement :: IntValue -> IntValue-doComplement v = intValue (intValueType v) $ complement $ intToInt64 v---- | @abs(-2) = 2@.-doAbs :: IntValue -> IntValue-doAbs v = intValue (intValueType v) $ abs $ intToInt64 v---- | @abs(-2.0) = 2.0@.-doFAbs :: FloatValue -> FloatValue-doFAbs v = floatValue (floatValueType v) $ abs $ floatToDouble v---- | @ssignum(-2)@ = -1.-doSSignum :: IntValue -> IntValue-doSSignum v = intValue (intValueType v) $ signum $ intToInt64 v---- | @usignum(-2)@ = -1.-doUSignum :: IntValue -> IntValue-doUSignum v = intValue (intValueType v) $ signum $ intToWord64 v---- | Apply a 'BinOp' to an operand.  Returns 'Nothing' if the--- application is mistyped, or outside the domain (e.g. division by--- zero).-doBinOp :: BinOp -> PrimValue -> PrimValue -> Maybe PrimValue-doBinOp Add{}    = doIntBinOp doAdd-doBinOp FAdd{}   = doFloatBinOp (+) (+)-doBinOp Sub{}    = doIntBinOp doSub-doBinOp FSub{}   = doFloatBinOp (-) (-)-doBinOp Mul{}    = doIntBinOp doMul-doBinOp FMul{}   = doFloatBinOp (*) (*)-doBinOp UDiv{}   = doRiskyIntBinOp doUDiv-doBinOp UDivUp{} = doRiskyIntBinOp doUDivUp-doBinOp SDiv{}   = doRiskyIntBinOp doSDiv-doBinOp SDivUp{} = doRiskyIntBinOp doSDivUp-doBinOp FDiv{}   = doFloatBinOp (/) (/)-doBinOp FMod{}   = doFloatBinOp mod' mod'-doBinOp UMod{}   = doRiskyIntBinOp doUMod-doBinOp SMod{}   = doRiskyIntBinOp doSMod-doBinOp SQuot{}  = doRiskyIntBinOp doSQuot-doBinOp SRem{}   = doRiskyIntBinOp doSRem-doBinOp SMin{}   = doIntBinOp doSMin-doBinOp UMin{}   = doIntBinOp doUMin-doBinOp FMin{}   = doFloatBinOp min min-doBinOp SMax{}   = doIntBinOp doSMax-doBinOp UMax{}   = doIntBinOp doUMax-doBinOp FMax{}   = doFloatBinOp max max-doBinOp Shl{}    = doIntBinOp doShl-doBinOp LShr{}   = doIntBinOp doLShr-doBinOp AShr{}   = doIntBinOp doAShr-doBinOp And{}    = doIntBinOp doAnd-doBinOp Or{}     = doIntBinOp doOr-doBinOp Xor{}    = doIntBinOp doXor-doBinOp Pow{}    = doRiskyIntBinOp doPow-doBinOp FPow{}   = doFloatBinOp (**) (**)-doBinOp LogAnd{} = doBoolBinOp (&&)-doBinOp LogOr{}  = doBoolBinOp (||)--doIntBinOp :: (IntValue -> IntValue -> IntValue) -> PrimValue -> PrimValue-           -> Maybe PrimValue-doIntBinOp f (IntValue v1) (IntValue v2) =-  Just $ IntValue $ f v1 v2-doIntBinOp _ _ _ = Nothing--doRiskyIntBinOp :: (IntValue -> IntValue -> Maybe IntValue) -> PrimValue -> PrimValue-           -> Maybe PrimValue-doRiskyIntBinOp f (IntValue v1) (IntValue v2) =-  IntValue <$> f v1 v2-doRiskyIntBinOp _ _ _ = Nothing--doFloatBinOp :: (Float -> Float -> Float)-             -> (Double -> Double -> Double)-             -> PrimValue -> PrimValue-             -> Maybe PrimValue-doFloatBinOp f32 _ (FloatValue (Float32Value v1)) (FloatValue (Float32Value v2)) =-  Just $ FloatValue $ Float32Value $ f32 v1 v2-doFloatBinOp _ f64 (FloatValue (Float64Value v1)) (FloatValue (Float64Value v2)) =-  Just $ FloatValue $ Float64Value $ f64 v1 v2-doFloatBinOp _ _ _ _ = Nothing--doBoolBinOp :: (Bool -> Bool -> Bool) -> PrimValue -> PrimValue-            -> Maybe PrimValue-doBoolBinOp f (BoolValue v1) (BoolValue v2) =-  Just $ BoolValue $ f v1 v2-doBoolBinOp _ _ _ = Nothing---- | Integer addition.-doAdd :: IntValue -> IntValue -> IntValue-doAdd v1 v2 = intValue (intValueType v1) $ intToInt64 v1 + intToInt64 v2---- | Integer subtraction.-doSub :: IntValue -> IntValue -> IntValue-doSub v1 v2 = intValue (intValueType v1) $ intToInt64 v1 - intToInt64 v2---- | Integer multiplication.-doMul :: IntValue -> IntValue -> IntValue-doMul v1 v2 = intValue (intValueType v1) $ intToInt64 v1 * intToInt64 v2---- | Unsigned integer division.  Rounds towards negativity infinity.--- Note: this is different from LLVM.-doUDiv :: IntValue -> IntValue -> Maybe IntValue-doUDiv v1 v2-  | zeroIshInt v2 = Nothing-  | otherwise = Just $ intValue (intValueType v1) $-                intToWord64 v1 `div` intToWord64 v2---- | Unsigned integer division.  Rounds towards positive infinity.-doUDivUp :: IntValue -> IntValue -> Maybe IntValue-doUDivUp v1 v2-  | zeroIshInt v2 = Nothing-  | otherwise = Just $ intValue (intValueType v1) $-                (intToWord64 v1 + intToWord64 v2 - 1) `div` intToWord64 v2---- | Signed integer division.  Rounds towards negativity infinity.--- Note: this is different from LLVM.-doSDiv :: IntValue -> IntValue -> Maybe IntValue-doSDiv v1 v2-  | zeroIshInt v2 = Nothing-  | otherwise = Just $ intValue (intValueType v1) $-                intToInt64 v1 `div` intToInt64 v2---- | Signed integer division.  Rounds towards positive infinity.-doSDivUp :: IntValue -> IntValue -> Maybe IntValue-doSDivUp v1 v2-  | zeroIshInt v2 = Nothing-  | otherwise = Just $ intValue (intValueType v1) $-                (intToInt64 v1 + intToInt64 v2 - 1) `div` intToInt64 v2---- | Unsigned integer modulus; the countepart to 'UDiv'.-doUMod :: IntValue -> IntValue -> Maybe IntValue-doUMod v1 v2-  | zeroIshInt v2 = Nothing-  | otherwise = Just $ intValue (intValueType v1) $ intToWord64 v1 `mod` intToWord64 v2---- | Signed integer modulus; the countepart to 'SDiv'.-doSMod :: IntValue -> IntValue -> Maybe IntValue-doSMod v1 v2-  | zeroIshInt v2 = Nothing-  | otherwise = Just $ intValue (intValueType v1) $ intToInt64 v1 `mod` intToInt64 v2---- | Signed integer division.  Rounds towards zero.--- This corresponds to the @sdiv@ instruction in LLVM.-doSQuot :: IntValue -> IntValue -> Maybe IntValue-doSQuot v1 v2-  | zeroIshInt v2 = Nothing-  | otherwise = Just $ intValue (intValueType v1) $ intToInt64 v1 `quot` intToInt64 v2---- | Signed integer division.  Rounds towards zero.--- This corresponds to the @srem@ instruction in LLVM.-doSRem :: IntValue -> IntValue -> Maybe IntValue-doSRem v1 v2-  | zeroIshInt v2 = Nothing-  | otherwise = Just $ intValue (intValueType v1) $ intToInt64 v1 `rem` intToInt64 v2---- | Minimum of two signed integers.-doSMin :: IntValue -> IntValue -> IntValue-doSMin v1 v2 = intValue (intValueType v1) $ intToInt64 v1 `min` intToInt64 v2---- | Minimum of two unsigned integers.-doUMin :: IntValue -> IntValue -> IntValue-doUMin v1 v2 = intValue (intValueType v1) $ intToWord64 v1 `min` intToWord64 v2---- | Maximum of two signed integers.-doSMax :: IntValue -> IntValue -> IntValue-doSMax v1 v2 = intValue (intValueType v1) $ intToInt64 v1 `max` intToInt64 v2---- | Maximum of two unsigned integers.-doUMax :: IntValue -> IntValue -> IntValue-doUMax v1 v2 = intValue (intValueType v1) $ intToWord64 v1 `max` intToWord64 v2---- | Left-shift.-doShl :: IntValue -> IntValue -> IntValue-doShl v1 v2 = intValue (intValueType v1) $ intToInt64 v1 `shift` intToInt v2---- | Logical right-shift, zero-extended.-doLShr :: IntValue -> IntValue -> IntValue-doLShr v1 v2 = intValue (intValueType v1) $ intToWord64 v1 `shift` negate (intToInt v2)---- | Arithmetic right-shift, sign-extended.-doAShr :: IntValue -> IntValue -> IntValue-doAShr v1 v2 = intValue (intValueType v1) $ intToInt64 v1 `shift` negate (intToInt v2)---- | Bitwise and.-doAnd :: IntValue -> IntValue -> IntValue-doAnd v1 v2 = intValue (intValueType v1) $ intToWord64 v1 .&. intToWord64 v2---- | Bitwise or.-doOr :: IntValue -> IntValue -> IntValue-doOr v1 v2 = intValue (intValueType v1) $ intToWord64 v1 .|. intToWord64 v2---- | Bitwise exclusive-or.-doXor :: IntValue -> IntValue -> IntValue-doXor v1 v2 = intValue (intValueType v1) $ intToWord64 v1 `xor` intToWord64 v2---- | Signed integer exponentatation.-doPow :: IntValue -> IntValue -> Maybe IntValue-doPow v1 v2-  | negativeIshInt v2 = Nothing-  | otherwise         = Just $ intValue (intValueType v1) $ intToInt64 v1 ^ intToInt64 v2---- | Apply a 'ConvOp' to an operand.  Returns 'Nothing' if the--- application is mistyped.-doConvOp :: ConvOp -> PrimValue -> Maybe PrimValue-doConvOp (ZExt _ to) (IntValue v)     = Just $ IntValue $ doZExt v to-doConvOp (SExt _ to) (IntValue v)     = Just $ IntValue $ doSExt v to-doConvOp (FPConv _ to) (FloatValue v) = Just $ FloatValue $ doFPConv v to-doConvOp (FPToUI _ to) (FloatValue v) = Just $ IntValue $ doFPToUI v to-doConvOp (FPToSI _ to) (FloatValue v) = Just $ IntValue $ doFPToSI v to-doConvOp (UIToFP _ to) (IntValue v)   = Just $ FloatValue $ doUIToFP v to-doConvOp (SIToFP _ to) (IntValue v)   = Just $ FloatValue $ doSIToFP v to-doConvOp (IToB _) (IntValue v)        = Just $ BoolValue $ intToInt64 v /= 0-doConvOp (BToI to) (BoolValue v)      = Just $ IntValue $ intValue to $ if v then 1 else 0::Int-doConvOp _ _                          = Nothing---- | Zero-extend the given integer value to the size of the given--- type.  If the type is smaller than the given value, the result is a--- truncation.-doZExt :: IntValue -> IntType -> IntValue-doZExt (Int8Value x) t  = intValue t $ toInteger (fromIntegral x :: Word8)-doZExt (Int16Value x) t = intValue t $ toInteger (fromIntegral x :: Word16)-doZExt (Int32Value x) t = intValue t $ toInteger (fromIntegral x :: Word32)-doZExt (Int64Value x) t = intValue t $ toInteger (fromIntegral x :: Word64)---- | Sign-extend the given integer value to the size of the given--- type.  If the type is smaller than the given value, the result is a--- truncation.-doSExt :: IntValue -> IntType -> IntValue-doSExt (Int8Value x) t  = intValue t $ toInteger x-doSExt (Int16Value x) t = intValue t $ toInteger x-doSExt (Int32Value x) t = intValue t $ toInteger x-doSExt (Int64Value x) t = intValue t $ toInteger x---- | Convert the former floating-point type to the latter.-doFPConv :: FloatValue -> FloatType -> FloatValue-doFPConv (Float32Value v) Float32 = Float32Value v-doFPConv (Float64Value v) Float32 = Float32Value $ fromRational $ toRational v-doFPConv (Float64Value v) Float64 = Float64Value v-doFPConv (Float32Value v) Float64 = Float64Value $ fromRational $ toRational v---- | Convert a floating-point value to the nearest--- unsigned integer (rounding towards zero).-doFPToUI :: FloatValue -> IntType -> IntValue-doFPToUI v t = intValue t (truncate $ floatToDouble v :: Word64)---- | Convert a floating-point value to the nearest--- signed integer (rounding towards zero).-doFPToSI :: FloatValue -> IntType -> IntValue-doFPToSI v t = intValue t (truncate $ floatToDouble v :: Word64)---- | Convert an unsigned integer to a floating-point value.-doUIToFP :: IntValue -> FloatType -> FloatValue-doUIToFP v t = floatValue t $ intToWord64 v---- | Convert a signed integer to a floating-point value.-doSIToFP :: IntValue -> FloatType -> FloatValue-doSIToFP v t = floatValue t $ intToInt64 v---- | Apply a 'CmpOp' to an operand.  Returns 'Nothing' if the--- application is mistyped.-doCmpOp :: CmpOp -> PrimValue -> PrimValue -> Maybe Bool-doCmpOp CmpEq{} v1 v2                            = Just $ doCmpEq v1 v2-doCmpOp CmpUlt{} (IntValue v1) (IntValue v2)     = Just $ doCmpUlt v1 v2-doCmpOp CmpUle{} (IntValue v1) (IntValue v2)     = Just $ doCmpUle v1 v2-doCmpOp CmpSlt{} (IntValue v1) (IntValue v2)     = Just $ doCmpSlt v1 v2-doCmpOp CmpSle{} (IntValue v1) (IntValue v2)     = Just $ doCmpSle v1 v2-doCmpOp FCmpLt{} (FloatValue v1) (FloatValue v2) = Just $ doFCmpLt v1 v2-doCmpOp FCmpLe{} (FloatValue v1) (FloatValue v2) = Just $ doFCmpLe v1 v2-doCmpOp CmpLlt{} (BoolValue v1) (BoolValue v2)   = Just $ not v1 && v2-doCmpOp CmpLle{} (BoolValue v1) (BoolValue v2)   = Just $ not (v1 && not v2)-doCmpOp _ _ _                                    = Nothing---- | Compare any two primtive values for exact equality.-doCmpEq :: PrimValue -> PrimValue -> Bool-doCmpEq (FloatValue (Float32Value v1)) (FloatValue (Float32Value v2)) = v1 == v2-doCmpEq (FloatValue (Float64Value v1)) (FloatValue (Float64Value v2)) = v1 == v2-doCmpEq v1 v2 = v1 == v2---- | Unsigned less than.-doCmpUlt :: IntValue -> IntValue -> Bool-doCmpUlt v1 v2 = intToWord64 v1 < intToWord64 v2---- | Unsigned less than or equal.-doCmpUle :: IntValue -> IntValue -> Bool-doCmpUle v1 v2 = intToWord64 v1 <= intToWord64 v2---- | Signed less than.-doCmpSlt :: IntValue -> IntValue -> Bool-doCmpSlt = (<)---- | Signed less than or equal.-doCmpSle :: IntValue -> IntValue -> Bool-doCmpSle = (<=)---- | Floating-point less than.-doFCmpLt :: FloatValue -> FloatValue -> Bool-doFCmpLt = (<)---- | Floating-point less than or equal.-doFCmpLe :: FloatValue -> FloatValue -> Bool-doFCmpLe = (<=)---- | Translate an t'IntValue' to 'Word64'.  This is guaranteed to fit.-intToWord64 :: IntValue -> Word64-intToWord64 (Int8Value v)  = fromIntegral (fromIntegral v :: Word8)-intToWord64 (Int16Value v) = fromIntegral (fromIntegral v :: Word16)-intToWord64 (Int32Value v) = fromIntegral (fromIntegral v :: Word32)-intToWord64 (Int64Value v) = fromIntegral (fromIntegral v :: Word64)---- | Translate an t'IntValue' to t'Int64'.  This is guaranteed to fit.-intToInt64 :: IntValue -> Int64-intToInt64 (Int8Value v)  = fromIntegral v-intToInt64 (Int16Value v) = fromIntegral v-intToInt64 (Int32Value v) = fromIntegral v-intToInt64 (Int64Value v) = fromIntegral v---- | Careful - there is no guarantee this will fit.-intToInt :: IntValue -> Int-intToInt = fromIntegral . intToInt64--floatToDouble :: FloatValue -> Double-floatToDouble (Float32Value v) = fromRational $ toRational v-floatToDouble (Float64Value v) = v---- | The result type of a binary operator.-binOpType :: BinOp -> PrimType-binOpType (Add t _) = IntType t-binOpType (Sub t _) = IntType t-binOpType (Mul t _) = IntType t-binOpType (SDiv t _)   = IntType t-binOpType (SDivUp t _) = IntType t-binOpType (SMod t _)  = IntType t-binOpType (SQuot t _) = IntType t-binOpType (SRem t _)  = IntType t-binOpType (UDiv t _)  = IntType t-binOpType (UDivUp t _) = IntType t-binOpType (UMod t _)   = IntType t-binOpType (SMin t)  = IntType t-binOpType (UMin t)  = IntType t-binOpType (FMin t)  = FloatType t-binOpType (SMax t)  = IntType t-binOpType (UMax t)  = IntType t-binOpType (FMax t)  = FloatType t-binOpType (Shl t)   = IntType t-binOpType (LShr t)  = IntType t-binOpType (AShr t)  = IntType t-binOpType (And t)   = IntType t-binOpType (Or t)    = IntType t-binOpType (Xor t)   = IntType t-binOpType (Pow t)   = IntType t-binOpType (FPow t)  = FloatType t-binOpType LogAnd    = Bool-binOpType LogOr     = Bool-binOpType (FAdd t)  = FloatType t-binOpType (FSub t)  = FloatType t-binOpType (FMul t)  = FloatType t-binOpType (FDiv t)  = FloatType t-binOpType (FMod t)  = FloatType t---- | The operand types of a comparison operator.-cmpOpType :: CmpOp -> PrimType-cmpOpType (CmpEq t) = t-cmpOpType (CmpSlt t) = IntType t-cmpOpType (CmpSle t) = IntType t-cmpOpType (CmpUlt t) = IntType t-cmpOpType (CmpUle t) = IntType t-cmpOpType (FCmpLt t) = FloatType t-cmpOpType (FCmpLe t) = FloatType t-cmpOpType CmpLlt = Bool-cmpOpType CmpLle = Bool---- | The operand and result type of a unary operator.-unOpType :: UnOp -> PrimType-unOpType (SSignum t)    = IntType t-unOpType (USignum t)    = IntType t-unOpType Not            = Bool-unOpType (Complement t) = IntType t-unOpType (Abs t)        = IntType t-unOpType (FAbs t)       = FloatType t---- | The input and output types of a conversion operator.-convOpType :: ConvOp -> (PrimType, PrimType)-convOpType (ZExt from to) = (IntType from, IntType to)-convOpType (SExt from to) = (IntType from, IntType to)-convOpType (FPConv from to) = (FloatType from, FloatType to)-convOpType (FPToUI from to) = (FloatType from, IntType to)-convOpType (FPToSI from to) = (FloatType from, IntType to)-convOpType (UIToFP from to) = (IntType from, FloatType to)-convOpType (SIToFP from to) = (IntType from, FloatType to)-convOpType (IToB from) = (IntType from, Bool)-convOpType (BToI to) = (Bool, IntType to)--floatToWord :: Float -> Word32-floatToWord = G.runGet G.getWord32le . P.runPut . P.putFloatle--wordToFloat :: Word32 -> Float-wordToFloat = G.runGet G.getFloatle . P.runPut . P.putWord32le--doubleToWord :: Double -> Word64-doubleToWord = G.runGet G.getWord64le . P.runPut . P.putDoublele--wordToDouble :: Word64 -> Double-wordToDouble = G.runGet G.getDoublele . P.runPut . P.putWord64le---- | A mapping from names of primitive functions to their parameter--- types, their result type, and a function for evaluating them.-primFuns :: M.Map String ([PrimType], PrimType,-                          [PrimValue] -> Maybe PrimValue)-primFuns = M.fromList-  [ f32 "sqrt32" sqrt, f64 "sqrt64" sqrt-  , f32 "log32" log, f64 "log64" log-  , f32 "log10_32" (logBase 10), f64 "log10_64" (logBase 10)-  , f32 "log2_32" (logBase 2), f64 "log2_64" (logBase 2)-  , f32 "exp32" exp, f64 "exp64" exp--  , f32 "sin32" sin, f64 "sin64" sin-  , f32 "sinh32" sinh, f64 "sinh64" sinh-  , f32 "cos32" cos, f64 "cos64" cos-  , f32 "cosh32" cosh, f64 "cosh64" cosh-  , f32 "tan32" tan, f64 "tan64" tan-  , f32 "tanh32" tanh, f64 "tanh64" tanh-  , f32 "asin32" asin, f64 "asin64" asin-  , f32 "asinh32" asinh, f64 "asinh64" asinh-  , f32 "acos32" acos, f64 "acos64" acos-  , f32 "acosh32" acosh, f64 "acosh64" acosh-  , f32 "atan32" atan, f64 "atan64" atan-  , f32 "atanh32" atanh, f64 "atanh64" atanh--  , f32 "round32" roundFloat, f64 "round64" roundDouble-  , f32 "ceil32" ceilFloat, f64 "ceil64" ceilDouble-  , f32 "floor32" floorFloat, f64 "floor64" floorDouble-  , f32 "gamma32" tgammaf, f64 "gamma64" tgamma-  , f32 "lgamma32" lgammaf, f64 "lgamma64" lgamma--  , i8 "clz8" $ IntValue . Int32Value . fromIntegral . countLeadingZeros-  , i16 "clz16" $ IntValue . Int32Value . fromIntegral . countLeadingZeros-  , i32 "clz32" $ IntValue . Int32Value . fromIntegral . countLeadingZeros-  , i64 "clz64" $ IntValue . Int32Value . fromIntegral . countLeadingZeros--  , i8 "ctz8" $ IntValue . Int32Value . fromIntegral . countTrailingZeros-  , i16 "ctz16" $ IntValue . Int32Value . fromIntegral . countTrailingZeros-  , i32 "ctz32" $ IntValue . Int32Value . fromIntegral . countTrailingZeros-  , i64 "ctz64" $ IntValue . Int32Value . fromIntegral . countTrailingZeros--  , i8 "popc8" $ IntValue . Int32Value . fromIntegral . popCount-  , i16 "popc16" $ IntValue . Int32Value . fromIntegral . popCount-  , i32 "popc32" $ IntValue . Int32Value . fromIntegral . popCount-  , i64 "popc64" $ IntValue . Int32Value . fromIntegral . popCount--  , ("mad_hi8", ([IntType Int8, IntType Int8, IntType Int8], IntType Int8,-                 \case-                   [IntValue (Int8Value a), IntValue (Int8Value b), IntValue (Int8Value c)] ->-                     Just $ IntValue . Int8Value $ mad_hi8 (Int8Value a) (Int8Value b) c-                   _ -> Nothing-                ))-  , ("mad_hi16", ([IntType Int16, IntType Int16, IntType Int16], IntType Int16,-                 \case-                   [IntValue (Int16Value a), IntValue (Int16Value b), IntValue (Int16Value c)] ->-                     Just $ IntValue . Int16Value  $ mad_hi16 (Int16Value a) (Int16Value b) c-                   _ -> Nothing-                ))-  , ("mad_hi32", ([IntType Int32, IntType Int32, IntType Int32], IntType Int32,-                  \case-                   [IntValue (Int32Value a), IntValue (Int32Value b), IntValue (Int32Value c)] ->-                     Just $ IntValue . Int32Value  $ mad_hi32 (Int32Value a) (Int32Value b) c-                   _ -> Nothing-                ))-  , ("mad_hi64", ([IntType Int64, IntType Int64, IntType Int64], IntType Int64,-                  \case-                    [IntValue (Int64Value a), IntValue (Int64Value b), IntValue (Int64Value c)] ->-                      Just $ IntValue . Int64Value $ mad_hi64 (Int64Value a) (Int64Value b) c-                    _ -> Nothing-                ))--  , ("mul_hi8", ([IntType Int8, IntType Int8], IntType Int8,-                 \case-                   [IntValue (Int8Value a), IntValue (Int8Value b)] ->-                     Just $ IntValue . Int8Value $ mul_hi8 (Int8Value a) (Int8Value b)-                   _ -> Nothing-                ))-  , ("mul_hi16", ([IntType Int16, IntType Int16], IntType Int16,-                 \case-                   [IntValue (Int16Value a), IntValue (Int16Value b)] ->-                     Just $ IntValue . Int16Value  $ mul_hi16 (Int16Value a) (Int16Value b)-                   _ -> Nothing-                ))-  , ("mul_hi32", ([IntType Int32, IntType Int32], IntType Int32,-                  \case-                   [IntValue (Int32Value a), IntValue (Int32Value b)] ->-                     Just $ IntValue . Int32Value  $ mul_hi32 (Int32Value a) (Int32Value b)-                   _ -> Nothing-                ))-  , ("mul_hi64", ([IntType Int64, IntType Int64], IntType Int64,-                  \case-                    [IntValue (Int64Value a), IntValue (Int64Value b)] ->-                      Just $ IntValue . Int64Value $ mul_hi64 (Int64Value a) (Int64Value b)-                    _ -> Nothing-                ))--  , ("atan2_32",-     ([FloatType Float32, FloatType Float32], FloatType Float32,-      \case-        [FloatValue (Float32Value x), FloatValue (Float32Value y)] ->-          Just $ FloatValue $ Float32Value $ atan2 x y-        _ -> Nothing))-  , ("atan2_64",-     ([FloatType Float64, FloatType Float64], FloatType Float64,-       \case-         [FloatValue (Float64Value x), FloatValue (Float64Value y)] ->-           Just $ FloatValue $ Float64Value $ atan2 x y-         _ -> Nothing))--  , ("isinf32",-     ([FloatType Float32], Bool,-      \case-        [FloatValue (Float32Value x)] -> Just $ BoolValue $ isInfinite x-        _ -> Nothing))-  , ("isinf64",-     ([FloatType Float64], Bool,-      \case-        [FloatValue (Float64Value x)] -> Just $ BoolValue $ isInfinite x-        _ -> Nothing))--  , ("isnan32",-     ([FloatType Float32], Bool,-      \case-        [FloatValue (Float32Value x)] -> Just $ BoolValue $ isNaN x-        _ -> Nothing))-  , ("isnan64",-     ([FloatType Float64], Bool,-      \case-        [FloatValue (Float64Value x)] -> Just $ BoolValue $ isNaN x-        _ -> Nothing))--  , ("to_bits32",-     ([FloatType Float32], IntType Int32,-      \case-        [FloatValue (Float32Value x)] ->-          Just $ IntValue $ Int32Value $ fromIntegral $ floatToWord x-        _ -> Nothing))-  , ("to_bits64",-     ([FloatType Float64], IntType Int64,-      \case-        [FloatValue (Float64Value x)] ->-          Just $ IntValue $ Int64Value $ fromIntegral $ doubleToWord x-        _ -> Nothing))--  , ("from_bits32",-     ([IntType Int32], FloatType Float32,-      \case-        [IntValue (Int32Value x)] ->-          Just $ FloatValue $ Float32Value $ wordToFloat $ fromIntegral x-        _ -> Nothing))-  , ("from_bits64",-     ([IntType Int64], FloatType Float64,-      \case-        [IntValue (Int64Value x)] ->-          Just $ FloatValue $ Float64Value $ wordToDouble $ fromIntegral x-        _ -> Nothing))--  , f32_3 "lerp32" (\v0 v1 t -> v0 + (v1-v0)*max 0 (min 1 t))-  , f64_3 "lerp64" (\v0 v1 t -> v0 + (v1-v0)*max 0 (min 1 t))--  , f32_3 "mad32" (\a b c -> a*b+c)-  , f64_3 "mad64" (\a b c -> a*b+c)--  , f32_3 "fma32" (\a b c -> a*b+c)-  , f64_3 "fma64" (\a b c -> a*b+c)--  ]-  where i8 s f = (s, ([IntType Int8], IntType Int32, i8PrimFun f))-        i16 s f = (s, ([IntType Int16], IntType Int32, i16PrimFun f))-        i32 s f = (s, ([IntType Int32], IntType Int32, i32PrimFun f))-        i64 s f = (s, ([IntType Int64], IntType Int32, i64PrimFun f))-        f32 s f = (s, ([FloatType Float32], FloatType Float32, f32PrimFun f))-        f64 s f = (s, ([FloatType Float64], FloatType Float64, f64PrimFun f))-        f32_3 s f = (s, ([FloatType Float32,FloatType Float32,FloatType Float32],-                         FloatType Float32, f32PrimFun3 f))-        f64_3 s f = (s, ([FloatType Float64,FloatType Float64,FloatType Float64],-                         FloatType Float64, f64PrimFun3 f))--        i8PrimFun f [IntValue (Int8Value x)] =-          Just $ f x-        i8PrimFun _ _ = Nothing--        i16PrimFun f [IntValue (Int16Value x)] =-          Just $ f x-        i16PrimFun _ _ = Nothing--        i32PrimFun f [IntValue (Int32Value x)] =-          Just $ f x-        i32PrimFun _ _ = Nothing--        i64PrimFun f [IntValue (Int64Value x)] =-          Just $ f x-        i64PrimFun _ _ = Nothing--        f32PrimFun f [FloatValue (Float32Value x)] =-          Just $ FloatValue $ Float32Value $ f x-        f32PrimFun _ _ = Nothing--        f64PrimFun f [FloatValue (Float64Value x)] =-          Just $ FloatValue $ Float64Value $ f x-        f64PrimFun _ _ = Nothing--        f32PrimFun3 f [FloatValue (Float32Value a),-                       FloatValue (Float32Value b),-                       FloatValue (Float32Value c)] =-          Just $ FloatValue $ Float32Value $ f a b c-        f32PrimFun3 _ _ = Nothing--        f64PrimFun3 f [FloatValue (Float64Value a),-                       FloatValue (Float64Value b),-                       FloatValue (Float64Value c)] =-          Just $ FloatValue $ Float64Value $ f a b c-        f64PrimFun3 _ _ = Nothing---- | Is the given value kind of zero?-zeroIsh :: PrimValue -> Bool-zeroIsh (IntValue k)                  = zeroIshInt k-zeroIsh (FloatValue (Float32Value k)) = k == 0-zeroIsh (FloatValue (Float64Value k)) = k == 0-zeroIsh (BoolValue False)             = True-zeroIsh _                             = False---- | Is the given value kind of one?-oneIsh :: PrimValue -> Bool-oneIsh (IntValue k)                  = oneIshInt k-oneIsh (FloatValue (Float32Value k)) = k == 1-oneIsh (FloatValue (Float64Value k)) = k == 1-oneIsh (BoolValue True)              = True-oneIsh _                             = False---- | Is the given value kind of negative?-negativeIsh :: PrimValue -> Bool-negativeIsh (IntValue k)                  = negativeIshInt k-negativeIsh (FloatValue (Float32Value k)) = k < 0-negativeIsh (FloatValue (Float64Value k)) = k < 0-negativeIsh (BoolValue _)                 = False-negativeIsh Checked                       = False---- | Is the given integer value kind of zero?-zeroIshInt :: IntValue -> Bool-zeroIshInt (Int8Value k)  = k == 0-zeroIshInt (Int16Value k) = k == 0-zeroIshInt (Int32Value k) = k == 0-zeroIshInt (Int64Value k) = k == 0---- | Is the given integer value kind of one?-oneIshInt :: IntValue -> Bool-oneIshInt (Int8Value k)  = k == 1-oneIshInt (Int16Value k) = k == 1-oneIshInt (Int32Value k) = k == 1-oneIshInt (Int64Value k) = k == 1---- | Is the given integer value kind of negative?-negativeIshInt :: IntValue -> Bool-negativeIshInt (Int8Value k)  = k < 0-negativeIshInt (Int16Value k) = k < 0-negativeIshInt (Int32Value k) = k < 0-negativeIshInt (Int64Value k) = k < 0---- | The size of a value of a given primitive type in bites.-primBitSize :: PrimType -> Int-primBitSize = (*8) . primByteSize---- | The size of a value of a given primitive type in eight-bit bytes.-primByteSize :: Num a => PrimType -> a-primByteSize (IntType t)   = intByteSize t-primByteSize (FloatType t) = floatByteSize t-primByteSize Bool          = 1-primByteSize Cert          = 1---- | The size of a value of a given integer type in eight-bit bytes.-intByteSize :: Num a => IntType -> a-intByteSize Int8  = 1-intByteSize Int16 = 2-intByteSize Int32 = 4-intByteSize Int64 = 8---- | The size of a value of a given floating-point type in eight-bit bytes.-floatByteSize :: Num a => FloatType -> a-floatByteSize Float32 = 4-floatByteSize Float64 = 8---- | True if the given binary operator is commutative.-commutativeBinOp :: BinOp -> Bool-commutativeBinOp Add{} = True-commutativeBinOp FAdd{} = True-commutativeBinOp Mul{} = True-commutativeBinOp FMul{} = True-commutativeBinOp And{} = True-commutativeBinOp Or{} = True-commutativeBinOp Xor{} = True-commutativeBinOp LogOr{} = True-commutativeBinOp LogAnd{} = True-commutativeBinOp SMax{} = True-commutativeBinOp SMin{} = True-commutativeBinOp UMax{} = True-commutativeBinOp UMin{} = True-commutativeBinOp FMax{} = True-commutativeBinOp FMin{} = True-commutativeBinOp _ = False---- Prettyprinting instances--instance Pretty BinOp where-  ppr (Add t OverflowWrap)  = taggedI "add" t-  ppr (Add t OverflowUndef) = taggedI "add_nw" t-  ppr (Sub t OverflowWrap)  = taggedI "sub" t-  ppr (Sub t OverflowUndef) = taggedI "sub_nw" t-  ppr (Mul t OverflowWrap)  = taggedI "mul" t-  ppr (Mul t OverflowUndef) = taggedI "mul_nw" t-  ppr (FAdd t)  = taggedF "fadd" t-  ppr (FSub t)  = taggedF "fsub" t-  ppr (FMul t)  = taggedF "fmul" t-  ppr (UDiv t Safe)    = taggedI "udiv_safe" t-  ppr (UDiv t Unsafe)  = taggedI "udiv" t-  ppr (UDivUp t Safe)   = taggedI "udiv_up_safe" t-  ppr (UDivUp t Unsafe) = taggedI "udiv_up" t-  ppr (UMod t Safe)    = taggedI "umod_safe" t-  ppr (UMod t Unsafe)  = taggedI "umod" t-  ppr (SDiv t Safe)    = taggedI "sdiv_safe" t-  ppr (SDiv t Unsafe)  = taggedI "sdiv" t-  ppr (SDivUp t Safe)   = taggedI "sdiv_up_safe" t-  ppr (SDivUp t Unsafe) = taggedI "sdiv_up" t-  ppr (SMod t Safe)    = taggedI "smod_safe" t-  ppr (SMod t Unsafe)  = taggedI "smod" t-  ppr (SQuot t Safe)   = taggedI "squot_safe" t-  ppr (SQuot t Unsafe) = taggedI "squot" t-  ppr (SRem t Safe)    = taggedI "srem_safe" t-  ppr (SRem t Unsafe)  = taggedI "srem" t-  ppr (FDiv t)  = taggedF "fdiv" t-  ppr (FMod t)  = taggedF "fmod" t-  ppr (SMin t)  = taggedI "smin" t-  ppr (UMin t)  = taggedI "umin" t-  ppr (FMin t)  = taggedF "fmin" t-  ppr (SMax t)  = taggedI "smax" t-  ppr (UMax t)  = taggedI "umax" t-  ppr (FMax t)  = taggedF "fmax" t-  ppr (Shl t)   = taggedI "shl" t-  ppr (LShr t)  = taggedI "lshr" t-  ppr (AShr t)  = taggedI "ashr" t-  ppr (And t)   = taggedI "and" t-  ppr (Or t)    = taggedI "or" t-  ppr (Xor t)   = taggedI "xor" t-  ppr (Pow t)   = taggedI "pow" t-  ppr (FPow t)  = taggedF "fpow" t-  ppr LogAnd    = text "logand"-  ppr LogOr     = text "logor"--instance Pretty CmpOp where-  ppr (CmpEq t)  = text "eq_" <> ppr t-  ppr (CmpUlt t) = taggedI "ult" t-  ppr (CmpUle t) = taggedI "ule" t-  ppr (CmpSlt t) = taggedI "slt" t-  ppr (CmpSle t) = taggedI "sle" t-  ppr (FCmpLt t) = taggedF "lt" t-  ppr (FCmpLe t) = taggedF "le" t-  ppr CmpLlt = text "llt"-  ppr CmpLle = text "lle"--instance Pretty ConvOp where-  ppr op = convOp (convOpFun op) from to-    where (from, to) = convOpType op--instance Pretty UnOp where-  ppr Not            = text "not"-  ppr (Abs t)        = taggedI "abs" t-  ppr (FAbs t)       = taggedF "fabs" t-  ppr (SSignum t)    = taggedI "ssignum" t-  ppr (USignum t)    = taggedI "usignum" t-  ppr (Complement t) = taggedI "complement" t---- | The human-readable name for a 'ConvOp'.  This is used to expose--- the 'ConvOp' in the @intrinsics@ module of a Futhark program.-convOpFun :: ConvOp -> String-convOpFun ZExt{}   = "zext"-convOpFun SExt{}   = "sext"-convOpFun FPConv{} = "fpconv"-convOpFun FPToUI{} = "fptoui"-convOpFun FPToSI{} = "fptosi"-convOpFun UIToFP{} = "uitofp"-convOpFun SIToFP{} = "sitofp"-convOpFun IToB{}   = "itob"-convOpFun BToI{}   = "btoi"--taggedI :: String -> IntType -> Doc-taggedI s Int8  = text $ s ++ "8"-taggedI s Int16 = text $ s ++ "16"-taggedI s Int32 = text $ s ++ "32"-taggedI s Int64 = text $ s ++ "64"--taggedF :: String -> FloatType -> Doc-taggedF s Float32 = text $ s ++ "32"-taggedF s Float64 = text $ s ++ "64"--convOp :: (Pretty from, Pretty to) => String -> from -> to -> Doc-convOp s from to = text s <> text "_" <> ppr from <> text "_" <> ppr to---- | True if signed.  Only makes a difference for integer types.-prettySigned :: Bool -> PrimType -> String-prettySigned True (IntType it) = 'u' : drop 1 (pretty it)-prettySigned _ t = pretty t--mul_hi8 :: IntValue -> IntValue -> Int8-mul_hi8 a b =-  let a' = intToWord64 a-      b' = intToWord64 b-  in fromIntegral (shiftR (a' * b') 8)--mul_hi16 :: IntValue -> IntValue -> Int16-mul_hi16 a b =-  let a' = intToWord64 a-      b' = intToWord64 b-  in fromIntegral (shiftR (a' * b') 16)--mul_hi32 :: IntValue -> IntValue -> Int32-mul_hi32 a b =-  let a' = intToWord64 a-      b' = intToWord64 b-  in fromIntegral (shiftR (a' * b') 32)--mul_hi64 :: IntValue -> IntValue -> Int64-mul_hi64 a b =-  let a' = (toInteger . intToWord64) a-      b' = (toInteger . intToWord64) b-  in fromIntegral (shiftR (a' * b') 64)+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE TypeOperators #-}++-- | Definitions of primitive types, the values that inhabit these+-- types, and operations on these values.  A primitive value can also+-- be called a scalar.+--+-- Essentially, this module describes the subset of the (internal)+-- Futhark language that operates on primitive types.+module Futhark.IR.Primitive+  ( -- * Types+    IntType (..),+    allIntTypes,+    FloatType (..),+    allFloatTypes,+    PrimType (..),+    allPrimTypes,+    module Data.Int,++    -- * Values+    IntValue (..),+    intValue,+    intValueType,+    valueIntegral,+    FloatValue (..),+    floatValue,+    floatValueType,+    PrimValue (..),+    primValueType,+    blankPrimValue,++    -- * Operations+    Overflow (..),+    Safety (..),+    UnOp (..),+    allUnOps,+    BinOp (..),+    allBinOps,+    ConvOp (..),+    allConvOps,+    CmpOp (..),+    allCmpOps,++    -- ** Unary Operations+    doUnOp,+    doComplement,+    doAbs,+    doFAbs,+    doSSignum,+    doUSignum,++    -- ** Binary Operations+    doBinOp,+    doAdd,+    doMul,+    doSDiv,+    doSMod,+    doPow,++    -- ** Conversion Operations+    doConvOp,+    doZExt,+    doSExt,+    doFPConv,+    doFPToUI,+    doFPToSI,+    doUIToFP,+    doSIToFP,+    intToInt64,+    intToWord64,++    -- * Comparison Operations+    doCmpOp,+    doCmpEq,+    doCmpUlt,+    doCmpUle,+    doCmpSlt,+    doCmpSle,+    doFCmpLt,+    doFCmpLe,++    -- * Type Of+    binOpType,+    unOpType,+    cmpOpType,+    convOpType,++    -- * Primitive functions+    primFuns,++    -- * Utility+    zeroIsh,+    zeroIshInt,+    oneIsh,+    oneIshInt,+    negativeIsh,+    primBitSize,+    primByteSize,+    intByteSize,+    floatByteSize,+    commutativeBinOp,++    -- * Prettyprinting+    convOpFun,+    prettySigned,+  )+where++import Control.Category+import qualified Data.Binary.Get as G+import qualified Data.Binary.Put as P+import Data.Bits+import Data.Fixed (mod') -- Weird location.+import Data.Int (Int16, Int32, Int64, Int8)+import qualified Data.Map as M+import qualified Data.Text as T+import Data.Word+import Futhark.Util+  ( ceilDouble,+    ceilFloat,+    floorDouble,+    floorFloat,+    lgamma,+    lgammaf,+    roundDouble,+    roundFloat,+    tgamma,+    tgammaf,+  )+import Futhark.Util.Pretty+import GHC.Generics (Generic)+import Language.SexpGrammar as Sexp+import Language.SexpGrammar.Generic+import Text.Read (readMaybe)+import Prelude hiding (id, (.))++-- | An integer type, ordered by size.  Note that signedness is not a+-- property of the type, but a property of the operations performed on+-- values of these types.+data IntType+  = Int8+  | Int16+  | Int32+  | Int64+  deriving (Eq, Ord, Show, Enum, Bounded, Generic)++instance SexpIso IntType where+  sexpIso =+    match $+      With (sym "i8" >>>) $+        With (sym "i16" >>>) $+          With (sym "i32" >>>) $+            With+              (sym "i64" >>>)+              End++instance Pretty IntType where+  ppr Int8 = text "i8"+  ppr Int16 = text "i16"+  ppr Int32 = text "i32"+  ppr Int64 = text "i64"++-- | A list of all integer types.+allIntTypes :: [IntType]+allIntTypes = [minBound .. maxBound]++-- | A floating point type.+data FloatType+  = Float32+  | Float64+  deriving (Eq, Ord, Show, Enum, Bounded, Generic)++instance SexpIso FloatType where+  sexpIso =+    match $+      With (sym "f32" >>>) $+        With+          (sym "f64" >>>)+          End++instance Pretty FloatType where+  ppr Float32 = text "f32"+  ppr Float64 = text "f64"++-- | A list of all floating-point types.+allFloatTypes :: [FloatType]+allFloatTypes = [minBound .. maxBound]++-- | Low-level primitive types.+data PrimType+  = IntType IntType+  | FloatType FloatType+  | Bool+  | Cert+  deriving (Eq, Ord, Show, Generic)++instance SexpIso PrimType where+  sexpIso =+    match $+      With (. sexpIso) $+        With (. sexpIso) $+          With (sym "bool" >>>) $+            With+              (sym "cert" >>>)+              End++instance Enum PrimType where+  toEnum 0 = IntType Int8+  toEnum 1 = IntType Int16+  toEnum 2 = IntType Int32+  toEnum 3 = IntType Int64+  toEnum 4 = FloatType Float32+  toEnum 5 = FloatType Float64+  toEnum 6 = Bool+  toEnum _ = Cert++  fromEnum (IntType Int8) = 0+  fromEnum (IntType Int16) = 1+  fromEnum (IntType Int32) = 2+  fromEnum (IntType Int64) = 3+  fromEnum (FloatType Float32) = 4+  fromEnum (FloatType Float64) = 5+  fromEnum Bool = 6+  fromEnum Cert = 7++instance Bounded PrimType where+  minBound = IntType Int8+  maxBound = Cert++instance Pretty PrimType where+  ppr (IntType t) = ppr t+  ppr (FloatType t) = ppr t+  ppr Bool = text "bool"+  ppr Cert = text "cert"++-- | A list of all primitive types.+allPrimTypes :: [PrimType]+allPrimTypes =+  map IntType allIntTypes+    ++ map FloatType allFloatTypes+    ++ [Bool, Cert]++numberIso :: (Read a, Pretty a) => T.Text -> Grammar p (T.Text :- t) (a :- t)+numberIso postfix = partialOsi (fromS postfix) (toS postfix)+  where+    toS t s = prettyText s <> t++    fromS t s+      | t `T.isSuffixOf` s,+        Just v <- readMaybe $ T.unpack $ T.dropEnd (T.length t) s =+        Right v+      | otherwise =+        Left $ expected $ "Couldn't parse " <> t++-- | An integer value.+data IntValue+  = Int8Value !Int8+  | Int16Value !Int16+  | Int32Value !Int32+  | Int64Value !Int64+  deriving (Eq, Ord, Show, Generic)++instance SexpIso IntValue where+  sexpIso =+    match $+      With (. numberIso "i8" . Sexp.symbol) $+        With (. numberIso "i16" . Sexp.symbol) $+          With (. numberIso "i32" . Sexp.symbol) $+            With+              (. numberIso "i64" . Sexp.symbol)+              End++instance Pretty IntValue where+  ppr (Int8Value v) = text $ show v ++ "i8"+  ppr (Int16Value v) = text $ show v ++ "i16"+  ppr (Int32Value v) = text $ show v ++ "i32"+  ppr (Int64Value v) = text $ show v ++ "i64"++-- | Create an t'IntValue' from a type and an 'Integer'.+intValue :: Integral int => IntType -> int -> IntValue+intValue Int8 = Int8Value . fromIntegral+intValue Int16 = Int16Value . fromIntegral+intValue Int32 = Int32Value . fromIntegral+intValue Int64 = Int64Value . fromIntegral++-- | The type of an integer value.+intValueType :: IntValue -> IntType+intValueType Int8Value {} = Int8+intValueType Int16Value {} = Int16+intValueType Int32Value {} = Int32+intValueType Int64Value {} = Int64++-- | Convert an t'IntValue' to any 'Integral' type.+valueIntegral :: Integral int => IntValue -> int+valueIntegral (Int8Value v) = fromIntegral v+valueIntegral (Int16Value v) = fromIntegral v+valueIntegral (Int32Value v) = fromIntegral v+valueIntegral (Int64Value v) = fromIntegral v++-- | A floating-point value.+data FloatValue+  = Float32Value !Float+  | Float64Value !Double+  deriving (Show, Generic)++instance Eq FloatValue where+  Float32Value x == Float32Value y = isNaN x && isNaN y || x == y+  Float64Value x == Float64Value y = isNaN x && isNaN y || x == y+  Float32Value _ == Float64Value _ = False+  Float64Value _ == Float32Value _ = False++-- The derived Ord instance does not handle NaNs correctly.+instance Ord FloatValue where+  Float32Value x <= Float32Value y = x <= y+  Float64Value x <= Float64Value y = x <= y+  Float32Value _ <= Float64Value _ = True+  Float64Value _ <= Float32Value _ = False++  Float32Value x < Float32Value y = x < y+  Float64Value x < Float64Value y = x < y+  Float32Value _ < Float64Value _ = True+  Float64Value _ < Float32Value _ = False++  (>) = flip (<)+  (>=) = flip (<=)++instance SexpIso FloatValue where+  sexpIso =+    match $+      With (. numberIso "f32" . Sexp.symbol) $+        With+          (. numberIso "f64" . Sexp.symbol)+          End++instance Pretty FloatValue where+  ppr (Float32Value v)+    | isInfinite v, v >= 0 = text "f32.inf"+    | isInfinite v, v < 0 = text "-f32.inf"+    | isNaN v = text "f32.nan"+    | otherwise = text $ show v ++ "f32"+  ppr (Float64Value v)+    | isInfinite v, v >= 0 = text "f64.inf"+    | isInfinite v, v < 0 = text "-f64.inf"+    | isNaN v = text "f64.nan"+    | otherwise = text $ show v ++ "f64"++-- | Create a t'FloatValue' from a type and a 'Rational'.+floatValue :: Real num => FloatType -> num -> FloatValue+floatValue Float32 = Float32Value . fromRational . toRational+floatValue Float64 = Float64Value . fromRational . toRational++-- | The type of a floating-point value.+floatValueType :: FloatValue -> FloatType+floatValueType Float32Value {} = Float32+floatValueType Float64Value {} = Float64++-- | Non-array values.+data PrimValue+  = IntValue !IntValue+  | FloatValue !FloatValue+  | BoolValue !Bool+  | -- | The only value of type @cert@.+    Checked+  deriving (Eq, Ord, Show, Generic)++instance SexpIso PrimValue where+  sexpIso =+    match $+      With (. sexpIso) $+        With (. sexpIso) $+          With (. sexpIso) $+            With+              (sym "checked" >>>)+              End++instance Pretty PrimValue where+  ppr (IntValue v) = ppr v+  ppr (BoolValue True) = text "true"+  ppr (BoolValue False) = text "false"+  ppr (FloatValue v) = ppr v+  ppr Checked = text "checked"++-- | The type of a basic value.+primValueType :: PrimValue -> PrimType+primValueType (IntValue v) = IntType $ intValueType v+primValueType (FloatValue v) = FloatType $ floatValueType v+primValueType BoolValue {} = Bool+primValueType Checked = Cert++-- | A "blank" value of the given primitive type - this is zero, or+-- whatever is close to it.  Don't depend on this value, but use it+-- for e.g. creating arrays to be populated by do-loops.+blankPrimValue :: PrimType -> PrimValue+blankPrimValue (IntType Int8) = IntValue $ Int8Value 0+blankPrimValue (IntType Int16) = IntValue $ Int16Value 0+blankPrimValue (IntType Int32) = IntValue $ Int32Value 0+blankPrimValue (IntType Int64) = IntValue $ Int64Value 0+blankPrimValue (FloatType Float32) = FloatValue $ Float32Value 0.0+blankPrimValue (FloatType Float64) = FloatValue $ Float64Value 0.0+blankPrimValue Bool = BoolValue False+blankPrimValue Cert = Checked++-- | Various unary operators.  It is a bit ad-hoc what is a unary+-- operator and what is a built-in function.  Perhaps these should all+-- go away eventually.+data UnOp+  = -- | E.g., @! True == False@.+    Not+  | -- | E.g., @~(~1) = 1@.+    Complement IntType+  | -- | @abs(-2) = 2@.+    Abs IntType+  | -- | @fabs(-2.0) = 2.0@.+    FAbs FloatType+  | -- | Signed sign function: @ssignum(-2)@ = -1.+    SSignum IntType+  | -- | Unsigned sign function: @usignum(2)@ = 1.+    USignum IntType+  deriving (Eq, Ord, Show, Generic)++instance SexpIso UnOp where+  sexpIso =+    match $+      With (. Sexp.sym "not") $+        With (. Sexp.list (Sexp.el (Sexp.sym "complement") >>> Sexp.el sexpIso)) $+          With (. Sexp.list (Sexp.el (Sexp.sym "abs") >>> Sexp.el sexpIso)) $+            With (. Sexp.list (Sexp.el (Sexp.sym "fabs") >>> Sexp.el sexpIso)) $+              With (. Sexp.list (Sexp.el (Sexp.sym "ssignum") >>> Sexp.el sexpIso)) $+                With+                  (. Sexp.list (Sexp.el (Sexp.sym "usignum") >>> Sexp.el sexpIso))+                  End++-- | What to do in case of arithmetic overflow.  Futhark's semantics+-- are that overflow does wraparound, but for generated code (like+-- address arithmetic), it can be beneficial for overflow to be+-- undefined behaviour, as it allows better optimisation of things+-- such as GPU kernels.+--+-- Note that all values of this type are considered equal for 'Eq' and+-- 'Ord'.+data Overflow = OverflowWrap | OverflowUndef+  deriving (Show, Generic)++instance SexpIso Overflow where+  sexpIso =+    match $+      With (. Sexp.sym "wrap") $+        With+          (. Sexp.sym "undef")+          End++instance Eq Overflow where+  _ == _ = True++instance Ord Overflow where+  _ `compare` _ = EQ++-- | Whether something is safe or unsafe (mostly function calls, and+-- in the context of whether operations are dynamically checked).+-- When we inline an 'Unsafe' function, we remove all safety checks in+-- its body.  The 'Ord' instance picks 'Unsafe' as being less than+-- 'Safe'.+--+-- For operations like integer division, a safe division will not+-- explode the computer in case of division by zero, but instead+-- return some unspecified value.  This always involves a run-time+-- check, so generally the unsafe variant is what the compiler will+-- insert, but guarded by an explicit assertion elsewhere.  Safe+-- operations are useful when the optimiser wants to move e.g. a+-- division to a location where the divisor may be zero, but where the+-- result will only be used when it is non-zero (so it doesn't matter+-- what result is provided with a zero divisor, as long as the program+-- keeps running).+data Safety = Unsafe | Safe deriving (Eq, Ord, Show, Generic)++instance SexpIso Safety where+  sexpIso =+    match $+      With (. Sexp.sym "unsafe") $+        With+          (. Sexp.sym "safe")+          End++-- | Binary operators.  These correspond closely to the binary operators in+-- LLVM.  Most are parametrised by their expected input and output+-- types.+data BinOp+  = -- | Integer addition.+    Add IntType Overflow+  | -- | Floating-point addition.+    FAdd FloatType+  | -- | Integer subtraction.+    Sub IntType Overflow+  | -- | Floating-point subtraction.+    FSub FloatType+  | -- | Integer multiplication.+    Mul IntType Overflow+  | -- | Floating-point multiplication.+    FMul FloatType+  | -- | Unsigned integer division.  Rounds towards+    -- negativity infinity.  Note: this is different+    -- from LLVM.+    UDiv IntType Safety+  | -- | Unsigned integer division.  Rounds towards positive+    -- infinity.+    UDivUp IntType Safety+  | -- | Signed integer division.  Rounds towards+    -- negativity infinity.  Note: this is different+    -- from LLVM.+    SDiv IntType Safety+  | -- | Signed integer division.  Rounds towards positive+    -- infinity.+    SDivUp IntType Safety+  | -- | Floating-point division.+    FDiv FloatType+  | -- | Floating-point modulus.+    FMod FloatType+  | -- | Unsigned integer modulus; the countepart to 'UDiv'.+    UMod IntType Safety+  | -- | Signed integer modulus; the countepart to 'SDiv'.+    SMod IntType Safety+  | -- | Signed integer division.  Rounds towards zero.  This+    -- corresponds to the @sdiv@ instruction in LLVM and+    -- integer division in C.+    SQuot IntType Safety+  | -- | Signed integer division.  Rounds towards zero.  This+    -- corresponds to the @srem@ instruction in LLVM and+    -- integer modulo in C.+    SRem IntType Safety+  | -- | Returns the smallest of two signed integers.+    SMin IntType+  | -- | Returns the smallest of two unsigned integers.+    UMin IntType+  | -- | Returns the smallest of two floating-point numbers.+    FMin FloatType+  | -- | Returns the greatest of two signed integers.+    SMax IntType+  | -- | Returns the greatest of two unsigned integers.+    UMax IntType+  | -- | Returns the greatest of two floating-point numbers.+    FMax FloatType+  | -- | Left-shift.+    Shl IntType+  | -- | Logical right-shift, zero-extended.+    LShr IntType+  | -- | Arithmetic right-shift, sign-extended.+    AShr IntType+  | -- | Bitwise and.+    And IntType+  | -- | Bitwise or.+    Or IntType+  | -- | Bitwise exclusive-or.+    Xor IntType+  | -- | Integer exponentiation.+    Pow IntType+  | -- | Floating-point exponentiation.+    FPow FloatType+  | -- | Boolean and - not short-circuiting.+    LogAnd+  | -- | Boolean or - not short-circuiting.+    LogOr+  deriving (Eq, Ord, Show, Generic)++-- | Comparison operators are like 'BinOp's, but they always return a+-- boolean value.  The somewhat ugly constructor names are straight+-- out of LLVM.+data CmpOp+  = -- | All types equality.+    CmpEq PrimType+  | -- | Unsigned less than.+    CmpUlt IntType+  | -- | Unsigned less than or equal.+    CmpUle IntType+  | -- | Signed less than.+    CmpSlt IntType+  | -- | Signed less than or equal.+    CmpSle IntType+  | -- Comparison operators for floating-point values.  TODO: extend+    -- this to handle NaNs and such, like the LLVM fcmp instruction.++    -- | Floating-point less than.+    FCmpLt FloatType+  | -- | Floating-point less than or equal.+    FCmpLe FloatType+  | -- Boolean comparison.++    -- | Boolean less than.+    CmpLlt+  | -- | Boolean less than or equal.+    CmpLle+  deriving (Eq, Ord, Show, Generic)++-- | Conversion operators try to generalise the @from t0 x to t1@+-- instructions from LLVM.+data ConvOp+  = -- | Zero-extend the former integer type to the latter.+    -- If the new type is smaller, the result is a+    -- truncation.+    ZExt IntType IntType+  | -- | Sign-extend the former integer type to the latter.+    -- If the new type is smaller, the result is a+    -- truncation.+    SExt IntType IntType+  | -- | Convert value of the former floating-point type to+    -- the latter.  If the new type is smaller, the result+    -- is a truncation.+    FPConv FloatType FloatType+  | -- | Convert a floating-point value to the nearest+    -- unsigned integer (rounding towards zero).+    FPToUI FloatType IntType+  | -- | Convert a floating-point value to the nearest+    -- signed integer (rounding towards zero).+    FPToSI FloatType IntType+  | -- | Convert an unsigned integer to a floating-point value.+    UIToFP IntType FloatType+  | -- | Convert a signed integer to a floating-point value.+    SIToFP IntType FloatType+  | -- | Convert an integer to a boolean value.  Zero+    -- becomes false; anything else is true.+    IToB IntType+  | -- | Convert a boolean to an integer.  True is converted+    -- to 1 and False to 0.+    BToI IntType+  deriving (Eq, Ord, Show, Generic)++-- | A list of all unary operators for all types.+allUnOps :: [UnOp]+allUnOps =+  Not :+  map Complement [minBound .. maxBound]+    ++ map Abs [minBound .. maxBound]+    ++ map FAbs [minBound .. maxBound]+    ++ map SSignum [minBound .. maxBound]+    ++ map USignum [minBound .. maxBound]++-- | A list of all binary operators for all types.+allBinOps :: [BinOp]+allBinOps =+  concat+    [ map (`Add` OverflowWrap) allIntTypes,+      map FAdd allFloatTypes,+      map (`Sub` OverflowWrap) allIntTypes,+      map FSub allFloatTypes,+      map (`Mul` OverflowWrap) allIntTypes,+      map FMul allFloatTypes,+      map (`UDiv` Unsafe) allIntTypes,+      map (`UDivUp` Unsafe) allIntTypes,+      map (`SDiv` Unsafe) allIntTypes,+      map (`SDivUp` Unsafe) allIntTypes,+      map FDiv allFloatTypes,+      map FMod allFloatTypes,+      map (`UMod` Unsafe) allIntTypes,+      map (`SMod` Unsafe) allIntTypes,+      map (`SQuot` Unsafe) allIntTypes,+      map (`SRem` Unsafe) allIntTypes,+      map SMin allIntTypes,+      map UMin allIntTypes,+      map FMin allFloatTypes,+      map SMax allIntTypes,+      map UMax allIntTypes,+      map FMax allFloatTypes,+      map Shl allIntTypes,+      map LShr allIntTypes,+      map AShr allIntTypes,+      map And allIntTypes,+      map Or allIntTypes,+      map Xor allIntTypes,+      map Pow allIntTypes,+      map FPow allFloatTypes,+      [LogAnd, LogOr]+    ]++-- | A list of all comparison operators for all types.+allCmpOps :: [CmpOp]+allCmpOps =+  concat+    [ map CmpEq allPrimTypes,+      map CmpUlt allIntTypes,+      map CmpUle allIntTypes,+      map CmpSlt allIntTypes,+      map CmpSle allIntTypes,+      map FCmpLt allFloatTypes,+      map FCmpLe allFloatTypes+    ]++-- | A list of all conversion operators for all types.+allConvOps :: [ConvOp]+allConvOps =+  concat+    [ ZExt <$> allIntTypes <*> allIntTypes,+      SExt <$> allIntTypes <*> allIntTypes,+      FPConv <$> allFloatTypes <*> allFloatTypes,+      FPToUI <$> allFloatTypes <*> allIntTypes,+      FPToSI <$> allFloatTypes <*> allIntTypes,+      UIToFP <$> allIntTypes <*> allFloatTypes,+      SIToFP <$> allIntTypes <*> allFloatTypes,+      IToB <$> allIntTypes,+      BToI <$> allIntTypes+    ]++-- | Apply an 'UnOp' to an operand.  Returns 'Nothing' if the+-- application is mistyped.+doUnOp :: UnOp -> PrimValue -> Maybe PrimValue+doUnOp Not (BoolValue b) = Just $ BoolValue $ not b+doUnOp Complement {} (IntValue v) = Just $ IntValue $ doComplement v+doUnOp Abs {} (IntValue v) = Just $ IntValue $ doAbs v+doUnOp FAbs {} (FloatValue v) = Just $ FloatValue $ doFAbs v+doUnOp SSignum {} (IntValue v) = Just $ IntValue $ doSSignum v+doUnOp USignum {} (IntValue v) = Just $ IntValue $ doUSignum v+doUnOp _ _ = Nothing++-- | E.g., @~(~1) = 1@.+doComplement :: IntValue -> IntValue+doComplement v = intValue (intValueType v) $ complement $ intToInt64 v++-- | @abs(-2) = 2@.+doAbs :: IntValue -> IntValue+doAbs v = intValue (intValueType v) $ abs $ intToInt64 v++-- | @abs(-2.0) = 2.0@.+doFAbs :: FloatValue -> FloatValue+doFAbs v = floatValue (floatValueType v) $ abs $ floatToDouble v++-- | @ssignum(-2)@ = -1.+doSSignum :: IntValue -> IntValue+doSSignum v = intValue (intValueType v) $ signum $ intToInt64 v++-- | @usignum(-2)@ = -1.+doUSignum :: IntValue -> IntValue+doUSignum v = intValue (intValueType v) $ signum $ intToWord64 v++-- | Apply a 'BinOp' to an operand.  Returns 'Nothing' if the+-- application is mistyped, or outside the domain (e.g. division by+-- zero).+doBinOp :: BinOp -> PrimValue -> PrimValue -> Maybe PrimValue+doBinOp Add {} = doIntBinOp doAdd+doBinOp FAdd {} = doFloatBinOp (+) (+)+doBinOp Sub {} = doIntBinOp doSub+doBinOp FSub {} = doFloatBinOp (-) (-)+doBinOp Mul {} = doIntBinOp doMul+doBinOp FMul {} = doFloatBinOp (*) (*)+doBinOp UDiv {} = doRiskyIntBinOp doUDiv+doBinOp UDivUp {} = doRiskyIntBinOp doUDivUp+doBinOp SDiv {} = doRiskyIntBinOp doSDiv+doBinOp SDivUp {} = doRiskyIntBinOp doSDivUp+doBinOp FDiv {} = doFloatBinOp (/) (/)+doBinOp FMod {} = doFloatBinOp mod' mod'+doBinOp UMod {} = doRiskyIntBinOp doUMod+doBinOp SMod {} = doRiskyIntBinOp doSMod+doBinOp SQuot {} = doRiskyIntBinOp doSQuot+doBinOp SRem {} = doRiskyIntBinOp doSRem+doBinOp SMin {} = doIntBinOp doSMin+doBinOp UMin {} = doIntBinOp doUMin+doBinOp FMin {} = doFloatBinOp min min+doBinOp SMax {} = doIntBinOp doSMax+doBinOp UMax {} = doIntBinOp doUMax+doBinOp FMax {} = doFloatBinOp max max+doBinOp Shl {} = doIntBinOp doShl+doBinOp LShr {} = doIntBinOp doLShr+doBinOp AShr {} = doIntBinOp doAShr+doBinOp And {} = doIntBinOp doAnd+doBinOp Or {} = doIntBinOp doOr+doBinOp Xor {} = doIntBinOp doXor+doBinOp Pow {} = doRiskyIntBinOp doPow+doBinOp FPow {} = doFloatBinOp (**) (**)+doBinOp LogAnd {} = doBoolBinOp (&&)+doBinOp LogOr {} = doBoolBinOp (||)++doIntBinOp ::+  (IntValue -> IntValue -> IntValue) ->+  PrimValue ->+  PrimValue ->+  Maybe PrimValue+doIntBinOp f (IntValue v1) (IntValue v2) =+  Just $ IntValue $ f v1 v2+doIntBinOp _ _ _ = Nothing++doRiskyIntBinOp ::+  (IntValue -> IntValue -> Maybe IntValue) ->+  PrimValue ->+  PrimValue ->+  Maybe PrimValue+doRiskyIntBinOp f (IntValue v1) (IntValue v2) =+  IntValue <$> f v1 v2+doRiskyIntBinOp _ _ _ = Nothing++doFloatBinOp ::+  (Float -> Float -> Float) ->+  (Double -> Double -> Double) ->+  PrimValue ->+  PrimValue ->+  Maybe PrimValue+doFloatBinOp f32 _ (FloatValue (Float32Value v1)) (FloatValue (Float32Value v2)) =+  Just $ FloatValue $ Float32Value $ f32 v1 v2+doFloatBinOp _ f64 (FloatValue (Float64Value v1)) (FloatValue (Float64Value v2)) =+  Just $ FloatValue $ Float64Value $ f64 v1 v2+doFloatBinOp _ _ _ _ = Nothing++doBoolBinOp ::+  (Bool -> Bool -> Bool) ->+  PrimValue ->+  PrimValue ->+  Maybe PrimValue+doBoolBinOp f (BoolValue v1) (BoolValue v2) =+  Just $ BoolValue $ f v1 v2+doBoolBinOp _ _ _ = Nothing++-- | Integer addition.+doAdd :: IntValue -> IntValue -> IntValue+doAdd v1 v2 = intValue (intValueType v1) $ intToInt64 v1 + intToInt64 v2++-- | Integer subtraction.+doSub :: IntValue -> IntValue -> IntValue+doSub v1 v2 = intValue (intValueType v1) $ intToInt64 v1 - intToInt64 v2++-- | Integer multiplication.+doMul :: IntValue -> IntValue -> IntValue+doMul v1 v2 = intValue (intValueType v1) $ intToInt64 v1 * intToInt64 v2++-- | Unsigned integer division.  Rounds towards negativity infinity.+-- Note: this is different from LLVM.+doUDiv :: IntValue -> IntValue -> Maybe IntValue+doUDiv v1 v2+  | zeroIshInt v2 = Nothing+  | otherwise =+    Just $+      intValue (intValueType v1) $+        intToWord64 v1 `div` intToWord64 v2++-- | Unsigned integer division.  Rounds towards positive infinity.+doUDivUp :: IntValue -> IntValue -> Maybe IntValue+doUDivUp v1 v2+  | zeroIshInt v2 = Nothing+  | otherwise =+    Just $+      intValue (intValueType v1) $+        (intToWord64 v1 + intToWord64 v2 - 1) `div` intToWord64 v2++-- | Signed integer division.  Rounds towards negativity infinity.+-- Note: this is different from LLVM.+doSDiv :: IntValue -> IntValue -> Maybe IntValue+doSDiv v1 v2+  | zeroIshInt v2 = Nothing+  | otherwise =+    Just $+      intValue (intValueType v1) $+        intToInt64 v1 `div` intToInt64 v2++-- | Signed integer division.  Rounds towards positive infinity.+doSDivUp :: IntValue -> IntValue -> Maybe IntValue+doSDivUp v1 v2+  | zeroIshInt v2 = Nothing+  | otherwise =+    Just $+      intValue (intValueType v1) $+        (intToInt64 v1 + intToInt64 v2 - 1) `div` intToInt64 v2++-- | Unsigned integer modulus; the countepart to 'UDiv'.+doUMod :: IntValue -> IntValue -> Maybe IntValue+doUMod v1 v2+  | zeroIshInt v2 = Nothing+  | otherwise = Just $ intValue (intValueType v1) $ intToWord64 v1 `mod` intToWord64 v2++-- | Signed integer modulus; the countepart to 'SDiv'.+doSMod :: IntValue -> IntValue -> Maybe IntValue+doSMod v1 v2+  | zeroIshInt v2 = Nothing+  | otherwise = Just $ intValue (intValueType v1) $ intToInt64 v1 `mod` intToInt64 v2++-- | Signed integer division.  Rounds towards zero.+-- This corresponds to the @sdiv@ instruction in LLVM.+doSQuot :: IntValue -> IntValue -> Maybe IntValue+doSQuot v1 v2+  | zeroIshInt v2 = Nothing+  | otherwise = Just $ intValue (intValueType v1) $ intToInt64 v1 `quot` intToInt64 v2++-- | Signed integer division.  Rounds towards zero.+-- This corresponds to the @srem@ instruction in LLVM.+doSRem :: IntValue -> IntValue -> Maybe IntValue+doSRem v1 v2+  | zeroIshInt v2 = Nothing+  | otherwise = Just $ intValue (intValueType v1) $ intToInt64 v1 `rem` intToInt64 v2++-- | Minimum of two signed integers.+doSMin :: IntValue -> IntValue -> IntValue+doSMin v1 v2 = intValue (intValueType v1) $ intToInt64 v1 `min` intToInt64 v2++-- | Minimum of two unsigned integers.+doUMin :: IntValue -> IntValue -> IntValue+doUMin v1 v2 = intValue (intValueType v1) $ intToWord64 v1 `min` intToWord64 v2++-- | Maximum of two signed integers.+doSMax :: IntValue -> IntValue -> IntValue+doSMax v1 v2 = intValue (intValueType v1) $ intToInt64 v1 `max` intToInt64 v2++-- | Maximum of two unsigned integers.+doUMax :: IntValue -> IntValue -> IntValue+doUMax v1 v2 = intValue (intValueType v1) $ intToWord64 v1 `max` intToWord64 v2++-- | Left-shift.+doShl :: IntValue -> IntValue -> IntValue+doShl v1 v2 = intValue (intValueType v1) $ intToInt64 v1 `shift` intToInt v2++-- | Logical right-shift, zero-extended.+doLShr :: IntValue -> IntValue -> IntValue+doLShr v1 v2 = intValue (intValueType v1) $ intToWord64 v1 `shift` negate (intToInt v2)++-- | Arithmetic right-shift, sign-extended.+doAShr :: IntValue -> IntValue -> IntValue+doAShr v1 v2 = intValue (intValueType v1) $ intToInt64 v1 `shift` negate (intToInt v2)++-- | Bitwise and.+doAnd :: IntValue -> IntValue -> IntValue+doAnd v1 v2 = intValue (intValueType v1) $ intToWord64 v1 .&. intToWord64 v2++-- | Bitwise or.+doOr :: IntValue -> IntValue -> IntValue+doOr v1 v2 = intValue (intValueType v1) $ intToWord64 v1 .|. intToWord64 v2++-- | Bitwise exclusive-or.+doXor :: IntValue -> IntValue -> IntValue+doXor v1 v2 = intValue (intValueType v1) $ intToWord64 v1 `xor` intToWord64 v2++-- | Signed integer exponentatation.+doPow :: IntValue -> IntValue -> Maybe IntValue+doPow v1 v2+  | negativeIshInt v2 = Nothing+  | otherwise = Just $ intValue (intValueType v1) $ intToInt64 v1 ^ intToInt64 v2++-- | Apply a 'ConvOp' to an operand.  Returns 'Nothing' if the+-- application is mistyped.+doConvOp :: ConvOp -> PrimValue -> Maybe PrimValue+doConvOp (ZExt _ to) (IntValue v) = Just $ IntValue $ doZExt v to+doConvOp (SExt _ to) (IntValue v) = Just $ IntValue $ doSExt v to+doConvOp (FPConv _ to) (FloatValue v) = Just $ FloatValue $ doFPConv v to+doConvOp (FPToUI _ to) (FloatValue v) = Just $ IntValue $ doFPToUI v to+doConvOp (FPToSI _ to) (FloatValue v) = Just $ IntValue $ doFPToSI v to+doConvOp (UIToFP _ to) (IntValue v) = Just $ FloatValue $ doUIToFP v to+doConvOp (SIToFP _ to) (IntValue v) = Just $ FloatValue $ doSIToFP v to+doConvOp (IToB _) (IntValue v) = Just $ BoolValue $ intToInt64 v /= 0+doConvOp (BToI to) (BoolValue v) = Just $ IntValue $ intValue to $ if v then 1 else 0 :: Int+doConvOp _ _ = Nothing++-- | Zero-extend the given integer value to the size of the given+-- type.  If the type is smaller than the given value, the result is a+-- truncation.+doZExt :: IntValue -> IntType -> IntValue+doZExt (Int8Value x) t = intValue t $ toInteger (fromIntegral x :: Word8)+doZExt (Int16Value x) t = intValue t $ toInteger (fromIntegral x :: Word16)+doZExt (Int32Value x) t = intValue t $ toInteger (fromIntegral x :: Word32)+doZExt (Int64Value x) t = intValue t $ toInteger (fromIntegral x :: Word64)++-- | Sign-extend the given integer value to the size of the given+-- type.  If the type is smaller than the given value, the result is a+-- truncation.+doSExt :: IntValue -> IntType -> IntValue+doSExt (Int8Value x) t = intValue t $ toInteger x+doSExt (Int16Value x) t = intValue t $ toInteger x+doSExt (Int32Value x) t = intValue t $ toInteger x+doSExt (Int64Value x) t = intValue t $ toInteger x++-- | Convert the former floating-point type to the latter.+doFPConv :: FloatValue -> FloatType -> FloatValue+doFPConv (Float32Value v) Float32 = Float32Value v+doFPConv (Float64Value v) Float32 = Float32Value $ fromRational $ toRational v+doFPConv (Float64Value v) Float64 = Float64Value v+doFPConv (Float32Value v) Float64 = Float64Value $ fromRational $ toRational v++-- | Convert a floating-point value to the nearest+-- unsigned integer (rounding towards zero).+doFPToUI :: FloatValue -> IntType -> IntValue+doFPToUI v t = intValue t (truncate $ floatToDouble v :: Word64)++-- | Convert a floating-point value to the nearest+-- signed integer (rounding towards zero).+doFPToSI :: FloatValue -> IntType -> IntValue+doFPToSI v t = intValue t (truncate $ floatToDouble v :: Word64)++-- | Convert an unsigned integer to a floating-point value.+doUIToFP :: IntValue -> FloatType -> FloatValue+doUIToFP v t = floatValue t $ intToWord64 v++-- | Convert a signed integer to a floating-point value.+doSIToFP :: IntValue -> FloatType -> FloatValue+doSIToFP v t = floatValue t $ intToInt64 v++-- | Apply a 'CmpOp' to an operand.  Returns 'Nothing' if the+-- application is mistyped.+doCmpOp :: CmpOp -> PrimValue -> PrimValue -> Maybe Bool+doCmpOp CmpEq {} v1 v2 = Just $ doCmpEq v1 v2+doCmpOp CmpUlt {} (IntValue v1) (IntValue v2) = Just $ doCmpUlt v1 v2+doCmpOp CmpUle {} (IntValue v1) (IntValue v2) = Just $ doCmpUle v1 v2+doCmpOp CmpSlt {} (IntValue v1) (IntValue v2) = Just $ doCmpSlt v1 v2+doCmpOp CmpSle {} (IntValue v1) (IntValue v2) = Just $ doCmpSle v1 v2+doCmpOp FCmpLt {} (FloatValue v1) (FloatValue v2) = Just $ doFCmpLt v1 v2+doCmpOp FCmpLe {} (FloatValue v1) (FloatValue v2) = Just $ doFCmpLe v1 v2+doCmpOp CmpLlt {} (BoolValue v1) (BoolValue v2) = Just $ not v1 && v2+doCmpOp CmpLle {} (BoolValue v1) (BoolValue v2) = Just $ not (v1 && not v2)+doCmpOp _ _ _ = Nothing++-- | Compare any two primtive values for exact equality.+doCmpEq :: PrimValue -> PrimValue -> Bool+doCmpEq (FloatValue (Float32Value v1)) (FloatValue (Float32Value v2)) = v1 == v2+doCmpEq (FloatValue (Float64Value v1)) (FloatValue (Float64Value v2)) = v1 == v2+doCmpEq v1 v2 = v1 == v2++-- | Unsigned less than.+doCmpUlt :: IntValue -> IntValue -> Bool+doCmpUlt v1 v2 = intToWord64 v1 < intToWord64 v2++-- | Unsigned less than or equal.+doCmpUle :: IntValue -> IntValue -> Bool+doCmpUle v1 v2 = intToWord64 v1 <= intToWord64 v2++-- | Signed less than.+doCmpSlt :: IntValue -> IntValue -> Bool+doCmpSlt = (<)++-- | Signed less than or equal.+doCmpSle :: IntValue -> IntValue -> Bool+doCmpSle = (<=)++-- | Floating-point less than.+doFCmpLt :: FloatValue -> FloatValue -> Bool+doFCmpLt = (<)++-- | Floating-point less than or equal.+doFCmpLe :: FloatValue -> FloatValue -> Bool+doFCmpLe = (<=)++-- | Translate an t'IntValue' to 'Word64'.  This is guaranteed to fit.+intToWord64 :: IntValue -> Word64+intToWord64 (Int8Value v) = fromIntegral (fromIntegral v :: Word8)+intToWord64 (Int16Value v) = fromIntegral (fromIntegral v :: Word16)+intToWord64 (Int32Value v) = fromIntegral (fromIntegral v :: Word32)+intToWord64 (Int64Value v) = fromIntegral (fromIntegral v :: Word64)++-- | Translate an t'IntValue' to t'Int64'.  This is guaranteed to fit.+intToInt64 :: IntValue -> Int64+intToInt64 (Int8Value v) = fromIntegral v+intToInt64 (Int16Value v) = fromIntegral v+intToInt64 (Int32Value v) = fromIntegral v+intToInt64 (Int64Value v) = fromIntegral v++-- | Careful - there is no guarantee this will fit.+intToInt :: IntValue -> Int+intToInt = fromIntegral . intToInt64++floatToDouble :: FloatValue -> Double+floatToDouble (Float32Value v) = fromRational $ toRational v+floatToDouble (Float64Value v) = v++-- | The result type of a binary operator.+binOpType :: BinOp -> PrimType+binOpType (Add t _) = IntType t+binOpType (Sub t _) = IntType t+binOpType (Mul t _) = IntType t+binOpType (SDiv t _) = IntType t+binOpType (SDivUp t _) = IntType t+binOpType (SMod t _) = IntType t+binOpType (SQuot t _) = IntType t+binOpType (SRem t _) = IntType t+binOpType (UDiv t _) = IntType t+binOpType (UDivUp t _) = IntType t+binOpType (UMod t _) = IntType t+binOpType (SMin t) = IntType t+binOpType (UMin t) = IntType t+binOpType (FMin t) = FloatType t+binOpType (SMax t) = IntType t+binOpType (UMax t) = IntType t+binOpType (FMax t) = FloatType t+binOpType (Shl t) = IntType t+binOpType (LShr t) = IntType t+binOpType (AShr t) = IntType t+binOpType (And t) = IntType t+binOpType (Or t) = IntType t+binOpType (Xor t) = IntType t+binOpType (Pow t) = IntType t+binOpType (FPow t) = FloatType t+binOpType LogAnd = Bool+binOpType LogOr = Bool+binOpType (FAdd t) = FloatType t+binOpType (FSub t) = FloatType t+binOpType (FMul t) = FloatType t+binOpType (FDiv t) = FloatType t+binOpType (FMod t) = FloatType t++-- | The operand types of a comparison operator.+cmpOpType :: CmpOp -> PrimType+cmpOpType (CmpEq t) = t+cmpOpType (CmpSlt t) = IntType t+cmpOpType (CmpSle t) = IntType t+cmpOpType (CmpUlt t) = IntType t+cmpOpType (CmpUle t) = IntType t+cmpOpType (FCmpLt t) = FloatType t+cmpOpType (FCmpLe t) = FloatType t+cmpOpType CmpLlt = Bool+cmpOpType CmpLle = Bool++-- | The operand and result type of a unary operator.+unOpType :: UnOp -> PrimType+unOpType (SSignum t) = IntType t+unOpType (USignum t) = IntType t+unOpType Not = Bool+unOpType (Complement t) = IntType t+unOpType (Abs t) = IntType t+unOpType (FAbs t) = FloatType t++-- | The input and output types of a conversion operator.+convOpType :: ConvOp -> (PrimType, PrimType)+convOpType (ZExt from to) = (IntType from, IntType to)+convOpType (SExt from to) = (IntType from, IntType to)+convOpType (FPConv from to) = (FloatType from, FloatType to)+convOpType (FPToUI from to) = (FloatType from, IntType to)+convOpType (FPToSI from to) = (FloatType from, IntType to)+convOpType (UIToFP from to) = (IntType from, FloatType to)+convOpType (SIToFP from to) = (IntType from, FloatType to)+convOpType (IToB from) = (IntType from, Bool)+convOpType (BToI to) = (Bool, IntType to)++floatToWord :: Float -> Word32+floatToWord = G.runGet G.getWord32le . P.runPut . P.putFloatle++wordToFloat :: Word32 -> Float+wordToFloat = G.runGet G.getFloatle . P.runPut . P.putWord32le++doubleToWord :: Double -> Word64+doubleToWord = G.runGet G.getWord64le . P.runPut . P.putDoublele++wordToDouble :: Word64 -> Double+wordToDouble = G.runGet G.getDoublele . P.runPut . P.putWord64le++-- | A mapping from names of primitive functions to their parameter+-- types, their result type, and a function for evaluating them.+primFuns ::+  M.Map+    String+    ( [PrimType],+      PrimType,+      [PrimValue] -> Maybe PrimValue+    )+primFuns =+  M.fromList+    [ f32 "sqrt32" sqrt,+      f64 "sqrt64" sqrt,+      f32 "log32" log,+      f64 "log64" log,+      f32 "log10_32" (logBase 10),+      f64 "log10_64" (logBase 10),+      f32 "log2_32" (logBase 2),+      f64 "log2_64" (logBase 2),+      f32 "exp32" exp,+      f64 "exp64" exp,+      f32 "sin32" sin,+      f64 "sin64" sin,+      f32 "sinh32" sinh,+      f64 "sinh64" sinh,+      f32 "cos32" cos,+      f64 "cos64" cos,+      f32 "cosh32" cosh,+      f64 "cosh64" cosh,+      f32 "tan32" tan,+      f64 "tan64" tan,+      f32 "tanh32" tanh,+      f64 "tanh64" tanh,+      f32 "asin32" asin,+      f64 "asin64" asin,+      f32 "asinh32" asinh,+      f64 "asinh64" asinh,+      f32 "acos32" acos,+      f64 "acos64" acos,+      f32 "acosh32" acosh,+      f64 "acosh64" acosh,+      f32 "atan32" atan,+      f64 "atan64" atan,+      f32 "atanh32" atanh,+      f64 "atanh64" atanh,+      f32 "round32" roundFloat,+      f64 "round64" roundDouble,+      f32 "ceil32" ceilFloat,+      f64 "ceil64" ceilDouble,+      f32 "floor32" floorFloat,+      f64 "floor64" floorDouble,+      f32 "gamma32" tgammaf,+      f64 "gamma64" tgamma,+      f32 "lgamma32" lgammaf,+      f64 "lgamma64" lgamma,+      i8 "clz8" $ IntValue . Int32Value . fromIntegral . countLeadingZeros,+      i16 "clz16" $ IntValue . Int32Value . fromIntegral . countLeadingZeros,+      i32 "clz32" $ IntValue . Int32Value . fromIntegral . countLeadingZeros,+      i64 "clz64" $ IntValue . Int32Value . fromIntegral . countLeadingZeros,+      i8 "ctz8" $ IntValue . Int32Value . fromIntegral . countTrailingZeros,+      i16 "ctz16" $ IntValue . Int32Value . fromIntegral . countTrailingZeros,+      i32 "ctz32" $ IntValue . Int32Value . fromIntegral . countTrailingZeros,+      i64 "ctz64" $ IntValue . Int32Value . fromIntegral . countTrailingZeros,+      i8 "popc8" $ IntValue . Int32Value . fromIntegral . popCount,+      i16 "popc16" $ IntValue . Int32Value . fromIntegral . popCount,+      i32 "popc32" $ IntValue . Int32Value . fromIntegral . popCount,+      i64 "popc64" $ IntValue . Int32Value . fromIntegral . popCount,+      ( "mad_hi8",+        ( [IntType Int8, IntType Int8, IntType Int8],+          IntType Int8,+          \case+            [IntValue (Int8Value a), IntValue (Int8Value b), IntValue (Int8Value c)] ->+              Just $ IntValue . Int8Value $ mad_hi8 (Int8Value a) (Int8Value b) c+            _ -> Nothing+        )+      ),+      ( "mad_hi16",+        ( [IntType Int16, IntType Int16, IntType Int16],+          IntType Int16,+          \case+            [IntValue (Int16Value a), IntValue (Int16Value b), IntValue (Int16Value c)] ->+              Just $ IntValue . Int16Value $ mad_hi16 (Int16Value a) (Int16Value b) c+            _ -> Nothing+        )+      ),+      ( "mad_hi32",+        ( [IntType Int32, IntType Int32, IntType Int32],+          IntType Int32,+          \case+            [IntValue (Int32Value a), IntValue (Int32Value b), IntValue (Int32Value c)] ->+              Just $ IntValue . Int32Value $ mad_hi32 (Int32Value a) (Int32Value b) c+            _ -> Nothing+        )+      ),+      ( "mad_hi64",+        ( [IntType Int64, IntType Int64, IntType Int64],+          IntType Int64,+          \case+            [IntValue (Int64Value a), IntValue (Int64Value b), IntValue (Int64Value c)] ->+              Just $ IntValue . Int64Value $ mad_hi64 (Int64Value a) (Int64Value b) c+            _ -> Nothing+        )+      ),+      ( "mul_hi8",+        ( [IntType Int8, IntType Int8],+          IntType Int8,+          \case+            [IntValue (Int8Value a), IntValue (Int8Value b)] ->+              Just $ IntValue . Int8Value $ mul_hi8 (Int8Value a) (Int8Value b)+            _ -> Nothing+        )+      ),+      ( "mul_hi16",+        ( [IntType Int16, IntType Int16],+          IntType Int16,+          \case+            [IntValue (Int16Value a), IntValue (Int16Value b)] ->+              Just $ IntValue . Int16Value $ mul_hi16 (Int16Value a) (Int16Value b)+            _ -> Nothing+        )+      ),+      ( "mul_hi32",+        ( [IntType Int32, IntType Int32],+          IntType Int32,+          \case+            [IntValue (Int32Value a), IntValue (Int32Value b)] ->+              Just $ IntValue . Int32Value $ mul_hi32 (Int32Value a) (Int32Value b)+            _ -> Nothing+        )+      ),+      ( "mul_hi64",+        ( [IntType Int64, IntType Int64],+          IntType Int64,+          \case+            [IntValue (Int64Value a), IntValue (Int64Value b)] ->+              Just $ IntValue . Int64Value $ mul_hi64 (Int64Value a) (Int64Value b)+            _ -> Nothing+        )+      ),+      ( "atan2_32",+        ( [FloatType Float32, FloatType Float32],+          FloatType Float32,+          \case+            [FloatValue (Float32Value x), FloatValue (Float32Value y)] ->+              Just $ FloatValue $ Float32Value $ atan2 x y+            _ -> Nothing+        )+      ),+      ( "atan2_64",+        ( [FloatType Float64, FloatType Float64],+          FloatType Float64,+          \case+            [FloatValue (Float64Value x), FloatValue (Float64Value y)] ->+              Just $ FloatValue $ Float64Value $ atan2 x y+            _ -> Nothing+        )+      ),+      ( "isinf32",+        ( [FloatType Float32],+          Bool,+          \case+            [FloatValue (Float32Value x)] -> Just $ BoolValue $ isInfinite x+            _ -> Nothing+        )+      ),+      ( "isinf64",+        ( [FloatType Float64],+          Bool,+          \case+            [FloatValue (Float64Value x)] -> Just $ BoolValue $ isInfinite x+            _ -> Nothing+        )+      ),+      ( "isnan32",+        ( [FloatType Float32],+          Bool,+          \case+            [FloatValue (Float32Value x)] -> Just $ BoolValue $ isNaN x+            _ -> Nothing+        )+      ),+      ( "isnan64",+        ( [FloatType Float64],+          Bool,+          \case+            [FloatValue (Float64Value x)] -> Just $ BoolValue $ isNaN x+            _ -> Nothing+        )+      ),+      ( "to_bits32",+        ( [FloatType Float32],+          IntType Int32,+          \case+            [FloatValue (Float32Value x)] ->+              Just $ IntValue $ Int32Value $ fromIntegral $ floatToWord x+            _ -> Nothing+        )+      ),+      ( "to_bits64",+        ( [FloatType Float64],+          IntType Int64,+          \case+            [FloatValue (Float64Value x)] ->+              Just $ IntValue $ Int64Value $ fromIntegral $ doubleToWord x+            _ -> Nothing+        )+      ),+      ( "from_bits32",+        ( [IntType Int32],+          FloatType Float32,+          \case+            [IntValue (Int32Value x)] ->+              Just $ FloatValue $ Float32Value $ wordToFloat $ fromIntegral x+            _ -> Nothing+        )+      ),+      ( "from_bits64",+        ( [IntType Int64],+          FloatType Float64,+          \case+            [IntValue (Int64Value x)] ->+              Just $ FloatValue $ Float64Value $ wordToDouble $ fromIntegral x+            _ -> Nothing+        )+      ),+      f32_3 "lerp32" (\v0 v1 t -> v0 + (v1 - v0) * max 0 (min 1 t)),+      f64_3 "lerp64" (\v0 v1 t -> v0 + (v1 - v0) * max 0 (min 1 t)),+      f32_3 "mad32" (\a b c -> a * b + c),+      f64_3 "mad64" (\a b c -> a * b + c),+      f32_3 "fma32" (\a b c -> a * b + c),+      f64_3 "fma64" (\a b c -> a * b + c)+    ]+  where+    i8 s f = (s, ([IntType Int8], IntType Int32, i8PrimFun f))+    i16 s f = (s, ([IntType Int16], IntType Int32, i16PrimFun f))+    i32 s f = (s, ([IntType Int32], IntType Int32, i32PrimFun f))+    i64 s f = (s, ([IntType Int64], IntType Int32, i64PrimFun f))+    f32 s f = (s, ([FloatType Float32], FloatType Float32, f32PrimFun f))+    f64 s f = (s, ([FloatType Float64], FloatType Float64, f64PrimFun f))+    f32_3 s f =+      ( s,+        ( [FloatType Float32, FloatType Float32, FloatType Float32],+          FloatType Float32,+          f32PrimFun3 f+        )+      )+    f64_3 s f =+      ( s,+        ( [FloatType Float64, FloatType Float64, FloatType Float64],+          FloatType Float64,+          f64PrimFun3 f+        )+      )++    i8PrimFun f [IntValue (Int8Value x)] =+      Just $ f x+    i8PrimFun _ _ = Nothing++    i16PrimFun f [IntValue (Int16Value x)] =+      Just $ f x+    i16PrimFun _ _ = Nothing++    i32PrimFun f [IntValue (Int32Value x)] =+      Just $ f x+    i32PrimFun _ _ = Nothing++    i64PrimFun f [IntValue (Int64Value x)] =+      Just $ f x+    i64PrimFun _ _ = Nothing++    f32PrimFun f [FloatValue (Float32Value x)] =+      Just $ FloatValue $ Float32Value $ f x+    f32PrimFun _ _ = Nothing++    f64PrimFun f [FloatValue (Float64Value x)] =+      Just $ FloatValue $ Float64Value $ f x+    f64PrimFun _ _ = Nothing++    f32PrimFun3+      f+      [ FloatValue (Float32Value a),+        FloatValue (Float32Value b),+        FloatValue (Float32Value c)+        ] =+        Just $ FloatValue $ Float32Value $ f a b c+    f32PrimFun3 _ _ = Nothing++    f64PrimFun3+      f+      [ FloatValue (Float64Value a),+        FloatValue (Float64Value b),+        FloatValue (Float64Value c)+        ] =+        Just $ FloatValue $ Float64Value $ f a b c+    f64PrimFun3 _ _ = Nothing++-- | Is the given value kind of zero?+zeroIsh :: PrimValue -> Bool+zeroIsh (IntValue k) = zeroIshInt k+zeroIsh (FloatValue (Float32Value k)) = k == 0+zeroIsh (FloatValue (Float64Value k)) = k == 0+zeroIsh (BoolValue False) = True+zeroIsh _ = False++-- | Is the given value kind of one?+oneIsh :: PrimValue -> Bool+oneIsh (IntValue k) = oneIshInt k+oneIsh (FloatValue (Float32Value k)) = k == 1+oneIsh (FloatValue (Float64Value k)) = k == 1+oneIsh (BoolValue True) = True+oneIsh _ = False++-- | Is the given value kind of negative?+negativeIsh :: PrimValue -> Bool+negativeIsh (IntValue k) = negativeIshInt k+negativeIsh (FloatValue (Float32Value k)) = k < 0+negativeIsh (FloatValue (Float64Value k)) = k < 0+negativeIsh (BoolValue _) = False+negativeIsh Checked = False++-- | Is the given integer value kind of zero?+zeroIshInt :: IntValue -> Bool+zeroIshInt (Int8Value k) = k == 0+zeroIshInt (Int16Value k) = k == 0+zeroIshInt (Int32Value k) = k == 0+zeroIshInt (Int64Value k) = k == 0++-- | Is the given integer value kind of one?+oneIshInt :: IntValue -> Bool+oneIshInt (Int8Value k) = k == 1+oneIshInt (Int16Value k) = k == 1+oneIshInt (Int32Value k) = k == 1+oneIshInt (Int64Value k) = k == 1++-- | Is the given integer value kind of negative?+negativeIshInt :: IntValue -> Bool+negativeIshInt (Int8Value k) = k < 0+negativeIshInt (Int16Value k) = k < 0+negativeIshInt (Int32Value k) = k < 0+negativeIshInt (Int64Value k) = k < 0++-- | The size of a value of a given primitive type in bites.+primBitSize :: PrimType -> Int+primBitSize = (* 8) . primByteSize++-- | The size of a value of a given primitive type in eight-bit bytes.+primByteSize :: Num a => PrimType -> a+primByteSize (IntType t) = intByteSize t+primByteSize (FloatType t) = floatByteSize t+primByteSize Bool = 1+primByteSize Cert = 1++-- | The size of a value of a given integer type in eight-bit bytes.+intByteSize :: Num a => IntType -> a+intByteSize Int8 = 1+intByteSize Int16 = 2+intByteSize Int32 = 4+intByteSize Int64 = 8++-- | The size of a value of a given floating-point type in eight-bit bytes.+floatByteSize :: Num a => FloatType -> a+floatByteSize Float32 = 4+floatByteSize Float64 = 8++-- | True if the given binary operator is commutative.+commutativeBinOp :: BinOp -> Bool+commutativeBinOp Add {} = True+commutativeBinOp FAdd {} = True+commutativeBinOp Mul {} = True+commutativeBinOp FMul {} = True+commutativeBinOp And {} = True+commutativeBinOp Or {} = True+commutativeBinOp Xor {} = True+commutativeBinOp LogOr {} = True+commutativeBinOp LogAnd {} = True+commutativeBinOp SMax {} = True+commutativeBinOp SMin {} = True+commutativeBinOp UMax {} = True+commutativeBinOp UMin {} = True+commutativeBinOp FMax {} = True+commutativeBinOp FMin {} = True+commutativeBinOp _ = False++-- SexpIso instances++instance SexpIso BinOp where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "add") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+        With (. Sexp.list (Sexp.el (Sexp.sym "fadd") >>> Sexp.el sexpIso)) $+          With (. Sexp.list (Sexp.el (Sexp.sym "sub") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+            With (. Sexp.list (Sexp.el (Sexp.sym "fsub") >>> Sexp.el sexpIso)) $+              With (. Sexp.list (Sexp.el (Sexp.sym "mul") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                With (. Sexp.list (Sexp.el (Sexp.sym "fmul") >>> Sexp.el sexpIso)) $+                  With (. Sexp.list (Sexp.el (Sexp.sym "udiv") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                    With (. Sexp.list (Sexp.el (Sexp.sym "udivup") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                      With (. Sexp.list (Sexp.el (Sexp.sym "sdiv") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                        With (. Sexp.list (Sexp.el (Sexp.sym "sdivup") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                          With (. Sexp.list (Sexp.el (Sexp.sym "fdiv") >>> Sexp.el sexpIso)) $+                            With (. Sexp.list (Sexp.el (Sexp.sym "fmod") >>> Sexp.el sexpIso)) $+                              With (. Sexp.list (Sexp.el (Sexp.sym "umod") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                                With (. Sexp.list (Sexp.el (Sexp.sym "smod") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                                  With (. Sexp.list (Sexp.el (Sexp.sym "squot") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                                    With (. Sexp.list (Sexp.el (Sexp.sym "srem") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                                      With (. Sexp.list (Sexp.el (Sexp.sym "smin") >>> Sexp.el sexpIso)) $+                                        With (. Sexp.list (Sexp.el (Sexp.sym "umin") >>> Sexp.el sexpIso)) $+                                          With (. Sexp.list (Sexp.el (Sexp.sym "fmin") >>> Sexp.el sexpIso)) $+                                            With (. Sexp.list (Sexp.el (Sexp.sym "smax") >>> Sexp.el sexpIso)) $+                                              With (. Sexp.list (Sexp.el (Sexp.sym "umax") >>> Sexp.el sexpIso)) $+                                                With (. Sexp.list (Sexp.el (Sexp.sym "fmap") >>> Sexp.el sexpIso)) $+                                                  With (. Sexp.list (Sexp.el (Sexp.sym "shl") >>> Sexp.el sexpIso)) $+                                                    With (. Sexp.list (Sexp.el (Sexp.sym "lshr") >>> Sexp.el sexpIso)) $+                                                      With (. Sexp.list (Sexp.el (Sexp.sym "ashr") >>> Sexp.el sexpIso)) $+                                                        With (. Sexp.list (Sexp.el (Sexp.sym "and") >>> Sexp.el sexpIso)) $+                                                          With (. Sexp.list (Sexp.el (Sexp.sym "or") >>> Sexp.el sexpIso)) $+                                                            With (. Sexp.list (Sexp.el (Sexp.sym "xor") >>> Sexp.el sexpIso)) $+                                                              With (. Sexp.list (Sexp.el (Sexp.sym "pow") >>> Sexp.el sexpIso)) $+                                                                With (. Sexp.list (Sexp.el (Sexp.sym "fpow") >>> Sexp.el sexpIso)) $+                                                                  With (. Sexp.sym "logand") $+                                                                    With+                                                                      (. Sexp.sym "logor")+                                                                      End++instance SexpIso CmpOp where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "eq") >>> Sexp.el sexpIso)) $+        With (. Sexp.list (Sexp.el (Sexp.sym "ult") >>> Sexp.el sexpIso)) $+          With (. Sexp.list (Sexp.el (Sexp.sym "ule") >>> Sexp.el sexpIso)) $+            With (. Sexp.list (Sexp.el (Sexp.sym "slt") >>> Sexp.el sexpIso)) $+              With (. Sexp.list (Sexp.el (Sexp.sym "sle") >>> Sexp.el sexpIso)) $+                With (. Sexp.list (Sexp.el (Sexp.sym "lt") >>> Sexp.el sexpIso)) $+                  With (. Sexp.list (Sexp.el (Sexp.sym "le") >>> Sexp.el sexpIso)) $+                    With (. Sexp.sym "llt") $+                      With+                        (. Sexp.sym "lle")+                        End++instance SexpIso ConvOp where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "zext") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+        With (. Sexp.list (Sexp.el (Sexp.sym "sext") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+          With (. Sexp.list (Sexp.el (Sexp.sym "fpconv") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+            With (. Sexp.list (Sexp.el (Sexp.sym "fptoui") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+              With (. Sexp.list (Sexp.el (Sexp.sym "fptosi") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                With (. Sexp.list (Sexp.el (Sexp.sym "uitofp") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                  With (. Sexp.list (Sexp.el (Sexp.sym "sitofp") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                    With (. Sexp.list (Sexp.el (Sexp.sym "itob") >>> Sexp.el sexpIso)) $+                      With+                        (. Sexp.list (Sexp.el (Sexp.sym "btoi") >>> Sexp.el sexpIso))+                        End++-- Prettyprinting instances++instance Pretty BinOp where+  ppr (Add t OverflowWrap) = taggedI "add" t+  ppr (Add t OverflowUndef) = taggedI "add_nw" t+  ppr (Sub t OverflowWrap) = taggedI "sub" t+  ppr (Sub t OverflowUndef) = taggedI "sub_nw" t+  ppr (Mul t OverflowWrap) = taggedI "mul" t+  ppr (Mul t OverflowUndef) = taggedI "mul_nw" t+  ppr (FAdd t) = taggedF "fadd" t+  ppr (FSub t) = taggedF "fsub" t+  ppr (FMul t) = taggedF "fmul" t+  ppr (UDiv t Safe) = taggedI "udiv_safe" t+  ppr (UDiv t Unsafe) = taggedI "udiv" t+  ppr (UDivUp t Safe) = taggedI "udiv_up_safe" t+  ppr (UDivUp t Unsafe) = taggedI "udiv_up" t+  ppr (UMod t Safe) = taggedI "umod_safe" t+  ppr (UMod t Unsafe) = taggedI "umod" t+  ppr (SDiv t Safe) = taggedI "sdiv_safe" t+  ppr (SDiv t Unsafe) = taggedI "sdiv" t+  ppr (SDivUp t Safe) = taggedI "sdiv_up_safe" t+  ppr (SDivUp t Unsafe) = taggedI "sdiv_up" t+  ppr (SMod t Safe) = taggedI "smod_safe" t+  ppr (SMod t Unsafe) = taggedI "smod" t+  ppr (SQuot t Safe) = taggedI "squot_safe" t+  ppr (SQuot t Unsafe) = taggedI "squot" t+  ppr (SRem t Safe) = taggedI "srem_safe" t+  ppr (SRem t Unsafe) = taggedI "srem" t+  ppr (FDiv t) = taggedF "fdiv" t+  ppr (FMod t) = taggedF "fmod" t+  ppr (SMin t) = taggedI "smin" t+  ppr (UMin t) = taggedI "umin" t+  ppr (FMin t) = taggedF "fmin" t+  ppr (SMax t) = taggedI "smax" t+  ppr (UMax t) = taggedI "umax" t+  ppr (FMax t) = taggedF "fmax" t+  ppr (Shl t) = taggedI "shl" t+  ppr (LShr t) = taggedI "lshr" t+  ppr (AShr t) = taggedI "ashr" t+  ppr (And t) = taggedI "and" t+  ppr (Or t) = taggedI "or" t+  ppr (Xor t) = taggedI "xor" t+  ppr (Pow t) = taggedI "pow" t+  ppr (FPow t) = taggedF "fpow" t+  ppr LogAnd = text "logand"+  ppr LogOr = text "logor"++instance Pretty CmpOp where+  ppr (CmpEq t) = text "eq_" <> ppr t+  ppr (CmpUlt t) = taggedI "ult" t+  ppr (CmpUle t) = taggedI "ule" t+  ppr (CmpSlt t) = taggedI "slt" t+  ppr (CmpSle t) = taggedI "sle" t+  ppr (FCmpLt t) = taggedF "lt" t+  ppr (FCmpLe t) = taggedF "le" t+  ppr CmpLlt = text "llt"+  ppr CmpLle = text "lle"++instance Pretty ConvOp where+  ppr op = convOp (convOpFun op) from to+    where+      (from, to) = convOpType op++instance Pretty UnOp where+  ppr Not = text "not"+  ppr (Abs t) = taggedI "abs" t+  ppr (FAbs t) = taggedF "fabs" t+  ppr (SSignum t) = taggedI "ssignum" t+  ppr (USignum t) = taggedI "usignum" t+  ppr (Complement t) = taggedI "complement" t++-- | The human-readable name for a 'ConvOp'.  This is used to expose+-- the 'ConvOp' in the @intrinsics@ module of a Futhark program.+convOpFun :: ConvOp -> String+convOpFun ZExt {} = "zext"+convOpFun SExt {} = "sext"+convOpFun FPConv {} = "fpconv"+convOpFun FPToUI {} = "fptoui"+convOpFun FPToSI {} = "fptosi"+convOpFun UIToFP {} = "uitofp"+convOpFun SIToFP {} = "sitofp"+convOpFun IToB {} = "itob"+convOpFun BToI {} = "btoi"++taggedI :: String -> IntType -> Doc+taggedI s Int8 = text $ s ++ "8"+taggedI s Int16 = text $ s ++ "16"+taggedI s Int32 = text $ s ++ "32"+taggedI s Int64 = text $ s ++ "64"++taggedF :: String -> FloatType -> Doc+taggedF s Float32 = text $ s ++ "32"+taggedF s Float64 = text $ s ++ "64"++convOp :: (Pretty from, Pretty to) => String -> from -> to -> Doc+convOp s from to = text s <> text "_" <> ppr from <> text "_" <> ppr to++-- | True if signed.  Only makes a difference for integer types.+prettySigned :: Bool -> PrimType -> String+prettySigned True (IntType it) = 'u' : drop 1 (pretty it)+prettySigned _ t = pretty t++mul_hi8 :: IntValue -> IntValue -> Int8+mul_hi8 a b =+  let a' = intToWord64 a+      b' = intToWord64 b+   in fromIntegral (shiftR (a' * b') 8)++mul_hi16 :: IntValue -> IntValue -> Int16+mul_hi16 a b =+  let a' = intToWord64 a+      b' = intToWord64 b+   in fromIntegral (shiftR (a' * b') 16)++mul_hi32 :: IntValue -> IntValue -> Int32+mul_hi32 a b =+  let a' = intToWord64 a+      b' = intToWord64 b+   in fromIntegral (shiftR (a' * b') 32)++mul_hi64 :: IntValue -> IntValue -> Int64+mul_hi64 a b =+  let a' = (toInteger . intToWord64) a+      b' = (toInteger . intToWord64) b+   in fromIntegral (shiftR (a' * b') 64)  mad_hi8 :: IntValue -> IntValue -> Int8 -> Int8 mad_hi8 a b c = mul_hi8 a b + c
src/Futhark/IR/Prop.hs view
@@ -1,80 +1,81 @@-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Safe #-}+{-# LANGUAGE TypeFamilies #-}+ -- | This module provides various simple ways to query and manipulate -- fundamental Futhark terms, such as types and values.  The intent is to -- keep "Futhark.IRrsentation.AST.Syntax" simple, and put whatever -- embellishments we need here.  This is an internal, desugared -- representation. module Futhark.IR.Prop-  ( module Futhark.IR.Prop.Reshape-  , module Futhark.IR.Prop.Rearrange-  , module Futhark.IR.Prop.Types-  , module Futhark.IR.Prop.Constants-  , module Futhark.IR.Prop.TypeOf-  , module Futhark.IR.Prop.Patterns-  , module Futhark.IR.Prop.Names-  , module Futhark.IR.RetType--  -- * Built-in functions-  , isBuiltInFunction-  , builtInFunctions+  ( module Futhark.IR.Prop.Reshape,+    module Futhark.IR.Prop.Rearrange,+    module Futhark.IR.Prop.Types,+    module Futhark.IR.Prop.Constants,+    module Futhark.IR.Prop.TypeOf,+    module Futhark.IR.Prop.Patterns,+    module Futhark.IR.Prop.Names,+    module Futhark.IR.RetType, -  -- * Extra tools-  , asBasicOp-  , safeExp-  , subExpVars-  , subExpVar-  , shapeVars-  , commutativeLambda-  , entryPointSize-  , defAux-  , stmCerts-  , certify-  , expExtTypesFromPattern-  , attrsForAssert+    -- * Built-in functions+    isBuiltInFunction,+    builtInFunctions, -  , ASTConstraints-  , IsOp (..)-  , ASTLore (..)+    -- * Extra tools+    asBasicOp,+    safeExp,+    subExpVars,+    subExpVar,+    shapeVars,+    commutativeLambda,+    entryPointSize,+    defAux,+    stmCerts,+    certify,+    expExtTypesFromPattern,+    attrsForAssert,+    ASTConstraints,+    IsOp (..),+    ASTLore (..),   )-  where+where  import Data.List (find)-import Data.Maybe (mapMaybe, isJust) import qualified Data.Map.Strict as M+import Data.Maybe (isJust, mapMaybe) import qualified Data.Set as S--import Futhark.IR.Prop.Reshape-import Futhark.IR.Prop.Rearrange-import Futhark.IR.Prop.Types+import Futhark.IR.Pretty import Futhark.IR.Prop.Constants-import Futhark.IR.Prop.Patterns import Futhark.IR.Prop.Names+import Futhark.IR.Prop.Patterns+import Futhark.IR.Prop.Rearrange+import Futhark.IR.Prop.Reshape import Futhark.IR.Prop.TypeOf+import Futhark.IR.Prop.Types import Futhark.IR.RetType import Futhark.IR.Syntax-import Futhark.IR.Pretty import Futhark.Transform.Rename (Rename, Renameable)-import Futhark.Transform.Substitute (Substitute, Substitutable)+import Futhark.Transform.Substitute (Substitutable, Substitute) import Futhark.Util.Pretty+import Language.SexpGrammar as Sexp  -- | @isBuiltInFunction k@ is 'True' if @k@ is an element of 'builtInFunctions'. isBuiltInFunction :: Name -> Bool isBuiltInFunction fnm = fnm `M.member` builtInFunctions  -- | A map of all built-in functions and their types.-builtInFunctions :: M.Map Name (PrimType,[PrimType])+builtInFunctions :: M.Map Name (PrimType, [PrimType]) builtInFunctions = M.fromList $ map namify $ M.toList primFuns-  where namify (k,(paramts,ret,_)) = (nameFromString k, (ret, paramts))+  where+    namify (k, (paramts, ret, _)) = (nameFromString k, (ret, paramts))  -- | If the expression is a t'BasicOp', return it, otherwise 'Nothing'. asBasicOp :: Exp lore -> Maybe BasicOp asBasicOp (BasicOp op) = Just op-asBasicOp _            = Nothing+asBasicOp _ = Nothing  -- | An expression is safe if it is always well-defined (assuming that -- any required certificates have been checked) in any context.  For@@ -82,57 +83,54 @@ -- bounds.  On the other hand, adding two numbers cannot fail. safeExp :: IsOp (Op lore) => Exp lore -> Bool safeExp (BasicOp op) = safeBasicOp op-  where safeBasicOp (BinOp (SDiv _ Safe) _ _) = True-        safeBasicOp (BinOp (SDivUp _ Safe) _ _) = True-        safeBasicOp (BinOp (SQuot _ Safe) _ _) = True-        safeBasicOp (BinOp (UDiv _ Safe) _ _) = True-        safeBasicOp (BinOp (UDivUp _ Safe) _ _) = True-        safeBasicOp (BinOp (SMod _ Safe) _ _) = True-        safeBasicOp (BinOp (SRem _ Safe) _ _) = True-        safeBasicOp (BinOp (UMod _ Safe) _ _) = True--        safeBasicOp (BinOp SDiv{} _ (Constant y)) = not $ zeroIsh y-        safeBasicOp (BinOp SDiv{} _ _) = False-        safeBasicOp (BinOp SDivUp{} _ (Constant y)) = not $ zeroIsh y-        safeBasicOp (BinOp SDivUp{} _ _) = False-        safeBasicOp (BinOp UDiv{} _ (Constant y)) = not $ zeroIsh y-        safeBasicOp (BinOp UDiv{} _ _) = False-        safeBasicOp (BinOp UDivUp{} _ (Constant y)) = not $ zeroIsh y-        safeBasicOp (BinOp UDivUp{} _ _) = False-        safeBasicOp (BinOp SMod{} _ (Constant y)) = not $ zeroIsh y-        safeBasicOp (BinOp SMod{} _ _) = False-        safeBasicOp (BinOp UMod{} _ (Constant y)) = not $ zeroIsh y-        safeBasicOp (BinOp UMod{} _ _) = False--        safeBasicOp (BinOp SQuot{} _ (Constant y)) = not $ zeroIsh y-        safeBasicOp (BinOp SQuot{} _ _) = False-        safeBasicOp (BinOp SRem{} _ (Constant y)) = not $ zeroIsh y-        safeBasicOp (BinOp SRem{} _ _) = False--        safeBasicOp (BinOp Pow{} _ (Constant y)) = not $ negativeIsh y-        safeBasicOp (BinOp Pow{} _ _) = False-        safeBasicOp ArrayLit{} = True-        safeBasicOp BinOp{} = True-        safeBasicOp SubExp{} = True-        safeBasicOp UnOp{} = True-        safeBasicOp CmpOp{} = True-        safeBasicOp ConvOp{} = True-        safeBasicOp Scratch{} = True-        safeBasicOp Concat{} = True-        safeBasicOp Reshape{} = True-        safeBasicOp Rearrange{} = True-        safeBasicOp Manifest{} = True-        safeBasicOp Iota{} = True-        safeBasicOp Replicate{} = True-        safeBasicOp Copy{} = True-        safeBasicOp _ = False-+  where+    safeBasicOp (BinOp (SDiv _ Safe) _ _) = True+    safeBasicOp (BinOp (SDivUp _ Safe) _ _) = True+    safeBasicOp (BinOp (SQuot _ Safe) _ _) = True+    safeBasicOp (BinOp (UDiv _ Safe) _ _) = True+    safeBasicOp (BinOp (UDivUp _ Safe) _ _) = True+    safeBasicOp (BinOp (SMod _ Safe) _ _) = True+    safeBasicOp (BinOp (SRem _ Safe) _ _) = True+    safeBasicOp (BinOp (UMod _ Safe) _ _) = True+    safeBasicOp (BinOp SDiv {} _ (Constant y)) = not $ zeroIsh y+    safeBasicOp (BinOp SDiv {} _ _) = False+    safeBasicOp (BinOp SDivUp {} _ (Constant y)) = not $ zeroIsh y+    safeBasicOp (BinOp SDivUp {} _ _) = False+    safeBasicOp (BinOp UDiv {} _ (Constant y)) = not $ zeroIsh y+    safeBasicOp (BinOp UDiv {} _ _) = False+    safeBasicOp (BinOp UDivUp {} _ (Constant y)) = not $ zeroIsh y+    safeBasicOp (BinOp UDivUp {} _ _) = False+    safeBasicOp (BinOp SMod {} _ (Constant y)) = not $ zeroIsh y+    safeBasicOp (BinOp SMod {} _ _) = False+    safeBasicOp (BinOp UMod {} _ (Constant y)) = not $ zeroIsh y+    safeBasicOp (BinOp UMod {} _ _) = False+    safeBasicOp (BinOp SQuot {} _ (Constant y)) = not $ zeroIsh y+    safeBasicOp (BinOp SQuot {} _ _) = False+    safeBasicOp (BinOp SRem {} _ (Constant y)) = not $ zeroIsh y+    safeBasicOp (BinOp SRem {} _ _) = False+    safeBasicOp (BinOp Pow {} _ (Constant y)) = not $ negativeIsh y+    safeBasicOp (BinOp Pow {} _ _) = False+    safeBasicOp ArrayLit {} = True+    safeBasicOp BinOp {} = True+    safeBasicOp SubExp {} = True+    safeBasicOp UnOp {} = True+    safeBasicOp CmpOp {} = True+    safeBasicOp ConvOp {} = True+    safeBasicOp Scratch {} = True+    safeBasicOp Concat {} = True+    safeBasicOp Reshape {} = True+    safeBasicOp Rearrange {} = True+    safeBasicOp Manifest {} = True+    safeBasicOp Iota {} = True+    safeBasicOp Replicate {} = True+    safeBasicOp Copy {} = True+    safeBasicOp _ = False safeExp (DoLoop _ _ _ body) = safeBody body safeExp (Apply fname _ _ _) =   isBuiltInFunction fname safeExp (If _ tbranch fbranch _) =-  all (safeExp . stmExp) (bodyStms tbranch) &&-  all (safeExp . stmExp) (bodyStms fbranch)+  all (safeExp . stmExp) (bodyStms tbranch)+    && all (safeExp . stmExp) (bodyStms fbranch) safeExp (Op op) = safeOp op  safeBody :: IsOp (Op lore) => Body lore -> Bool@@ -145,8 +143,8 @@  -- | If the t'SubExp' is a 'Var' return the variable name. subExpVar :: SubExp -> Maybe VName-subExpVar (Var v)    = Just v-subExpVar Constant{} = Nothing+subExpVar (Var v) = Just v+subExpVar Constant {} = Nothing  -- | Return the variable dimension sizes.  May contain -- duplicates.@@ -161,19 +159,19 @@ commutativeLambda lam =   let body = lambdaBody lam       n2 = length (lambdaParams lam) `div` 2-      (xps,yps) = splitAt n2 (lambdaParams lam)+      (xps, yps) = splitAt n2 (lambdaParams lam)        okComponent c = isJust $ find (okBinOp c) $ bodyStms body-      okBinOp (xp,yp,Var r) (Let (Pattern [] [pe]) _ (BasicOp (BinOp op (Var x) (Var y)))) =-        patElemName pe == r &&-        commutativeBinOp op &&-        ((x == paramName xp && y == paramName yp) ||-         (y == paramName xp && x == paramName yp))+      okBinOp (xp, yp, Var r) (Let (Pattern [] [pe]) _ (BasicOp (BinOp op (Var x) (Var y)))) =+        patElemName pe == r+          && commutativeBinOp op+          && ( (x == paramName xp && y == paramName yp)+                 || (y == paramName xp && x == paramName yp)+             )       okBinOp _ _ = False--  in n2 * 2 == length (lambdaParams lam) &&-     n2 == length (bodyResult body) &&-     all okComponent (zip3 xps yps $ bodyResult body)+   in n2 * 2 == length (lambdaParams lam)+        && n2 == length (bodyResult body)+        && all okComponent (zip3 xps yps $ bodyResult body)  -- | How many value parameters are accepted by this entry point?  This -- is used to determine which of the function parameters correspond to@@ -195,17 +193,18 @@ -- | Add certificates to a statement. certify :: Certificates -> Stm lore -> Stm lore certify cs1 (Let pat (StmAux cs2 attrs dec) e) =-  Let pat (StmAux (cs2<>cs1) attrs dec) e+  Let pat (StmAux (cs2 <> cs1) attrs dec) e  -- | A handy shorthand for properties that we usually want to things -- we stuff into ASTs. type ASTConstraints a =-  (Eq a, Ord a, Show a, Rename a, Substitute a, FreeIn a, Pretty a)+  (Eq a, Ord a, Show a, Rename a, Substitute a, FreeIn a, Pretty a, SexpIso a)  -- | A type class for operations. class (ASTConstraints op, TypedOp op) => IsOp op where   -- | Like 'safeExp', but for arbitrary ops.   safeOp :: op -> Bool+   -- | Should we try to hoist this out of branches?   cheapOp :: op -> Bool @@ -215,35 +214,40 @@  -- | Lore-specific attributes; also means the lore supports some basic -- facilities.-class (Decorations lore,--       PrettyLore lore,--       Renameable lore, Substitutable lore,-       FreeDec (ExpDec lore),-       FreeIn (LetDec lore),-       FreeDec (BodyDec lore),-       FreeIn (FParamInfo lore),-       FreeIn (LParamInfo lore),-       FreeIn (RetType lore),-       FreeIn (BranchType lore),--       IsOp (Op lore)) => ASTLore lore where+class+  ( Decorations lore,+    PrettyLore lore,+    Renameable lore,+    Substitutable lore,+    FreeDec (ExpDec lore),+    FreeIn (LetDec lore),+    FreeDec (BodyDec lore),+    FreeIn (FParamInfo lore),+    FreeIn (LParamInfo lore),+    FreeIn (RetType lore),+    FreeIn (BranchType lore),+    IsOp (Op lore)+  ) =>+  ASTLore lore+  where   -- | Given a pattern, construct the type of a body that would match   -- it.  An implementation for many lores would be   -- 'expExtTypesFromPattern'.-  expTypesFromPattern :: (HasScope lore m, Monad m) =>-                         Pattern lore -> m [BranchType lore]+  expTypesFromPattern ::+    (HasScope lore m, Monad m) =>+    Pattern lore ->+    m [BranchType lore]  -- | Construct the type of an expression that would match the pattern. expExtTypesFromPattern :: Typed dec => PatternT dec -> [ExtType] expExtTypesFromPattern pat =   existentialiseExtTypes (patternContextNames pat) $-  staticShapes $ map patElemType $ patternValueElements pat+    staticShapes $ map patElemType $ patternValueElements pat  -- | Keep only those attributes that are relevant for 'Assert' -- expressions. attrsForAssert :: Attrs -> Attrs attrsForAssert (Attrs attrs) =   Attrs $ S.filter attrForAssert attrs-  where attrForAssert = (==AttrComp "warn" ["safety_checks"])+  where+    attrForAssert = (== AttrComp "warn" ["safety_checks"])
src/Futhark/IR/Prop/Aliases.hs view
@@ -1,5 +1,7 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeFamilies #-}-{-# Language FlexibleInstances, FlexibleContexts #-}+ -- | The IR tracks aliases, mostly to ensure the soundness of in-place -- updates, but it can also be used for other things (such as memory -- optimisations).  This module contains the raw building blocks for@@ -10,35 +12,42 @@ -- Thus, they do not take aliases-of-aliases into account.  See -- "Futhark.Analysis.Alias" if this is not what you want. module Futhark.IR.Prop.Aliases-       ( subExpAliases-       , expAliases-       , patternAliases-       , Aliased (..)-       , AliasesOf (..)-         -- * Consumption-       , consumedInStm-       , consumedInExp-       , consumedByLambda-       -- * Extensibility-       , AliasedOp (..)-       , CanBeAliased (..)-       )-       where+  ( subExpAliases,+    expAliases,+    patternAliases,+    Aliased (..),+    AliasesOf (..), +    -- * Consumption+    consumedInStm,+    consumedInExp,+    consumedByLambda,++    -- * Extensibility+    AliasedOp (..),+    CanBeAliased (..),+  )+where+ import Control.Arrow (first) import qualified Data.Kind- import Futhark.IR.Prop (IsOp)-import Futhark.IR.Syntax+import Futhark.IR.Prop.Names import Futhark.IR.Prop.Patterns import Futhark.IR.Prop.Types-import Futhark.IR.Prop.Names+import Futhark.IR.Syntax  -- | The class of lores that contain aliasing information.-class (Decorations lore, AliasedOp (Op lore),-       AliasesOf (LetDec lore)) => Aliased lore where+class+  ( Decorations lore,+    AliasedOp (Op lore),+    AliasesOf (LetDec lore)+  ) =>+  Aliased lore+  where   -- | The aliases of the body results.   bodyAliases :: Body lore -> [Names]+   -- | The variables consumed in the body.   consumedInBody :: Body lore -> Names @@ -47,71 +56,76 @@  -- | The alises of a subexpression. subExpAliases :: SubExp -> Names-subExpAliases Constant{} = mempty-subExpAliases (Var v)    = vnameAliases v+subExpAliases Constant {} = mempty+subExpAliases (Var v) = vnameAliases v  basicOpAliases :: BasicOp -> [Names] basicOpAliases (SubExp se) = [subExpAliases se] basicOpAliases (Opaque se) = [subExpAliases se] basicOpAliases (ArrayLit _ _) = [mempty]-basicOpAliases BinOp{} = [mempty]-basicOpAliases ConvOp{} = [mempty]-basicOpAliases CmpOp{} = [mempty]-basicOpAliases UnOp{} = [mempty]+basicOpAliases BinOp {} = [mempty]+basicOpAliases ConvOp {} = [mempty]+basicOpAliases CmpOp {} = [mempty]+basicOpAliases UnOp {} = [mempty] basicOpAliases (Index ident _) = [vnameAliases ident]-basicOpAliases Update{} = [mempty]-basicOpAliases Iota{} = [mempty]-basicOpAliases Replicate{} = [mempty]-basicOpAliases Scratch{} = [mempty]+basicOpAliases Update {} = [mempty]+basicOpAliases Iota {} = [mempty]+basicOpAliases Replicate {} = [mempty]+basicOpAliases Scratch {} = [mempty] basicOpAliases (Reshape _ e) = [vnameAliases e] basicOpAliases (Rearrange _ e) = [vnameAliases e] basicOpAliases (Rotate _ e) = [vnameAliases e]-basicOpAliases Concat{} = [mempty]-basicOpAliases Copy{} = [mempty]-basicOpAliases Manifest{} = [mempty]-basicOpAliases Assert{} = [mempty]+basicOpAliases Concat {} = [mempty]+basicOpAliases Copy {} = [mempty]+basicOpAliases Manifest {} = [mempty]+basicOpAliases Assert {} = [mempty]  ifAliases :: ([Names], Names) -> ([Names], Names) -> [Names]-ifAliases (als1,cons1) (als2,cons2) =+ifAliases (als1, cons1) (als2, cons2) =   map (`namesSubtract` cons) $ zipWith mappend als1 als2-  where cons = cons1 <> cons2+  where+    cons = cons1 <> cons2  funcallAliases :: [(SubExp, Diet)] -> [TypeBase shape Uniqueness] -> [Names] funcallAliases args t =-  returnAliases t [(subExpAliases se, d) | (se,d) <- args ]+  returnAliases t [(subExpAliases se, d) | (se, d) <- args]  -- | The aliases of an expression, one per non-context value returned. expAliases :: (Aliased lore) => Exp lore -> [Names] expAliases (If _ tb fb dec) =   drop (length all_aliases - length ts) all_aliases-  where ts = ifReturns dec-        all_aliases = ifAliases-                      (bodyAliases tb, consumedInBody tb)-                      (bodyAliases fb, consumedInBody fb)+  where+    ts = ifReturns dec+    all_aliases =+      ifAliases+        (bodyAliases tb, consumedInBody tb)+        (bodyAliases fb, consumedInBody fb) expAliases (BasicOp op) = basicOpAliases op expAliases (DoLoop ctxmerge valmerge _ loopbody) =   map (`namesSubtract` merge_names) val_aliases-  where (_ctx_aliases, val_aliases) =-          splitAt (length ctxmerge) $ bodyAliases loopbody-        merge_names = namesFromList $ map (paramName . fst) $ ctxmerge ++ valmerge+  where+    (_ctx_aliases, val_aliases) =+      splitAt (length ctxmerge) $ bodyAliases loopbody+    merge_names = namesFromList $ map (paramName . fst) $ ctxmerge ++ valmerge expAliases (Apply _ args t _) =   funcallAliases args $ map declExtTypeOf t expAliases (Op op) = opAliases op  returnAliases :: [TypeBase shape Uniqueness] -> [(Names, Diet)] -> [Names] returnAliases rts args = map returnType' rts-  where returnType' (Array _ _ Nonunique) =-          mconcat $ map (uncurry maskAliases) args-        returnType' (Array _ _ Unique) =-          mempty-        returnType' (Prim _) =-          mempty-        returnType' Mem{} =-          error "returnAliases Mem"+  where+    returnType' (Array _ _ Nonunique) =+      mconcat $ map (uncurry maskAliases) args+    returnType' (Array _ _ Unique) =+      mempty+    returnType' (Prim _) =+      mempty+    returnType' Mem {} =+      error "returnAliases Mem"  maskAliases :: Names -> Diet -> Names-maskAliases _   Consume = mempty-maskAliases _   ObservePrim = mempty+maskAliases _ Consume = mempty+maskAliases _ ObservePrim = mempty maskAliases als Observe = als  -- | The variables consumed in this statement.@@ -122,13 +136,16 @@ consumedInExp :: (Aliased lore) => Exp lore -> Names consumedInExp (Apply _ args _ _) =   mconcat (map (consumeArg . first subExpAliases) args)-  where consumeArg (als, Consume) = als-        consumeArg _              = mempty+  where+    consumeArg (als, Consume) = als+    consumeArg _ = mempty consumedInExp (If _ tb fb _) =   consumedInBody tb <> consumedInBody fb consumedInExp (DoLoop _ merge _ _) =-  mconcat (map (subExpAliases . snd) $-           filter (unique . paramDeclType . fst) merge)+  mconcat+    ( map (subExpAliases . snd) $+        filter (unique . paramDeclType . fst) merge+    ) consumedInExp (BasicOp (Update src _ _)) = oneName src consumedInExp (Op op) = consumedInOp op consumedInExp _ = mempty
src/Futhark/IR/Prop/Constants.hs view
@@ -1,12 +1,11 @@ -- | Possibly convenient facilities for constructing constants. module Futhark.IR.Prop.Constants-       (-         IsValue (..)-       , constant-       , intConst-       , floatConst-       )-       where+  ( IsValue (..),+    constant,+    intConst,+    floatConst,+  )+where  import Futhark.IR.Syntax.Core @@ -17,9 +16,6 @@ -- (LogVal True) loc@. class IsValue a where   value :: a -> PrimValue--instance IsValue Int where-  value = IntValue . Int32Value . fromIntegral  instance IsValue Int8 where   value = IntValue . Int8Value
src/Futhark/IR/Prop/Names.hs view
@@ -1,55 +1,72 @@-{-# LANGUAGE FlexibleInstances, FlexibleContexts, UndecidableInstances #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE UndecidableInstances #-}+ -- | Facilities for determining which names are used in some syntactic -- construct.  The most important interface is the 'FreeIn' class and -- its instances, but for reasons related to the Haskell type system, -- some constructs have specialised functions. module Futhark.IR.Prop.Names-       ( -- * Free names-         Names-       , namesIntMap-       , nameIn-       , oneName-       , namesFromList-       , namesToList-       , namesIntersection-       , namesIntersect-       , namesSubtract-       , mapNames-       -- * Class-       , FreeIn (..)-       , freeIn-       -- * Specialised Functions-       , freeInStmsAndRes-       -- * Bound Names-       , boundInBody-       , boundByStm-       , boundByStms-       , boundByLambda-       -- * Efficient computation-       , FreeDec(..)-       , FV-       , fvBind-       , fvName-       , fvNames-       )-       where+  ( -- * Free names+    Names,+    namesIntMap,+    nameIn,+    oneName,+    namesFromList,+    namesToList,+    namesIntersection,+    namesIntersect,+    namesSubtract,+    mapNames, +    -- * Class+    FreeIn (..),+    freeIn,++    -- * Specialised Functions+    freeInStmsAndRes,++    -- * Bound Names+    boundInBody,+    boundByStm,+    boundByStms,+    boundByLambda,++    -- * Efficient computation+    FreeDec (..),+    FV,+    fvBind,+    fvName,+    fvNames,+  )+where++import Control.Category import Control.Monad.State.Strict+import Data.Foldable import qualified Data.IntMap.Strict as IM import qualified Data.Map.Strict as M-import Data.Foldable--import Futhark.IR.Syntax-import Futhark.IR.Traversals import Futhark.IR.Prop.Patterns import Futhark.IR.Prop.Scope+import Futhark.IR.Syntax+import Futhark.IR.Traversals import Futhark.Util.Pretty+import GHC.Generics+import Language.SexpGrammar as Sexp+import Language.SexpGrammar.Generic+import Prelude hiding (id, (.))  -- | A set of names.  Note that the 'Ord' instance is a dummy that -- treats everything as 'EQ' if '==', and otherwise 'LT'. newtype Names = Names (IM.IntMap VName)-              deriving (Eq, Show)+  deriving (Eq, Show, Generic) +instance SexpIso Names where+  sexpIso = with $ \names ->+    (iso IM.fromList IM.toList . sexpIso) >>> names+ -- | Retrieve the data structure underlying the names representation. namesIntMap :: Names -> IM.IntMap VName namesIntMap (Names m) = m@@ -99,7 +116,8 @@ mapNames f vs = namesFromList $ map f $ namesToList vs  -- | A computation to build a free variable set.-newtype FV = FV { unFV :: Names }+newtype FV = FV {unFV :: Names}+ -- Right now the variable set is just stored explicitly, without the -- fancy functional representation that GHC uses.  Turns out it's -- faster this way.@@ -122,32 +140,39 @@ fvNames :: Names -> FV fvNames = FV -freeWalker :: (FreeDec (ExpDec lore),-               FreeDec (BodyDec lore),-               FreeIn (FParamInfo lore),-               FreeIn (LParamInfo lore),-               FreeIn (LetDec lore),-               FreeIn (Op lore)) =>-              Walker lore (State FV)-freeWalker = identityWalker {-               walkOnSubExp = modify . (<>) . freeIn'-             , walkOnBody = \scope body -> do-                 modify $ (<>) $ freeIn' body-                 modify $ fvBind (namesFromList (M.keys scope))-             , walkOnVName = modify . (<>) . fvName-             , walkOnOp = modify . (<>) . freeIn'-             }+freeWalker ::+  ( FreeDec (ExpDec lore),+    FreeDec (BodyDec lore),+    FreeIn (FParamInfo lore),+    FreeIn (LParamInfo lore),+    FreeIn (LetDec lore),+    FreeIn (Op lore)+  ) =>+  Walker lore (State FV)+freeWalker =+  identityWalker+    { walkOnSubExp = modify . (<>) . freeIn',+      walkOnBody = \scope body -> do+        modify $ (<>) $ freeIn' body+        modify $ fvBind (namesFromList (M.keys scope)),+      walkOnVName = modify . (<>) . fvName,+      walkOnOp = modify . (<>) . freeIn'+    }  -- | Return the set of variable names that are free in the given -- statements and result.  Filters away the names that are bound by -- the statements.-freeInStmsAndRes :: (FreeIn (Op lore),-                     FreeIn (LetDec lore),-                     FreeIn (LParamInfo lore),-                     FreeIn (FParamInfo lore),-                     FreeDec (BodyDec lore),-                     FreeDec (ExpDec lore)) =>-                    Stms lore -> Result -> FV+freeInStmsAndRes ::+  ( FreeIn (Op lore),+    FreeIn (LetDec lore),+    FreeIn (LParamInfo lore),+    FreeIn (FParamInfo lore),+    FreeDec (BodyDec lore),+    FreeDec (ExpDec lore)+  ) =>+  Stms lore ->+  Result ->+  FV freeInStmsAndRes stms res =   fvBind (boundByStms stms) $ foldMap freeIn' stms <> freeIn' res @@ -170,71 +195,94 @@ instance FreeIn Int where   freeIn' = const mempty -instance (FreeIn a, FreeIn b) => FreeIn (a,b) where-  freeIn' (a,b) = freeIn' a <> freeIn' b+instance (FreeIn a, FreeIn b) => FreeIn (a, b) where+  freeIn' (a, b) = freeIn' a <> freeIn' b -instance (FreeIn a, FreeIn b, FreeIn c) => FreeIn (a,b,c) where-  freeIn' (a,b,c) = freeIn' a <> freeIn' b <> freeIn' c+instance (FreeIn a, FreeIn b, FreeIn c) => FreeIn (a, b, c) where+  freeIn' (a, b, c) = freeIn' a <> freeIn' b <> freeIn' c  instance FreeIn a => FreeIn [a] where   freeIn' = foldMap freeIn' -instance (FreeDec (ExpDec lore),-          FreeDec (BodyDec lore),-          FreeIn (FParamInfo lore),-          FreeIn (LParamInfo lore),-          FreeIn (LetDec lore),-          FreeIn (RetType lore),-          FreeIn (Op lore)) => FreeIn (FunDef lore) where+instance+  ( FreeDec (ExpDec lore),+    FreeDec (BodyDec lore),+    FreeIn (FParamInfo lore),+    FreeIn (LParamInfo lore),+    FreeIn (LetDec lore),+    FreeIn (RetType lore),+    FreeIn (Op lore)+  ) =>+  FreeIn (FunDef lore)+  where   freeIn' (FunDef _ _ _ rettype params body) =     fvBind (namesFromList $ map paramName params) $-    freeIn' rettype <> freeIn' params <> freeIn' body+      freeIn' rettype <> freeIn' params <> freeIn' body -instance (FreeDec (ExpDec lore),-          FreeDec (BodyDec lore),-          FreeIn (FParamInfo lore),-          FreeIn (LParamInfo lore),-          FreeIn (LetDec lore),-          FreeIn (Op lore)) => FreeIn (Lambda lore) where+instance+  ( FreeDec (ExpDec lore),+    FreeDec (BodyDec lore),+    FreeIn (FParamInfo lore),+    FreeIn (LParamInfo lore),+    FreeIn (LetDec lore),+    FreeIn (Op lore)+  ) =>+  FreeIn (Lambda lore)+  where   freeIn' (Lambda params body rettype) =     fvBind (namesFromList $ map paramName params) $-    freeIn' rettype <> freeIn' params <> freeIn' body+      freeIn' rettype <> freeIn' params <> freeIn' body -instance (FreeDec (ExpDec lore),-          FreeDec (BodyDec lore),-          FreeIn (FParamInfo lore),-          FreeIn (LParamInfo lore),-          FreeIn (LetDec lore),-          FreeIn (Op lore)) => FreeIn (Body lore) where+instance+  ( FreeDec (ExpDec lore),+    FreeDec (BodyDec lore),+    FreeIn (FParamInfo lore),+    FreeIn (LParamInfo lore),+    FreeIn (LetDec lore),+    FreeIn (Op lore)+  ) =>+  FreeIn (Body lore)+  where   freeIn' (Body dec stms res) =     precomputed dec $ freeIn' dec <> freeInStmsAndRes stms res -instance (FreeDec (ExpDec lore),-          FreeDec (BodyDec lore),-          FreeIn (FParamInfo lore),-          FreeIn (LParamInfo lore),-          FreeIn (LetDec lore),-          FreeIn (Op lore)) => FreeIn (Exp lore) where+instance+  ( FreeDec (ExpDec lore),+    FreeDec (BodyDec lore),+    FreeIn (FParamInfo lore),+    FreeIn (LParamInfo lore),+    FreeIn (LetDec lore),+    FreeIn (Op lore)+  ) =>+  FreeIn (Exp lore)+  where   freeIn' (DoLoop ctxmerge valmerge form loopbody) =     let (ctxparams, ctxinits) = unzip ctxmerge         (valparams, valinits) = unzip valmerge-        bound_here = namesFromList $ M.keys $-                     scopeOf form <>-                     scopeOfFParams (ctxparams ++ valparams)-    in fvBind bound_here $-       freeIn' (ctxinits ++ valinits) <> freeIn' form <>-       freeIn' (ctxparams ++ valparams) <> freeIn' loopbody+        bound_here =+          namesFromList $+            M.keys $+              scopeOf form+                <> scopeOfFParams (ctxparams ++ valparams)+     in fvBind bound_here $+          freeIn' (ctxinits ++ valinits) <> freeIn' form+            <> freeIn' (ctxparams ++ valparams)+            <> freeIn' loopbody   freeIn' e = execState (walkExpM freeWalker e) mempty -instance (FreeDec (ExpDec lore),-          FreeDec (BodyDec lore),-          FreeIn (FParamInfo lore),-          FreeIn (LParamInfo lore),-          FreeIn (LetDec lore),-          FreeIn (Op lore)) => FreeIn (Stm lore) where+instance+  ( FreeDec (ExpDec lore),+    FreeDec (BodyDec lore),+    FreeIn (FParamInfo lore),+    FreeIn (LParamInfo lore),+    FreeIn (LetDec lore),+    FreeIn (Op lore)+  ) =>+  FreeIn (Stm lore)+  where   freeIn' (Let pat (StmAux cs attrs dec) e) =-    freeIn' cs <> freeIn' attrs <>-    precomputed dec (freeIn' dec <> freeIn' e <> freeIn' pat)+    freeIn' cs <> freeIn' attrs+      <> precomputed dec (freeIn' dec <> freeIn' e <> freeIn' pat)  instance FreeIn (Stm lore) => FreeIn (Stms lore) where   freeIn' = foldMap freeIn'@@ -256,7 +304,7 @@  instance FreeIn SubExp where   freeIn' (Var v) = freeIn' v-  freeIn' Constant{} = mempty+  freeIn' Constant {} = mempty  instance FreeIn Space where   freeIn' (ScalarSpace d _) = freeIn' d@@ -268,12 +316,12 @@  instance FreeIn d => FreeIn (Ext d) where   freeIn' (Free x) = freeIn' x-  freeIn' (Ext _)  = mempty+  freeIn' (Ext _) = mempty  instance FreeIn shape => FreeIn (TypeBase shape u) where   freeIn' (Array _ shape _) = freeIn' shape-  freeIn' (Mem s)           = freeIn' s-  freeIn' (Prim _)          = mempty+  freeIn' (Mem s) = freeIn' s+  freeIn' (Prim _) = mempty  instance FreeIn dec => FreeIn (Param dec) where   freeIn' (Param _ dec) = freeIn' dec@@ -294,7 +342,8 @@ instance FreeIn dec => FreeIn (PatternT dec) where   freeIn' (Pattern context values) =     fvBind bound_here $ freeIn' $ context ++ values-    where bound_here = namesFromList $ map patElemName $ context ++ values+    where+      bound_here = namesFromList $ map patElemName $ context ++ values  instance FreeIn Certificates where   freeIn' (Certificates cs) = freeIn' cs@@ -315,14 +364,14 @@   precomputed :: dec -> FV -> FV   precomputed _ = id -instance FreeDec () where+instance FreeDec () -instance (FreeDec a, FreeIn b) => FreeDec (a,b) where-  precomputed (a,_) = precomputed a+instance (FreeDec a, FreeIn b) => FreeDec (a, b) where+  precomputed (a, _) = precomputed a  instance FreeDec a => FreeDec [a] where   precomputed [] = id-  precomputed (a:_) = precomputed a+  precomputed (a : _) = precomputed a  instance FreeDec a => FreeDec (Maybe a) where   precomputed Nothing = id
src/Futhark/IR/Prop/Patterns.hs view
@@ -1,34 +1,36 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}+ -- | Inspecing and modifying t'Pattern's, function parameters and -- pattern elements. module Futhark.IR.Prop.Patterns-       (-         -- * Function parameters-         paramIdent-       , paramType-       , paramDeclType-         -- * Pattern elements-       , patElemIdent-       , patElemType-       , setPatElemLore-       , patternElements-       , patternIdents-       , patternContextIdents-       , patternValueIdents-       , patternNames-       , patternValueNames-       , patternContextNames-       , patternTypes-       , patternValueTypes-       , patternSize-       -- * Pattern construction-       , basicPattern-       )-       where+  ( -- * Function parameters+    paramIdent,+    paramType,+    paramDeclType, +    -- * Pattern elements+    patElemIdent,+    patElemType,+    setPatElemLore,+    patternElements,+    patternIdents,+    patternContextIdents,+    patternValueIdents,+    patternNames,+    patternValueNames,+    patternContextNames,+    patternTypes,+    patternValueTypes,+    patternSize,++    -- * Pattern construction+    basicPattern,+  )+where++import Futhark.IR.Prop.Types (DeclTyped (..), Typed (..)) import Futhark.IR.Syntax-import Futhark.IR.Prop.Types (Typed(..), DeclTyped(..))  -- | The 'Type' of a parameter. paramType :: Typed dec => Param dec -> Type@@ -98,4 +100,5 @@ basicPattern :: [Ident] -> [Ident] -> PatternT Type basicPattern context values =   Pattern (map patElem context) (map patElem values)-  where patElem (Ident name t) = PatElem name t+  where+    patElem (Ident name t) = PatElem name t
src/Futhark/IR/Prop/Rearrange.hs view
@@ -1,40 +1,42 @@ -- | A rearrangement is a generalisation of transposition, where the -- dimensions are arbitrarily permuted. module Futhark.IR.Prop.Rearrange-       ( rearrangeShape-       , rearrangeInverse-       , rearrangeReach-       , rearrangeCompose-       , isPermutationOf-       , transposeIndex-       , isMapTranspose-       ) where+  ( rearrangeShape,+    rearrangeInverse,+    rearrangeReach,+    rearrangeCompose,+    isPermutationOf,+    transposeIndex,+    isMapTranspose,+  )+where  import Data.List (sortOn, tails)- import Futhark.Util  -- | Calculate the given permutation of the list.  It is an error if -- the permutation goes out of bounds. rearrangeShape :: [Int] -> [a] -> [a] rearrangeShape perm l = map pick perm-  where pick i-          | 0 <= i, i < n = l!!i-          | otherwise =-              error $ show perm ++ " is not a valid permutation for input."-        n = length l+  where+    pick i+      | 0 <= i, i < n = l !! i+      | otherwise =+        error $ show perm ++ " is not a valid permutation for input."+    n = length l  -- | Produce the inverse permutation. rearrangeInverse :: [Int] -> [Int]-rearrangeInverse perm = map snd $ sortOn fst $ zip perm [0..]+rearrangeInverse perm = map snd $ sortOn fst $ zip perm [0 ..]  -- | Return the first dimension not affected by the permutation.  For -- example, the permutation @[1,0,2]@ would return @2@. rearrangeReach :: [Int] -> Int-rearrangeReach perm = case dropWhile (uncurry (/=)) $ zip (tails perm) (tails [0..n-1]) of-                      []          -> n + 1-                      (perm',_):_ -> n - length perm'-  where n = length perm+rearrangeReach perm = case dropWhile (uncurry (/=)) $ zip (tails perm) (tails [0 .. n -1]) of+  [] -> n + 1+  (perm', _) : _ -> n - length perm'+  where+    n = length perm  -- | Compose two permutations, with the second given permutation being -- applied first.@@ -49,15 +51,16 @@ isPermutationOf l1 l2 =   case mapAccumLM (pick 0) (map Just l2) l1 of     Just (l2', perm)-      | all (==Nothing) l2' -> Just perm-    _                       -> Nothing-  where pick :: Eq a => Int -> [Maybe a] -> a -> Maybe ([Maybe a], Int)-        pick _ [] _ = Nothing-        pick i (x:xs) y-          | Just y == x = Just (Nothing : xs, i)-          | otherwise = do-              (xs', v) <- pick (i+1) xs y-              return (x : xs', v)+      | all (== Nothing) l2' -> Just perm+    _ -> Nothing+  where+    pick :: Eq a => Int -> [Maybe a] -> a -> Maybe ([Maybe a], Int)+    pick _ [] _ = Nothing+    pick i (x : xs) y+      | Just y == x = Just (Nothing : xs, i)+      | otherwise = do+        (xs', v) <- pick (i + 1) xs y+        return (x : xs', v)  -- | If @l@ is an index into the array @a@, then @transposeIndex k n -- l@ is an index to the same element in the array @transposeArray k n@@ -65,14 +68,14 @@ transposeIndex :: Int -> Int -> [a] -> [a] transposeIndex k n l   | k + n >= length l =-    let n' = ((k + n) `mod` length l)-k-    in transposeIndex k n' l+    let n' = ((k + n) `mod` length l) - k+     in transposeIndex k n' l   | n < 0,-    (pre,needle:end) <- splitAt k l,-    (beg,mid) <- splitAt (length pre+n) pre =+    (pre, needle : end) <- splitAt k l,+    (beg, mid) <- splitAt (length pre + n) pre =     beg ++ [needle] ++ mid ++ end-  | (beg,needle:post) <- splitAt k l,-    (mid,end) <- splitAt n post =+  | (beg, needle : post) <- splitAt k l,+    (mid, end) <- splitAt n post =     beg ++ mid ++ [needle] ++ end   | otherwise = l @@ -87,22 +90,24 @@ -- undefined. isMapTranspose :: [Int] -> Maybe (Int, Int, Int) isMapTranspose perm-  | posttrans == [length mapped..length mapped+length posttrans-1],-    not $ null pretrans, not $ null posttrans =-      Just (length mapped, length pretrans, length posttrans)+  | posttrans == [length mapped .. length mapped + length posttrans -1],+    not $ null pretrans,+    not $ null posttrans =+    Just (length mapped, length pretrans, length posttrans)   | otherwise =-      Nothing-  where (mapped, notmapped) = findIncreasingFrom 0 perm-        (pretrans, posttrans) = findTransposed notmapped+    Nothing+  where+    (mapped, notmapped) = findIncreasingFrom 0 perm+    (pretrans, posttrans) = findTransposed notmapped -        findIncreasingFrom x (i:is)-          | i == x =-            let (js, ps) = findIncreasingFrom (x+1) is-            in (i : js, ps)-        findIncreasingFrom _ is =-          ([], is)+    findIncreasingFrom x (i : is)+      | i == x =+        let (js, ps) = findIncreasingFrom (x + 1) is+         in (i : js, ps)+    findIncreasingFrom _ is =+      ([], is) -        findTransposed [] =-          ([], [])-        findTransposed (i:is) =-          findIncreasingFrom i (i:is)+    findTransposed [] =+      ([], [])+    findTransposed (i : is) =+      findIncreasingFrom i (i : is)
src/Futhark/IR/Prop/Reshape.hs view
@@ -1,45 +1,42 @@ -- | Facilities for creating, inspecting, and simplifying reshape and -- coercion operations. module Futhark.IR.Prop.Reshape-       (-         -- * Basic tools-         newDim-       , newDims-       , newShape+  ( -- * Basic tools+    newDim,+    newDims,+    newShape, -         -- * Construction-       , shapeCoerce+    -- * Construction+    shapeCoerce, -         -- * Execution-       , reshapeOuter-       , reshapeInner+    -- * Execution+    reshapeOuter,+    reshapeInner, -         -- * Inspection-       , shapeCoercion+    -- * Inspection+    shapeCoercion, -         -- * Simplification-       , fuseReshape-       , informReshape+    -- * Simplification+    fuseReshape,+    informReshape, -         -- * Shape calculations-       , reshapeIndex-       , flattenIndex-       , unflattenIndex-       , sliceSizes-       )-       where+    -- * Shape calculations+    reshapeIndex,+    flattenIndex,+    unflattenIndex,+    sliceSizes,+  )+where  import Data.Foldable--import Prelude hiding (sum, product, quot)- import Futhark.IR.Syntax import Futhark.Util.IntegralExp+import Prelude hiding (product, quot, sum)  -- | The new dimension. newDim :: DimChange d -> d newDim (DimCoercion se) = se-newDim (DimNew      se) = se+newDim (DimNew se) = se  -- | The new dimensions resulting from a reshape operation. newDims :: ShapeChange d -> [d]@@ -60,9 +57,10 @@ reshapeOuter :: ShapeChange SubExp -> Int -> Shape -> ShapeChange SubExp reshapeOuter newshape n oldshape =   newshape ++ map coercion_or_new (drop n (shapeDims oldshape))-  where coercion_or_new-          | length newshape == n = DimCoercion-          | otherwise            = DimNew+  where+    coercion_or_new+      | length newshape == n = DimCoercion+      | otherwise = DimNew  -- | @reshapeInner newshape n oldshape@ returns a 'Reshape' expression -- that replaces the inner @m-n@ dimensions (where @m@ is the rank of@@ -70,17 +68,19 @@ reshapeInner :: ShapeChange SubExp -> Int -> Shape -> ShapeChange SubExp reshapeInner newshape n oldshape =   map coercion_or_new (take n (shapeDims oldshape)) ++ newshape-  where coercion_or_new-          | length newshape == m-n = DimCoercion-          | otherwise              = DimNew-        m = shapeRank oldshape+  where+    coercion_or_new+      | length newshape == m - n = DimCoercion+      | otherwise = DimNew+    m = shapeRank oldshape  -- | If the shape change is nothing but shape coercions, return the new dimensions.  Otherwise, return -- 'Nothing'. shapeCoercion :: ShapeChange d -> Maybe [d] shapeCoercion = mapM dimCoercion-  where dimCoercion (DimCoercion d) = Just d-        dimCoercion (DimNew      _) = Nothing+  where+    dimCoercion (DimCoercion d) = Just d+    dimCoercion (DimNew _) = Nothing  -- | @fuseReshape s1 s2@ creates a new 'ShapeChange' that is -- semantically the same as first applying @s1@ and then @s2@.  This@@ -89,14 +89,15 @@ fuseReshape :: Eq d => ShapeChange d -> ShapeChange d -> ShapeChange d fuseReshape s1 s2   | length s1 == length s2 =-      zipWith comb s1 s2-  where comb (DimNew _)       (DimCoercion d2) =-          DimNew d2-        comb (DimCoercion d1) (DimNew d2)-          | d1 == d2  = DimCoercion d2-          | otherwise = DimNew d2-        comb _                d2 =-          d2+    zipWith comb s1 s2+  where+    comb (DimNew _) (DimCoercion d2) =+      DimNew d2+    comb (DimCoercion d1) (DimNew d2)+      | d1 == d2 = DimCoercion d2+      | otherwise = DimNew d2+    comb _ d2 =+      d2 -- TODO: intelligently handle case where s1 is a prefix of s2. fuseReshape _ s2 = s2 @@ -106,10 +107,11 @@ informReshape shape sc   | length shape == length sc =     zipWith inform shape sc-  where inform d1 (DimNew d2)-          | d1 == d2  = DimCoercion d2-        inform _ dc =-          dc+  where+    inform d1 (DimNew d2)+      | d1 == d2 = DimCoercion d2+    inform _ dc =+      dc informReshape _ sc = sc  -- | @reshapeIndex to_dims from_dims is@ transforms the index list@@ -117,20 +119,30 @@ -- list @is'@, which is into an array of shape @to_dims@.  @is@ must -- have the same length as @from_dims@, and @is'@ will have the same -- length as @to_dims@.-reshapeIndex :: IntegralExp num =>-                [num] -> [num] -> [num] -> [num]+reshapeIndex ::+  IntegralExp num =>+  [num] ->+  [num] ->+  [num] ->+  [num] reshapeIndex to_dims from_dims is =   unflattenIndex to_dims $ flattenIndex from_dims is  -- | @unflattenIndex dims i@ computes a list of indices into an array -- with dimension @dims@ given the flat index @i@.  The resulting list -- will have the same size as @dims@.-unflattenIndex :: IntegralExp num =>-                  [num] -> num -> [num]+unflattenIndex ::+  IntegralExp num =>+  [num] ->+  num ->+  [num] unflattenIndex = unflattenIndexFromSlices . drop 1 . sliceSizes -unflattenIndexFromSlices :: IntegralExp num =>-                            [num] -> num -> [num]+unflattenIndexFromSlices ::+  IntegralExp num =>+  [num] ->+  num ->+  [num] unflattenIndexFromSlices [] _ = [] unflattenIndexFromSlices (size : slices) i =   (i `quot` size) : unflattenIndexFromSlices slices (i - (i `quot` size) * size)@@ -138,20 +150,26 @@ -- | @flattenIndex dims is@ computes the flat index of @is@ into an -- array with dimensions @dims@.  The length of @dims@ and @is@ must -- be the same.-flattenIndex :: IntegralExp num =>-                [num] -> [num] -> num+flattenIndex ::+  IntegralExp num =>+  [num] ->+  [num] ->+  num flattenIndex dims is =   sum $ zipWith (*) is slicesizes-  where slicesizes = drop 1 $ sliceSizes dims+  where+    slicesizes = drop 1 $ sliceSizes dims  -- | Given a length @n@ list of dimensions @dims@, @sizeSizes dims@ -- will compute a length @n+1@ list of the size of each possible array -- slice.  The first element of this list will be the product of -- @dims@, and the last element will be 1.-sliceSizes :: IntegralExp num =>-              [num] -> [num]+sliceSizes ::+  IntegralExp num =>+  [num] ->+  [num] sliceSizes [] = [1]-sliceSizes (n:ns) =+sliceSizes (n : ns) =   product (n : ns) : sliceSizes ns  {- HLINT ignore sliceSizes -}
src/Futhark/IR/Prop/Scope.hs view
@@ -1,12 +1,13 @@-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+ -- | The core Futhark AST does not contain type information when we -- use a variable.  Therefore, most transformations expect to be able -- to access some kind of symbol table that maps names to their types.@@ -16,42 +17,42 @@ -- also provided to communicate that some monad or applicative functor -- maintains type information. module Futhark.IR.Prop.Scope-       ( HasScope (..)-       , NameInfo (..)-       , LocalScope (..)-       , Scope-       , Scoped(..)-       , inScopeOf-       , scopeOfLParams-       , scopeOfFParams-       , scopeOfPattern-       , scopeOfPatElem--       , SameScope-       , castScope+  ( HasScope (..),+    NameInfo (..),+    LocalScope (..),+    Scope,+    Scoped (..),+    inScopeOf,+    scopeOfLParams,+    scopeOfFParams,+    scopeOfPattern,+    scopeOfPatElem,+    SameScope,+    castScope, -         -- * Extended type environment-       , ExtendedScope-       , extendedScope-       ) where+    -- * Extended type environment+    ExtendedScope,+    extendedScope,+  )+where  import Control.Monad.Except-import Control.Monad.Reader-import qualified Control.Monad.RWS.Strict import qualified Control.Monad.RWS.Lazy+import qualified Control.Monad.RWS.Strict+import Control.Monad.Reader import qualified Data.Map.Strict as M- import Futhark.IR.Decorations-import Futhark.IR.Syntax-import Futhark.IR.Prop.Types-import Futhark.IR.Prop.Patterns import Futhark.IR.Pretty ()+import Futhark.IR.Prop.Patterns+import Futhark.IR.Prop.Types+import Futhark.IR.Syntax  -- | How some name in scope was bound.-data NameInfo lore = LetName (LetDec lore)-                   | FParamName (FParamInfo lore)-                   | LParamName (LParamInfo lore)-                   | IndexName IntType+data NameInfo lore+  = LetName (LetDec lore)+  | FParamName (FParamInfo lore)+  | LParamName (LParamInfo lore)+  | IndexName IntType  deriving instance Decorations lore => Show (NameInfo lore) @@ -81,9 +82,11 @@   lookupInfo :: VName -> m (NameInfo lore)   lookupInfo name =     asksScope (M.findWithDefault notFound name)-    where notFound =-            error $ "Scope.lookupInfo: Name " ++ pretty name ++-            " not found in type environment."+    where+      notFound =+        error $+          "Scope.lookupInfo: Name " ++ pretty name+            ++ " not found in type environment."    -- | Return the type environment contained in the applicative   -- functor.@@ -94,19 +97,25 @@   asksScope :: (Scope lore -> a) -> m a   asksScope f = f <$> askScope -instance (Applicative m, Monad m, Decorations lore) =>-         HasScope lore (ReaderT (Scope lore) m) where+instance+  (Applicative m, Monad m, Decorations lore) =>+  HasScope lore (ReaderT (Scope lore) m)+  where   askScope = ask  instance (Monad m, HasScope lore m) => HasScope lore (ExceptT e m) where   askScope = lift askScope -instance (Applicative m, Monad m, Monoid w, Decorations lore) =>-         HasScope lore (Control.Monad.RWS.Strict.RWST (Scope lore) w s m) where+instance+  (Applicative m, Monad m, Monoid w, Decorations lore) =>+  HasScope lore (Control.Monad.RWS.Strict.RWST (Scope lore) w s m)+  where   askScope = ask -instance (Applicative m, Monad m, Monoid w, Decorations lore) =>-         HasScope lore (Control.Monad.RWS.Lazy.RWST (Scope lore) w s m) where+instance+  (Applicative m, Monad m, Monoid w, Decorations lore) =>+  HasScope lore (Control.Monad.RWS.Lazy.RWST (Scope lore) w s m)+  where   askScope = ask  -- | The class of monads that not only provide a 'Scope', but also@@ -121,16 +130,22 @@ instance (Monad m, LocalScope lore m) => LocalScope lore (ExceptT e m) where   localScope = mapExceptT . localScope -instance (Applicative m, Monad m, Decorations lore) =>-         LocalScope lore (ReaderT (Scope lore) m) where+instance+  (Applicative m, Monad m, Decorations lore) =>+  LocalScope lore (ReaderT (Scope lore) m)+  where   localScope = local . M.union -instance (Applicative m, Monad m, Monoid w, Decorations lore) =>-         LocalScope lore (Control.Monad.RWS.Strict.RWST (Scope lore) w s m) where+instance+  (Applicative m, Monad m, Monoid w, Decorations lore) =>+  LocalScope lore (Control.Monad.RWS.Strict.RWST (Scope lore) w s m)+  where   localScope = local . M.union -instance (Applicative m, Monad m, Monoid w, Decorations lore) =>-         LocalScope lore (Control.Monad.RWS.Lazy.RWST (Scope lore) w s m) where+instance+  (Applicative m, Monad m, Monoid w, Decorations lore) =>+  LocalScope lore (Control.Monad.RWS.Lazy.RWST (Scope lore) w s m)+  where   localScope = local . M.union  -- | The class of things that can provide a scope.  There is no@@ -173,34 +188,46 @@ scopeOfPatElem (PatElem name dec) = M.singleton name $ LetName dec  -- | The scope of some lambda parameters.-scopeOfLParams :: LParamInfo lore ~ dec =>-                  [Param dec] -> Scope lore+scopeOfLParams ::+  LParamInfo lore ~ dec =>+  [Param dec] ->+  Scope lore scopeOfLParams = M.fromList . map f-  where f param = (paramName param, LParamName $ paramDec param)+  where+    f param = (paramName param, LParamName $ paramDec param)  -- | The scope of some function or loop parameters.-scopeOfFParams :: FParamInfo lore ~ dec =>-                  [Param dec] -> Scope lore+scopeOfFParams ::+  FParamInfo lore ~ dec =>+  [Param dec] ->+  Scope lore scopeOfFParams = M.fromList . map f-  where f param = (paramName param, FParamName $ paramDec param)+  where+    f param = (paramName param, FParamName $ paramDec param)  instance Scoped lore (Lambda lore) where   scopeOf lam = scopeOfLParams $ lambdaParams lam  -- | A constraint that indicates two lores have the same 'NameInfo' -- representation.-type SameScope lore1 lore2 = (LetDec lore1 ~ LetDec lore2,-                              FParamInfo lore1 ~ FParamInfo lore2,-                              LParamInfo lore1 ~ LParamInfo lore2)+type SameScope lore1 lore2 =+  ( LetDec lore1 ~ LetDec lore2,+    FParamInfo lore1 ~ FParamInfo lore2,+    LParamInfo lore1 ~ LParamInfo lore2+  )  -- | If two scopes are really the same, then you can convert one to -- the other.-castScope :: SameScope fromlore tolore =>-             Scope fromlore -> Scope tolore+castScope ::+  SameScope fromlore tolore =>+  Scope fromlore ->+  Scope tolore castScope = M.map castNameInfo -castNameInfo :: SameScope fromlore tolore =>-                NameInfo fromlore -> NameInfo tolore+castNameInfo ::+  SameScope fromlore tolore =>+  NameInfo fromlore ->+  NameInfo tolore castNameInfo (LetName dec) = LetName dec castNameInfo (FParamName dec) = FParamName dec castNameInfo (LParamName dec) = LParamName dec@@ -210,18 +237,25 @@ -- Its 'lookupType' method will first look in the extended 'Scope', -- and then use the 'lookupType' method of the underlying monad. newtype ExtendedScope lore m a = ExtendedScope (ReaderT (Scope lore) m a)-                            deriving (Functor, Applicative, Monad,-                                      MonadReader (Scope lore))+  deriving+    ( Functor,+      Applicative,+      Monad,+      MonadReader (Scope lore)+    ) -instance (HasScope lore m, Monad m) =>-         HasScope lore (ExtendedScope lore m) where+instance+  (HasScope lore m, Monad m) =>+  HasScope lore (ExtendedScope lore m)+  where   lookupType name = do     res <- asks $ fmap typeOf . M.lookup name     maybe (ExtendedScope $ lift $ lookupType name) return res   askScope = asks M.union <*> ExtendedScope (lift askScope)  -- | Run a computation in the extended type environment.-extendedScope :: ExtendedScope lore m a-              -> Scope lore-              -> m a+extendedScope ::+  ExtendedScope lore m a ->+  Scope lore ->+  m a extendedScope (ExtendedScope m) = runReaderT m
src/Futhark/IR/Prop/TypeOf.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-}+ -- | This module provides facilities for obtaining the types of -- various Futhark constructs.  Typically, you will need to execute -- these in a context where type information is available as a@@ -16,44 +17,45 @@ -- "Futhark.IR.Mem" exposes functionality for -- also obtaining information about the storage location of results. module Futhark.IR.Prop.TypeOf-       (-         expExtType-       , expExtTypeSize-       , subExpType-       , primOpType-       , mapType+  ( expExtType,+    expExtTypeSize,+    subExpType,+    primOpType,+    mapType, -       -- * Return type-       , module Futhark.IR.RetType-       -- * Type environment-       , module Futhark.IR.Prop.Scope+    -- * Return type+    module Futhark.IR.RetType, -         -- * Extensibility-       , TypedOp(..)-       )-       where+    -- * Type environment+    module Futhark.IR.Prop.Scope, -import Data.Maybe+    -- * Extensibility+    TypedOp (..),+  )+where -import Futhark.IR.Syntax+import Data.Maybe+import Futhark.IR.Prop.Constants+import Futhark.IR.Prop.Patterns import Futhark.IR.Prop.Reshape+import Futhark.IR.Prop.Scope import Futhark.IR.Prop.Types-import Futhark.IR.Prop.Patterns-import Futhark.IR.Prop.Constants import Futhark.IR.RetType-import Futhark.IR.Prop.Scope+import Futhark.IR.Syntax  -- | The type of a subexpression. subExpType :: HasScope t m => SubExp -> m Type subExpType (Constant val) = pure $ Prim $ primValueType val-subExpType (Var name)     = lookupType name+subExpType (Var name) = lookupType name  -- | @mapType f arrts@ wraps each element in the return type of @f@ in -- an array with size equal to the outermost dimension of the first -- element of @arrts@. mapType :: SubExp -> Lambda lore -> [Type]-mapType outersize f = [ arrayOf t (Shape [outersize]) NoUniqueness-                      | t <- lambdaReturnType f ]+mapType outersize f =+  [ arrayOf t (Shape [outersize]) NoUniqueness+    | t <- lambdaReturnType f+  ]  -- | The type of a primitive operation. primOpType :: HasScope lore m => BasicOp -> m [Type]@@ -63,21 +65,23 @@   pure <$> subExpType se primOpType (ArrayLit es rt) =   pure [arrayOf rt (Shape [n]) NoUniqueness]-  where n = Constant (value (length es))+  where+    n = intConst Int64 $ toInteger $ length es primOpType (BinOp bop _ _) =   pure [Prim $ binOpType bop] primOpType (UnOp _ x) =   pure <$> subExpType x-primOpType CmpOp{} =+primOpType CmpOp {} =   pure [Prim Bool] primOpType (ConvOp conv _) =   pure [Prim $ snd $ convOpType conv] primOpType (Index ident slice) =   result <$> lookupType ident-  where result t = [Prim (elemType t) `arrayOfShape` shape]-        shape = Shape $ mapMaybe dimSize slice-        dimSize (DimSlice _ d _) = Just d-        dimSize DimFix{}         = Nothing+  where+    result t = [Prim (elemType t) `arrayOfShape` shape]+    shape = Shape $ mapMaybe dimSize slice+    dimSize (DimSlice _ d _) = Just d+    dimSize DimFix {} = Nothing primOpType (Update src _ _) =   pure <$> lookupType src primOpType (Iota n _ _ et) =@@ -90,42 +94,50 @@   pure [arrayOf (Prim t) (Shape shape) NoUniqueness] primOpType (Reshape [] e) =   result <$> lookupType e-  where result t = [Prim $ elemType t]+  where+    result t = [Prim $ elemType t] primOpType (Reshape shape e) =   result <$> lookupType e-  where result t = [t `setArrayShape` newShape shape]+  where+    result t = [t `setArrayShape` newShape shape] primOpType (Rearrange perm e) =   result <$> lookupType e-  where result t = [rearrangeType perm t]+  where+    result t = [rearrangeType perm t] primOpType (Rotate _ e) =   pure <$> lookupType e primOpType (Concat i x _ ressize) =   result <$> lookupType x-  where result xt = [setDimSize i xt ressize]+  where+    result xt = [setDimSize i xt ressize] primOpType (Copy v) =   pure <$> lookupType v primOpType (Manifest _ v) =   pure <$> lookupType v-primOpType Assert{} =+primOpType Assert {} =   pure [Prim Cert]  -- | The type of an expression.-expExtType :: (HasScope lore m, TypedOp (Op lore)) =>-              Exp lore -> m [ExtType]+expExtType ::+  (HasScope lore m, TypedOp (Op lore)) =>+  Exp lore ->+  m [ExtType] expExtType (Apply _ _ rt _) = pure $ map (fromDecl . declExtTypeOf) rt-expExtType (If _ _ _ rt)  = pure $ map extTypeOf $ ifReturns rt+expExtType (If _ _ _ rt) = pure $ map extTypeOf $ ifReturns rt expExtType (DoLoop ctxmerge valmerge _ _) =   pure $ loopExtType (map (paramIdent . fst) ctxmerge) (map (paramIdent . fst) valmerge)-expExtType (BasicOp op)    = staticShapes <$> primOpType op-expExtType (Op op)        = opType op+expExtType (BasicOp op) = staticShapes <$> primOpType op+expExtType (Op op) = opType op  -- | The number of values returned by an expression.-expExtTypeSize :: (Decorations lore, TypedOp (Op lore)) =>-                  Exp lore -> Int+expExtTypeSize ::+  (Decorations lore, TypedOp (Op lore)) =>+  Exp lore ->+  Int expExtTypeSize = length . feelBad . expExtType  -- FIXME, this is a horrible quick hack.-newtype FeelBad lore a = FeelBad { feelBad :: a }+newtype FeelBad lore a = FeelBad {feelBad :: a}  instance Functor (FeelBad lore) where   fmap f = FeelBad . f . feelBad@@ -135,7 +147,7 @@   f <*> x = FeelBad $ feelBad f $ feelBad x  instance Decorations lore => HasScope lore (FeelBad lore) where-  lookupType = const $ pure $ Prim $ IntType Int32+  lookupType = const $ pure $ Prim $ IntType Int64   askScope = pure mempty  -- | Given the context and value merge parameters of a Futhark @loop@,@@ -143,7 +155,8 @@ loopExtType :: [Ident] -> [Ident] -> [ExtType] loopExtType ctx val =   existentialiseExtTypes inaccessible $ staticShapes $ map identType val-  where inaccessible = map identName ctx+  where+    inaccessible = map identName ctx  -- | Any operation must define an instance of this class, which -- describes the type of the operation (at the value level).
src/Futhark/IR/Prop/Types.hs view
@@ -1,82 +1,79 @@-{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+ -- | Functions for inspecting and constructing various types. module Futhark.IR.Prop.Types-       (-         rankShaped-       , arrayRank-       , arrayShape-       , setArrayShape-       , existential-       , uniqueness-       , unique-       , staticShapes-       , staticShapes1-       , primType--       , arrayOf-       , arrayOfRow-       , arrayOfShape-       , setOuterSize-       , setDimSize-       , setOuterDim-       , setDim-       , setArrayDims-       , peelArray-       , stripArray-       , arrayDims-       , arrayExtDims-       , shapeSize-       , arraySize-       , arraysSize-       , rowType-       , elemType--       , transposeType-       , rearrangeType--       , mapOnExtType-       , mapOnType--       , diet--       , subtypeOf-       , subtypesOf--       , toDecl-       , fromDecl--       , isExt-       , isFree-       , extractShapeContext-       , shapeContext-       , hasStaticShape-       , generaliseExtTypes-       , existentialiseExtTypes-       , shapeExtMapping+  ( rankShaped,+    arrayRank,+    arrayShape,+    setArrayShape,+    existential,+    uniqueness,+    unique,+    staticShapes,+    staticShapes1,+    primType,+    arrayOf,+    arrayOfRow,+    arrayOfShape,+    setOuterSize,+    setDimSize,+    setOuterDim,+    setDim,+    setArrayDims,+    peelArray,+    stripArray,+    arrayDims,+    arrayExtDims,+    shapeSize,+    arraySize,+    arraysSize,+    rowType,+    elemType,+    transposeType,+    rearrangeType,+    mapOnExtType,+    mapOnType,+    diet,+    subtypeOf,+    subtypesOf,+    toDecl,+    fromDecl,+    isExt,+    isFree,+    extractShapeContext,+    shapeContext,+    hasStaticShape,+    generaliseExtTypes,+    existentialiseExtTypes,+    shapeExtMapping, -         -- * Abbreviations-       , int8, int16, int32, int64-       , float32, float64+    -- * Abbreviations+    int8,+    int16,+    int32,+    int64,+    float32,+    float64, -         -- * The Typed typeclass-       , Typed (..)-       , DeclTyped (..)-       , ExtTyped (..)-       , DeclExtTyped (..)-       , SetType (..)-       , FixExt (..)-       )-       where+    -- * The Typed typeclass+    Typed (..),+    DeclTyped (..),+    ExtTyped (..),+    DeclExtTyped (..),+    SetType (..),+    FixExt (..),+  )+where  import Control.Monad.State-import Data.Maybe import Data.List (elemIndex, foldl')-import qualified Data.Set as S import qualified Data.Map.Strict as M--import Futhark.IR.Syntax.Core+import Data.Maybe+import qualified Data.Set as S import Futhark.IR.Prop.Constants import Futhark.IR.Prop.Rearrange+import Futhark.IR.Syntax.Core  -- | Remove shape information from a type. rankShaped :: ArrayShape shape => TypeBase shape u -> TypeBase Rank u@@ -94,33 +91,37 @@ -- 'mempty'. arrayShape :: ArrayShape shape => TypeBase shape u -> shape arrayShape (Array _ ds _) = ds-arrayShape _              = mempty+arrayShape _ = mempty  -- | Modify the shape of an array - for non-arrays, this does nothing.-modifyArrayShape :: ArrayShape newshape =>-                    (oldshape -> newshape)-                 -> TypeBase oldshape u-                 -> TypeBase newshape u+modifyArrayShape ::+  ArrayShape newshape =>+  (oldshape -> newshape) ->+  TypeBase oldshape u ->+  TypeBase newshape u modifyArrayShape f (Array t ds u)   | shapeRank ds' == 0 = Prim t-  | otherwise          = Array t (f ds) u-  where ds' = f ds-modifyArrayShape _ (Prim t)    = Prim t+  | otherwise = Array t (f ds) u+  where+    ds' = f ds+modifyArrayShape _ (Prim t) = Prim t modifyArrayShape _ (Mem space) = Mem space  -- | Set the shape of an array.  If the given type is not an -- array, return the type unchanged.-setArrayShape :: ArrayShape newshape =>-                 TypeBase oldshape u-              -> newshape-              -> TypeBase newshape u+setArrayShape ::+  ArrayShape newshape =>+  TypeBase oldshape u ->+  newshape ->+  TypeBase newshape u setArrayShape t ds = modifyArrayShape (const ds) t  -- | True if the given type has a dimension that is existentially sized. existential :: ExtType -> Bool existential = any ext . shapeDims . arrayShape-  where ext (Ext _)  = True-        ext (Free _) = False+  where+    ext (Ext _) = True+    ext (Free _) = False  -- | Return the uniqueness of a type. uniqueness :: TypeBase shape Uniqueness -> Uniqueness@@ -129,7 +130,7 @@  -- | @unique t@ is 'True' if the type of the argument is unique. unique :: TypeBase shape Uniqueness -> Bool-unique = (==Unique) . uniqueness+unique = (== Unique) . uniqueness  -- | Convert types with non-existential shapes to types with -- non-existential shapes.  Only the representation is changed, so all@@ -154,24 +155,29 @@ -- uniqueness of @t@.  If the shape @s@ has rank 0, then the @t@ will -- be returned, although if it is an array, with the uniqueness -- changed to @u@.-arrayOf :: ArrayShape shape =>-           TypeBase shape u_unused -> shape -> u -> TypeBase shape u+arrayOf ::+  ArrayShape shape =>+  TypeBase shape u_unused ->+  shape ->+  u ->+  TypeBase shape u arrayOf (Array et size1 _) size2 u =   Array et (size2 <> size1) u arrayOf (Prim et) s _   | 0 <- shapeRank s = Prim et arrayOf (Prim et) size u =   Array et size u-arrayOf Mem{} _ _ =+arrayOf Mem {} _ _ =   error "arrayOf Mem"  -- | Construct an array whose rows are the given type, and the outer -- size is the given dimension.  This is just a convenient wrapper -- around 'arrayOf'.-arrayOfRow :: ArrayShape (ShapeBase d) =>-              TypeBase (ShapeBase d) NoUniqueness-           -> d-           -> TypeBase (ShapeBase d) NoUniqueness+arrayOfRow ::+  ArrayShape (ShapeBase d) =>+  TypeBase (ShapeBase d) NoUniqueness ->+  d ->+  TypeBase (ShapeBase d) NoUniqueness arrayOfRow t size = arrayOf t (Shape [size]) NoUniqueness  -- | Construct an array whose rows are the given type, and the outer@@ -187,14 +193,21 @@  -- | Replace the size of the outermost dimension of an array.  If the -- given type is not an array, it is returned unchanged.-setOuterSize :: ArrayShape (ShapeBase d) =>-                TypeBase (ShapeBase d) u -> d -> TypeBase (ShapeBase d) u+setOuterSize ::+  ArrayShape (ShapeBase d) =>+  TypeBase (ShapeBase d) u ->+  d ->+  TypeBase (ShapeBase d) u setOuterSize = setDimSize 0  -- | Replace the size of the given dimension of an array.  If the -- given type is not an array, it is returned unchanged.-setDimSize :: ArrayShape (ShapeBase d) =>-              Int -> TypeBase (ShapeBase d) u -> d -> TypeBase (ShapeBase d) u+setDimSize ::+  ArrayShape (ShapeBase d) =>+  Int ->+  TypeBase (ShapeBase d) u ->+  d ->+  TypeBase (ShapeBase d) u setDimSize i t e = t `setArrayShape` setDim i (arrayShape t) e  -- | Replace the outermost dimension of an array shape.@@ -203,17 +216,20 @@  -- | Replace the specified dimension of an array shape. setDim :: Int -> ShapeBase d -> d -> ShapeBase d-setDim i (Shape ds) e = Shape $ take i ds ++ e : drop (i+1) ds+setDim i (Shape ds) e = Shape $ take i ds ++ e : drop (i + 1) ds  -- | @peelArray n t@ returns the type resulting from peeling the first -- @n@ array dimensions from @t@.  Returns @Nothing@ if @t@ has less -- than @n@ dimensions.-peelArray :: ArrayShape shape =>-             Int -> TypeBase shape u -> Maybe (TypeBase shape u)+peelArray ::+  ArrayShape shape =>+  Int ->+  TypeBase shape u ->+  Maybe (TypeBase shape u) peelArray 0 t = Just t peelArray n (Array et shape u)   | shapeRank shape == n = Just $ Prim et-  | shapeRank shape >  n = Just $ Array et (stripDims n shape) u+  | shapeRank shape > n = Just $ Array et (stripDims n shape) u peelArray _ _ = Nothing  -- | @stripArray n t@ removes the @n@ outermost layers of the array.@@ -222,7 +238,7 @@ stripArray :: ArrayShape shape => Int -> TypeBase shape u -> TypeBase shape u stripArray n (Array et shape u)   | n < shapeRank shape = Array et (stripDims n shape) u-  | otherwise           = Prim et+  | otherwise = Prim et stripArray _ t = t  -- | Return the size of the given dimension.  If the dimension does@@ -230,7 +246,7 @@ shapeSize :: Int -> Shape -> SubExp shapeSize i shape = case drop i $ shapeDims shape of   e : _ -> e-  []    -> constant (0 :: Int32)+  [] -> constant (0 :: Int64)  -- | Return the dimensions of a type - for non-arrays, this is the -- empty list.@@ -251,8 +267,8 @@ -- the given type list.  If the dimension does not exist, or no types -- are given, the zero constant is returned. arraysSize :: Int -> [TypeBase Shape u] -> SubExp-arraysSize _ []    = constant (0 :: Int32)-arraysSize i (t:_) = arraySize i t+arraysSize _ [] = constant (0 :: Int64)+arraysSize i (t : _) = arraySize i t  -- | Return the immediate row-type of an array.  For @[[int]]@, this -- would be @[int]@.@@ -261,20 +277,20 @@  -- | A type is a primitive type if it is not an array or memory block. primType :: TypeBase shape u -> Bool-primType Array{} = False-primType Mem{} = False+primType Array {} = False+primType Mem {} = False primType _ = True  -- | Returns the bottommost type of an array.  For @[[int]]@, this -- would be @int@.  If the given type is not an array, it is returned. elemType :: TypeBase shape u -> PrimType elemType (Array t _ _) = t-elemType (Prim t)     = t-elemType Mem{}      = error "elemType Mem"+elemType (Prim t) = t+elemType Mem {} = error "elemType Mem"  -- | Swap the two outer dimensions of the type. transposeType :: Type -> Type-transposeType = rearrangeType [1,0]+transposeType = rearrangeType [1, 0]  -- | Rearrange the dimensions of the type.  If the length of the -- permutation does not match the rank of the type, the permutation@@ -282,13 +298,15 @@ rearrangeType :: [Int] -> Type -> Type rearrangeType perm t =   t `setArrayShape` Shape (rearrangeShape perm' $ arrayDims t)-  where perm' = perm ++ [length perm .. arrayRank t - 1]+  where+    perm' = perm ++ [length perm .. arrayRank t - 1]  -- | Transform any t'SubExp's in the type.-mapOnExtType :: Monad m =>-                (SubExp -> m SubExp)-             -> TypeBase ExtShape u-             -> m (TypeBase ExtShape u)+mapOnExtType ::+  Monad m =>+  (SubExp -> m SubExp) ->+  TypeBase ExtShape u ->+  m (TypeBase ExtShape u) mapOnExtType _ (Prim bt) =   return $ Prim bt mapOnExtType _ (Mem space) =@@ -297,10 +315,11 @@   Array t <$> (Shape <$> mapM (traverse f) (shapeDims shape)) <*> pure u  -- | Transform any t'SubExp's in the type.-mapOnType :: Monad m =>-             (SubExp -> m SubExp)-          -> TypeBase Shape u-          -> m (TypeBase Shape u)+mapOnType ::+  Monad m =>+  (SubExp -> m SubExp) ->+  TypeBase Shape u ->+  m (TypeBase Shape u) mapOnType _ (Prim bt) = return $ Prim bt mapOnType _ (Mem space) = pure $ Mem space mapOnType f (Array t shape u) =@@ -312,18 +331,19 @@ diet (Prim _) = ObservePrim diet (Array _ _ Unique) = Consume diet (Array _ _ Nonunique) = Observe-diet Mem{} = Observe+diet Mem {} = Observe  -- | @x \`subtypeOf\` y@ is true if @x@ is a subtype of @y@ (or equal to -- @y@), meaning @x@ is valid whenever @y@ is.-subtypeOf :: (Ord u, ArrayShape shape) =>-             TypeBase shape u-          -> TypeBase shape u-          -> Bool+subtypeOf ::+  (Ord u, ArrayShape shape) =>+  TypeBase shape u ->+  TypeBase shape u ->+  Bool subtypeOf (Array t1 shape1 u1) (Array t2 shape2 u2) =-  u2 <= u1 &&-  t1 == t2 &&-  shape1 `subShapeOf` shape2+  u2 <= u1+    && t1 == t2+    && shape1 `subShapeOf` shape2 subtypeOf (Prim t1) (Prim t2) = t1 == t2 subtypeOf (Mem space1) (Mem space2) = space1 == space2 subtypeOf _ _ = False@@ -331,24 +351,28 @@ -- | @xs \`subtypesOf\` ys@ is true if @xs@ is the same size as @ys@, -- and each element in @xs@ is a subtype of the corresponding element -- in @ys@..-subtypesOf :: (Ord u, ArrayShape shape) =>-              [TypeBase shape u]-           -> [TypeBase shape u]-           -> Bool-subtypesOf xs ys = length xs == length ys &&-                   and (zipWith subtypeOf xs ys)+subtypesOf ::+  (Ord u, ArrayShape shape) =>+  [TypeBase shape u] ->+  [TypeBase shape u] ->+  Bool+subtypesOf xs ys =+  length xs == length ys+    && and (zipWith subtypeOf xs ys)  -- | Add the given uniqueness information to the types.-toDecl :: TypeBase shape NoUniqueness-       -> Uniqueness-       -> TypeBase shape Uniqueness+toDecl ::+  TypeBase shape NoUniqueness ->+  Uniqueness ->+  TypeBase shape Uniqueness toDecl (Prim bt) _ = Prim bt toDecl (Array et shape _) u = Array et shape u toDecl (Mem space) _ = Mem space  -- | Remove uniqueness information from the type.-fromDecl :: TypeBase shape Uniqueness-         -> TypeBase shape NoUniqueness+fromDecl ::+  TypeBase shape Uniqueness ->+  TypeBase shape NoUniqueness fromDecl (Prim bt) = Prim bt fromDecl (Array et shape _) = Array et shape NoUniqueness fromDecl (Mem space) = Mem space@@ -370,22 +394,27 @@ extractShapeContext :: [TypeBase ExtShape u] -> [[a]] -> [a] extractShapeContext ts shapes =   evalState (concat <$> zipWithM extract ts shapes) S.empty-  where extract t shape =-          catMaybes <$> zipWithM extract' (shapeDims $ arrayShape t) shape-        extract' (Ext x) v = do-          seen <- gets $ S.member x-          if seen then return Nothing-            else do modify $ S.insert x-                    return $ Just v-        extract' (Free _) _ = return Nothing+  where+    extract t shape =+      catMaybes <$> zipWithM extract' (shapeDims $ arrayShape t) shape+    extract' (Ext x) v = do+      seen <- gets $ S.member x+      if seen+        then return Nothing+        else do+          modify $ S.insert x+          return $ Just v+    extract' (Free _) _ = return Nothing  -- | The set of identifiers used for the shape context in the given -- 'ExtType's. shapeContext :: [TypeBase ExtShape u] -> S.Set Int-shapeContext = S.fromList-               . concatMap (mapMaybe ext . shapeDims . arrayShape)-  where ext (Ext x)  = Just x-        ext (Free _) = Nothing+shapeContext =+  S.fromList+    . concatMap (mapMaybe ext . shapeDims . arrayShape)+  where+    ext (Ext x) = Just x+    ext (Free _) = Nothing  -- | If all dimensions of the given 'ExtShape' are statically known, -- change to the corresponding 'Shape'.@@ -397,29 +426,37 @@  -- | Given two lists of 'ExtType's of the same length, return a list -- of 'ExtType's that is a subtype of the two operands.-generaliseExtTypes :: [TypeBase ExtShape u]-                   -> [TypeBase ExtShape u]-                   -> [TypeBase ExtShape u]+generaliseExtTypes ::+  [TypeBase ExtShape u] ->+  [TypeBase ExtShape u] ->+  [TypeBase ExtShape u] generaliseExtTypes rt1 rt2 =   evalState (zipWithM unifyExtShapes rt1 rt2) (0, M.empty)-  where unifyExtShapes t1 t2 =-          setArrayShape t1 . Shape <$>-          zipWithM unifyExtDims+  where+    unifyExtShapes t1 t2 =+      setArrayShape t1 . Shape+        <$> zipWithM+          unifyExtDims           (shapeDims $ arrayShape t1)           (shapeDims $ arrayShape t2)-        unifyExtDims (Free se1) (Free se2)-          | se1 == se2 = return $ Free se1 -- Arbitrary-          | otherwise  = do (n,m) <- get-                            put (n + 1, m)-                            return $ Ext n-        unifyExtDims (Ext x) (Ext y)-          | x == y = Ext <$> (maybe (new x) return =<<-                              gets (M.lookup x . snd))-        unifyExtDims (Ext x) _ = Ext <$> new x-        unifyExtDims _ (Ext x) = Ext <$> new x-        new x = do (n,m) <- get-                   put (n + 1, M.insert x n m)-                   return n+    unifyExtDims (Free se1) (Free se2)+      | se1 == se2 = return $ Free se1 -- Arbitrary+      | otherwise = do+        (n, m) <- get+        put (n + 1, m)+        return $ Ext n+    unifyExtDims (Ext x) (Ext y)+      | x == y =+        Ext+          <$> ( maybe (new x) return+                  =<< gets (M.lookup x . snd)+              )+    unifyExtDims (Ext x) _ = Ext <$> new x+    unifyExtDims _ (Ext x) = Ext <$> new x+    new x = do+      (n, m) <- get+      put (n + 1, M.insert x n m)+      return n  -- | Given a list of 'ExtType's and a list of "forbidden" names, -- modify the dimensions of the 'ExtType's such that they are 'Ext'@@ -427,24 +464,30 @@ -- forbidden names. existentialiseExtTypes :: [VName] -> [ExtType] -> [ExtType] existentialiseExtTypes inaccessible = map makeBoundShapesFree-  where makeBoundShapesFree =-          modifyArrayShape $ fmap checkDim-        checkDim (Free (Var v))-          | Just i <- v `elemIndex` inaccessible =-              Ext i-        checkDim d = d+  where+    makeBoundShapesFree =+      modifyArrayShape $ fmap checkDim+    checkDim (Free (Var v))+      | Just i <- v `elemIndex` inaccessible =+        Ext i+    checkDim d = d  -- | Produce a mapping for the dimensions context. shapeExtMapping :: [TypeBase ExtShape u] -> [TypeBase Shape u1] -> M.Map Int SubExp shapeExtMapping = dimMapping arrayExtDims arrayDims match mappend-  where match Free{} _ =  mempty-        match (Ext i) dim = M.singleton i dim+  where+    match Free {} _ = mempty+    match (Ext i) dim = M.singleton i dim -dimMapping :: Monoid res =>-              (t1 -> [dim1]) -> (t2 -> [dim2]) -> (dim1 -> dim2 -> res)-           -> (res -> res -> res)-           -> [t1] -> [t2]-           -> res+dimMapping ::+  Monoid res =>+  (t1 -> [dim1]) ->+  (t2 -> [dim2]) ->+  (dim1 -> dim2 -> res) ->+  (res -> res -> res) ->+  [t1] ->+  [t2] ->+  res dimMapping getDims1 getDims2 f comb ts1 ts2 =   foldl' comb mempty $ concat $ zipWith (zipWith f) (map getDims1 ts1) (map getDims2 ts2) @@ -491,7 +534,7 @@ instance Typed dec => Typed (PatElemT dec) where   typeOf = typeOf . patElemDec -instance Typed b => Typed (a,b) where+instance Typed b => Typed (a, b) where   typeOf = typeOf . snd  -- | Typeclass for things that contain 'DeclType's.@@ -549,9 +592,10 @@   fixExt i se = fmap $ fixExt i se  instance FixExt ExtSize where-  fixExt i se (Ext j) | j > i     = Ext $ j - 1-                      | j == i    = Free se-                      | otherwise = Ext j+  fixExt i se (Ext j)+    | j > i = Ext $ j - 1+    | j == i = Free se+    | otherwise = Ext j   fixExt _ _ (Free x) = Free x  instance FixExt () where
src/Futhark/IR/RetType.hs view
@@ -1,18 +1,19 @@-{-# LANGUAGE FlexibleInstances, TypeFamilies #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeFamilies #-}+ -- | This module exports a type class covering representations of -- function return types. module Futhark.IR.RetType-       (-         IsBodyType (..)-       , IsRetType (..)-       , expectedTypes-       )-       where+  ( IsBodyType (..),+    IsRetType (..),+    expectedTypes,+  )+where +import Control.Monad.Identity import qualified Data.Map.Strict as M--import Futhark.IR.Syntax.Core import Futhark.IR.Prop.Types+import Futhark.IR.Syntax.Core  -- | A type representing the return type of a body.  It should contain -- at least the information contained in a list of 'ExtType's, but may@@ -35,50 +36,46 @@   -- | Given a function return type, the parameters of the function,   -- and the arguments for a concrete call, return the instantiated   -- return type for the concrete call, if valid.-  applyRetType :: Typed dec =>-                  [rt]-               -> [Param dec]-               -> [(SubExp, Type)]-               -> Maybe [rt]+  applyRetType ::+    Typed dec =>+    [rt] ->+    [Param dec] ->+    [(SubExp, Type)] ->+    Maybe [rt]  -- | Given shape parameter names and value parameter types, produce the -- types of arguments accepted. expectedTypes :: Typed t => [VName] -> [t] -> [SubExp] -> [Type] expectedTypes shapes value_ts args = map (correctDims . typeOf) value_ts-    where parammap :: M.Map VName SubExp-          parammap = M.fromList $ zip shapes args--          correctDims t =-            t `setArrayShape`-            Shape (map correctDim $ shapeDims $ arrayShape t)+  where+    parammap :: M.Map VName SubExp+    parammap = M.fromList $ zip shapes args -          correctDim (Constant v) = Constant v-          correctDim (Var v)-            | Just se <- M.lookup v parammap = se-            | otherwise                       = Var v+    correctDims = runIdentity . mapOnType (pure . f)+      where+        f (Var v)+          | Just se <- M.lookup v parammap = se+        f se = se  instance IsRetType DeclExtType where   primRetType = Prim    applyRetType extret params args =-    if length args == length params &&-       and (zipWith subtypeOf argtypes $-            expectedTypes (map paramName params) params $ map fst args)-    then Just $ map correctExtDims extret-    else Nothing-    where argtypes = map snd args--          parammap :: M.Map VName SubExp-          parammap = M.fromList $ zip (map paramName params) (map fst args)--          correctExtDims t =-            t `setArrayShape`-            Shape (map correctExtDim $ shapeDims $ arrayShape t)+    if length args == length params+      && and+        ( zipWith subtypeOf argtypes $+            expectedTypes (map paramName params) params $ map fst args+        )+      then Just $ map correctExtDims extret+      else Nothing+    where+      argtypes = map snd args -          correctExtDim (Ext i)  = Ext i-          correctExtDim (Free d) = Free $ correctDim d+      parammap :: M.Map VName SubExp+      parammap = M.fromList $ zip (map paramName params) (map fst args) -          correctDim (Constant v) = Constant v-          correctDim (Var v)+      correctExtDims = runIdentity . mapOnExtType (pure . f)+        where+          f (Var v)             | Just se <- M.lookup v parammap = se-            | otherwise                       = Var v+          f se = se
src/Futhark/IR/SOACS.hs view
@@ -1,43 +1,54 @@-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE Safe #-}+{-# LANGUAGE TypeFamilies #-}+ -- | A simple representation with SOACs and nested parallelism. module Futhark.IR.SOACS-       ( -- * The Lore definition-         SOACS-         -- * Syntax types-       , Body-       , Stm-       , Pattern-       , Exp-       , Lambda-       , FParam-       , LParam-       , RetType-       , PatElem-         -- * Module re-exports-       , module Futhark.IR.Prop-       , module Futhark.IR.Traversals-       , module Futhark.IR.Pretty-       , module Futhark.IR.Syntax-       , module Futhark.IR.SOACS.SOAC-       , AST.LambdaT(Lambda)-       , AST.BodyT(Body)-       , AST.PatternT(Pattern)-       , AST.PatElemT(PatElem)-       )+  ( -- * The Lore definition+    SOACS,++    -- * Syntax types+    Body,+    Stm,+    Pattern,+    Exp,+    Lambda,+    FParam,+    LParam,+    RetType,+    PatElem,++    -- * Module re-exports+    module Futhark.IR.Prop,+    module Futhark.IR.Traversals,+    module Futhark.IR.Pretty,+    module Futhark.IR.Syntax,+    module Futhark.IR.SOACS.SOAC,+    AST.LambdaT (Lambda),+    AST.BodyT (Body),+    AST.PatternT (Pattern),+    AST.PatElemT (PatElem),+  ) where -import qualified Futhark.IR.Syntax as AST-import Futhark.IR.Syntax-  hiding (Exp, Body, Stm,-          Pattern, Lambda, FParam, LParam, RetType, PatElem)-import Futhark.IR.SOACS.SOAC-import Futhark.IR.Prop-import Futhark.IR.Traversals-import Futhark.IR.Pretty import Futhark.Binder import Futhark.Construct+import Futhark.IR.Pretty+import Futhark.IR.Prop+import Futhark.IR.SOACS.SOAC+import Futhark.IR.Syntax hiding+  ( Body,+    Exp,+    FParam,+    LParam,+    Lambda,+    PatElem,+    Pattern,+    RetType,+    Stm,+  )+import qualified Futhark.IR.Syntax as AST+import Futhark.IR.Traversals import qualified Futhark.TypeCheck as TypeCheck  -- This module could be written much nicer if Haskell had functors@@ -54,19 +65,27 @@   expTypesFromPattern = return . expExtTypesFromPattern  type Exp = AST.Exp SOACS+ type Body = AST.Body SOACS+ type Stm = AST.Stm SOACS+ type Pattern = AST.Pattern SOACS+ type Lambda = AST.Lambda SOACS+ type FParam = AST.FParam SOACS+ type LParam = AST.LParam SOACS+ type RetType = AST.RetType SOACS+ type PatElem = AST.PatElem SOACS  instance TypeCheck.CheckableOp SOACS where   checkOp = typeCheckSOAC -instance TypeCheck.Checkable SOACS where+instance TypeCheck.Checkable SOACS  instance Bindable SOACS where   mkBody = AST.Body ()@@ -74,6 +93,6 @@   mkExpDec _ _ = ()   mkLetNames = simpleMkLetNames -instance BinderOps SOACS where+instance BinderOps SOACS -instance PrettyLore SOACS where+instance PrettyLore SOACS
src/Futhark/IR/SOACS/SOAC.hs view
@@ -1,83 +1,85 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+ -- | Definition of /Second-Order Array Combinators/ (SOACs), which are -- the main form of parallelism in the early stages of the compiler. module Futhark.IR.SOACS.SOAC-       ( SOAC(..)-       , StreamOrd(..)-       , StreamForm(..)-       , ScremaForm(..)-       , HistOp(..)-       , Scan(..)-       , scanResults-       , singleScan-       , Reduce(..)-       , redResults-       , singleReduce--         -- * Utility-       , getStreamAccums-       , scremaType-       , soacType--       , typeCheckSOAC--       , mkIdentityLambda-       , isIdentityLambda-       , nilFn-       , scanomapSOAC-       , redomapSOAC-       , scanSOAC-       , reduceSOAC-       , mapSOAC-       , isScanomapSOAC-       , isRedomapSOAC-       , isScanSOAC-       , isReduceSOAC-       , isMapSOAC+  ( SOAC (..),+    StreamOrd (..),+    StreamForm (..),+    ScremaForm (..),+    HistOp (..),+    Scan (..),+    scanResults,+    singleScan,+    Reduce (..),+    redResults,+    singleReduce, -       , ppScrema-       , ppHist+    -- * Utility+    getStreamAccums,+    scremaType,+    soacType,+    typeCheckSOAC,+    mkIdentityLambda,+    isIdentityLambda,+    nilFn,+    scanomapSOAC,+    redomapSOAC,+    scanSOAC,+    reduceSOAC,+    mapSOAC,+    isScanomapSOAC,+    isRedomapSOAC,+    isScanSOAC,+    isReduceSOAC,+    isMapSOAC,+    ppScrema,+    ppHist, -         -- * Generic traversal-       , SOACMapper(..)-       , identitySOACMapper-       , mapSOACM-       )-       where+    -- * Generic traversal+    SOACMapper (..),+    identitySOACMapper,+    mapSOACM,+  )+where +import Control.Category+import Control.Monad.Identity import Control.Monad.State.Strict import Control.Monad.Writer-import Control.Monad.Identity+import Data.List (intersperse) import qualified Data.Map.Strict as M import Data.Maybe-import Data.List (intersperse)--import Futhark.IR import qualified Futhark.Analysis.Alias as Alias-import qualified Futhark.Util.Pretty as PP-import Futhark.Util.Pretty (ppr, Doc, Pretty, parens, comma, (</>), (<+>), commasep, text)+import Futhark.Analysis.Metrics+import Futhark.Analysis.PrimExp.Convert+import qualified Futhark.Analysis.SymbolTable as ST+import Futhark.Construct+import Futhark.IR+import Futhark.IR.Aliases (Aliases, removeLambdaAliases) import Futhark.IR.Prop.Aliases-import Futhark.Transform.Substitute-import Futhark.Transform.Rename import Futhark.Optimise.Simplify.Lore-import Futhark.IR.Aliases (Aliases, removeLambdaAliases)-import qualified Futhark.Analysis.SymbolTable as ST-import Futhark.Analysis.PrimExp.Convert+import Futhark.Transform.Rename+import Futhark.Transform.Substitute import qualified Futhark.TypeCheck as TC-import Futhark.Analysis.Metrics-import Futhark.Construct-import Futhark.Util (maybeNth, chunks)+import Futhark.Util (chunks, maybeNth)+import Futhark.Util.Pretty (Doc, Pretty, comma, commasep, parens, ppr, text, (<+>), (</>))+import qualified Futhark.Util.Pretty as PP+import GHC.Generics (Generic)+import Language.SexpGrammar as Sexp+import Language.SexpGrammar.Generic+import Prelude hiding (id, (.))  -- | A second-order array combinator (SOAC).-data SOAC lore =-    Stream SubExp (StreamForm lore) (Lambda lore) [VName]-  | Scatter SubExp (Lambda lore) [VName] [(SubExp, Int, VName)]-    -- ^ @Scatter <cs> <length> <lambda> <original index and value arrays>@+data SOAC lore+  = Stream SubExp (StreamForm lore) (Lambda lore) [VName]+  | -- | @Scatter <cs> <length> <lambda> <original index and value arrays>@     --     -- <input/output arrays along with their sizes and number of     -- values to write for that array>@@ -92,65 +94,142 @@     --     [index_0, index_1, ..., index_n, value_0, value_1, ..., value_n]     --     -- This must be consistent along all Scatter-related optimisations.-  | Hist SubExp [HistOp lore] (Lambda lore) [VName]-    -- ^ @Hist <length> <dest-arrays-and-ops> <bucket fun> <input arrays>@+    Scatter SubExp (Lambda lore) [VName] [(SubExp, Int, VName)]+  | -- | @Hist <length> <dest-arrays-and-ops> <bucket fun> <input arrays>@     --     -- The first SubExp is the length of the input arrays. The first     -- list describes the operations to perform.  The t'Lambda' is the     -- bucket function.  Finally comes the input images.-  | Screma SubExp (ScremaForm lore) [VName]-    -- ^ A combination of scan, reduction, and map.  The first+    Hist SubExp [HistOp lore] (Lambda lore) [VName]+  | -- | A combination of scan, reduction, and map.  The first     -- t'SubExp' is the size of the input arrays.-    deriving (Eq, Ord, Show)+    Screma SubExp (ScremaForm lore) [VName]+  deriving (Eq, Ord, Show, Generic) +instance Decorations lore => SexpIso (SOAC lore) where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "stream") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+        With (. Sexp.list (Sexp.el (Sexp.sym "scatter") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+          With (. Sexp.list (Sexp.el (Sexp.sym "hist") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+            With+              (. Sexp.list (Sexp.el (Sexp.sym "screma") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso))+              End+ -- | Information about computing a single histogram.-data HistOp lore = HistOp { histWidth :: SubExp-                          , histRaceFactor :: SubExp-                          -- ^ Race factor @RF@ means that only @1/RF@-                          -- bins are used.-                          , histDest :: [VName]-                          , histNeutral :: [SubExp]-                          , histOp :: Lambda lore-                          }-                      deriving (Eq, Ord, Show)+data HistOp lore = HistOp+  { histWidth :: SubExp,+    -- | Race factor @RF@ means that only @1/RF@+    -- bins are used.+    histRaceFactor :: SubExp,+    histDest :: [VName],+    histNeutral :: [SubExp],+    histOp :: Lambda lore+  }+  deriving (Eq, Ord, Show, Generic) +instance Decorations lore => SexpIso (HistOp lore) where+  sexpIso = with $ \histop ->+    Sexp.list+      ( Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> histop+ -- | Is the stream chunk required to correspond to a contiguous -- subsequence of the original input ('InOrder') or not?  'Disorder' -- streams can be more efficient, but not all algorithms work with -- this.-data StreamOrd  = InOrder | Disorder-                deriving (Eq, Ord, Show)+data StreamOrd = InOrder | Disorder+  deriving (Eq, Ord, Show, Generic) +instance SexpIso StreamOrd where+  sexpIso =+    match $+      With (. Sexp.sym "in-order") $+        With+          (. Sexp.sym "disorder")+          End+ -- | What kind of stream is this?-data StreamForm lore  =-    Parallel StreamOrd Commutativity (Lambda lore) [SubExp]+data StreamForm lore+  = Parallel StreamOrd Commutativity (Lambda lore) [SubExp]   | Sequential [SubExp]-  deriving (Eq, Ord, Show)+  deriving (Eq, Ord, Show, Generic) +instance Decorations lore => SexpIso (StreamForm lore) where+  sexpIso =+    match $+      With+        ( .+            Sexp.list+              ( Sexp.el (Sexp.sym "parallel")+                  >>> Sexp.el sexpIso+                  >>> Sexp.el sexpIso+                  >>> Sexp.el sexpIso+                  >>> Sexp.rest sexpIso+              )+        )+        $ With+          ( .+              Sexp.list+                ( Sexp.el (Sexp.sym "sequential")+                    >>> Sexp.rest sexpIso+                )+          )+          End+ -- | The essential parts of a 'Screma' factored out (everything -- except the input arrays).-data ScremaForm lore = ScremaForm-                         [Scan lore]-                         [Reduce lore]-                         (Lambda lore)-  deriving (Eq, Ord, Show)+data ScremaForm lore+  = ScremaForm+      [Scan lore]+      [Reduce lore]+      (Lambda lore)+  deriving (Eq, Ord, Show, Generic) +instance Decorations lore => SexpIso (ScremaForm lore) where+  sexpIso = with $ \scremaform ->+    Sexp.list+      ( Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> scremaform+ singleBinOp :: Bindable lore => [Lambda lore] -> Lambda lore singleBinOp lams =-  Lambda { lambdaParams = concatMap xParams lams ++ concatMap yParams lams-         , lambdaReturnType = concatMap lambdaReturnType lams-         , lambdaBody = mkBody (mconcat (map (bodyStms . lambdaBody) lams))-                        (concatMap (bodyResult . lambdaBody) lams)-         }-  where xParams lam = take (length (lambdaReturnType lam)) (lambdaParams lam)-        yParams lam = drop (length (lambdaReturnType lam)) (lambdaParams lam)+  Lambda+    { lambdaParams = concatMap xParams lams ++ concatMap yParams lams,+      lambdaReturnType = concatMap lambdaReturnType lams,+      lambdaBody =+        mkBody+          (mconcat (map (bodyStms . lambdaBody) lams))+          (concatMap (bodyResult . lambdaBody) lams)+    }+  where+    xParams lam = take (length (lambdaReturnType lam)) (lambdaParams lam)+    yParams lam = drop (length (lambdaReturnType lam)) (lambdaParams lam)  -- | How to compute a single scan result.-data Scan lore = Scan { scanLambda :: Lambda lore-                      , scanNeutral :: [SubExp]-                      }-               deriving (Eq, Ord, Show)+data Scan lore = Scan+  { scanLambda :: Lambda lore,+    scanNeutral :: [SubExp]+  }+  deriving (Eq, Ord, Show, Generic) +instance Decorations lore => SexpIso (Scan lore) where+  sexpIso = with $ \scan ->+    Sexp.list+      ( Sexp.el (Sexp.sym "scan")+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> scan+ -- | How many reduction results are produced by these 'Scan's? scanResults :: [Scan lore] -> Int scanResults = sum . map (length . scanNeutral)@@ -160,15 +239,26 @@ singleScan scans =   let scan_nes = concatMap scanNeutral scans       scan_lam = singleBinOp $ map scanLambda scans-  in Scan scan_lam scan_nes+   in Scan scan_lam scan_nes  -- | How to compute a single reduction result.-data Reduce lore = Reduce { redComm :: Commutativity-                          , redLambda :: Lambda lore-                          , redNeutral :: [SubExp]-                          }-                   deriving (Eq, Ord, Show)+data Reduce lore = Reduce+  { redComm :: Commutativity,+    redLambda :: Lambda lore,+    redNeutral :: [SubExp]+  }+  deriving (Eq, Ord, Show, Generic) +instance Decorations lore => SexpIso (Reduce lore) where+  sexpIso = with $ \red ->+    Sexp.list+      ( Sexp.el (Sexp.sym "reduce")+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> red+ -- | How many reduction results are produced by these 'Reduce's? redResults :: [Reduce lore] -> Int redResults = sum . map (length . redNeutral)@@ -178,32 +268,40 @@ singleReduce reds =   let red_nes = concatMap redNeutral reds       red_lam = singleBinOp $ map redLambda reds-  in Reduce (mconcat (map redComm reds)) red_lam red_nes+   in Reduce (mconcat (map redComm reds)) red_lam red_nes  -- | The types produced by a single 'Screma', given the size of the -- input array. scremaType :: SubExp -> ScremaForm lore -> [Type] scremaType w (ScremaForm scans reds map_lam) =   scan_tps ++ red_tps ++ map (`arrayOfRow` w) map_tps-  where scan_tps = map (`arrayOfRow` w) $-                   concatMap (lambdaReturnType . scanLambda) scans-        red_tps  = concatMap (lambdaReturnType . redLambda) reds-        map_tps  = drop (length scan_tps + length red_tps) $ lambdaReturnType map_lam+  where+    scan_tps =+      map (`arrayOfRow` w) $+        concatMap (lambdaReturnType . scanLambda) scans+    red_tps = concatMap (lambdaReturnType . redLambda) reds+    map_tps = drop (length scan_tps + length red_tps) $ lambdaReturnType map_lam  -- | Construct a lambda that takes parameters of the given types and -- simply returns them unchanged.-mkIdentityLambda :: (Bindable lore, MonadFreshNames m) =>-                    [Type] -> m (Lambda lore)+mkIdentityLambda ::+  (Bindable lore, MonadFreshNames m) =>+  [Type] ->+  m (Lambda lore) mkIdentityLambda ts = do   params <- mapM (newParam "x") ts-  return Lambda { lambdaParams = params-                , lambdaBody = mkBody mempty $ map (Var . paramName) params-                , lambdaReturnType = ts }+  return+    Lambda+      { lambdaParams = params,+        lambdaBody = mkBody mempty $ map (Var . paramName) params,+        lambdaReturnType = ts+      }  -- | Is the given lambda an identity lambda? isIdentityLambda :: Lambda lore -> Bool-isIdentityLambda lam = bodyResult (lambdaBody lam) ==-                       map (Var . paramName) (lambdaParams lam)+isIdentityLambda lam =+  bodyResult (lambdaBody lam)+    == map (Var . paramName) (lambdaParams lam)  -- | A lambda with no parameters that returns no values. nilFn :: Bindable lore => Lambda lore@@ -221,17 +319,23 @@  -- | Construct a Screma with possibly multiple scans, and identity map -- function.-scanSOAC :: (Bindable lore, MonadFreshNames m) =>-            [Scan lore] -> m (ScremaForm lore)+scanSOAC ::+  (Bindable lore, MonadFreshNames m) =>+  [Scan lore] ->+  m (ScremaForm lore) scanSOAC scans = scanomapSOAC scans <$> mkIdentityLambda ts-  where ts = concatMap (lambdaReturnType . scanLambda) scans+  where+    ts = concatMap (lambdaReturnType . scanLambda) scans  -- | Construct a Screma with possibly multiple reductions, and -- identity map function.-reduceSOAC :: (Bindable lore, MonadFreshNames m) =>-              [Reduce lore] -> m (ScremaForm lore)+reduceSOAC ::+  (Bindable lore, MonadFreshNames m) =>+  [Reduce lore] ->+  m (ScremaForm lore) reduceSOAC reds = redomapSOAC reds <$> mkIdentityLambda ts-  where ts = concatMap (lambdaReturnType . redLambda) reds+  where+    ts = concatMap (lambdaReturnType . redLambda) reds  -- | Construct a Screma corresponding to a map. mapSOAC :: Lambda lore -> ScremaForm lore@@ -246,9 +350,10 @@  -- | Does this Screma correspond to pure scan? isScanSOAC :: ScremaForm lore -> Maybe [Scan lore]-isScanSOAC form = do (scans, map_lam) <- isScanomapSOAC form-                     guard $ isIdentityLambda map_lam-                     return scans+isScanSOAC form = do+  (scans, map_lam) <- isScanomapSOAC form+  guard $ isIdentityLambda map_lam+  return scans  -- | Does this Screma correspond to a reduce-map composition? isRedomapSOAC :: ScremaForm lore -> Maybe ([Reduce lore], Lambda lore)@@ -259,9 +364,10 @@  -- | Does this Screma correspond to a pure reduce? isReduceSOAC :: ScremaForm lore -> Maybe [Reduce lore]-isReduceSOAC form = do (reds, map_lam) <- isRedomapSOAC form-                       guard $ isIdentityLambda map_lam-                       return reds+isReduceSOAC form = do+  (reds, map_lam) <- isRedomapSOAC form+  guard $ isIdentityLambda map_lam+  return reds  -- | Does this Screma correspond to a simple map, without any -- reduction or scan results?@@ -272,100 +378,132 @@   return map_lam  -- | Like 'Mapper', but just for 'SOAC's.-data SOACMapper flore tlore m = SOACMapper {-    mapOnSOACSubExp :: SubExp -> m SubExp-  , mapOnSOACLambda :: Lambda flore -> m (Lambda tlore)-  , mapOnSOACVName :: VName -> m VName+data SOACMapper flore tlore m = SOACMapper+  { mapOnSOACSubExp :: SubExp -> m SubExp,+    mapOnSOACLambda :: Lambda flore -> m (Lambda tlore),+    mapOnSOACVName :: VName -> m VName   }  -- | A mapper that simply returns the SOAC verbatim. identitySOACMapper :: Monad m => SOACMapper lore lore m-identitySOACMapper = SOACMapper { mapOnSOACSubExp = return-                                , mapOnSOACLambda = return-                                , mapOnSOACVName = return-                                }+identitySOACMapper =+  SOACMapper+    { mapOnSOACSubExp = return,+      mapOnSOACLambda = return,+      mapOnSOACVName = return+    }  -- | Map a monadic action across the immediate children of a -- SOAC.  The mapping does not descend recursively into subexpressions -- and is done left-to-right.-mapSOACM :: (Applicative m, Monad m) =>-            SOACMapper flore tlore m -> SOAC flore -> m (SOAC tlore)+mapSOACM ::+  (Applicative m, Monad m) =>+  SOACMapper flore tlore m ->+  SOAC flore ->+  m (SOAC tlore) mapSOACM tv (Stream size form lam arrs) =-  Stream <$> mapOnSOACSubExp tv size <*>-  mapOnStreamForm form <*> mapOnSOACLambda tv lam <*>-  mapM (mapOnSOACVName tv) arrs-  where mapOnStreamForm (Parallel o comm lam0 acc) =-            Parallel o comm <$>-            mapOnSOACLambda tv lam0 <*>-            mapM (mapOnSOACSubExp tv) acc-        mapOnStreamForm (Sequential acc) =-            Sequential <$> mapM (mapOnSOACSubExp tv) acc+  Stream <$> mapOnSOACSubExp tv size+    <*> mapOnStreamForm form+    <*> mapOnSOACLambda tv lam+    <*> mapM (mapOnSOACVName tv) arrs+  where+    mapOnStreamForm (Parallel o comm lam0 acc) =+      Parallel o comm+        <$> mapOnSOACLambda tv lam0+        <*> mapM (mapOnSOACSubExp tv) acc+    mapOnStreamForm (Sequential acc) =+      Sequential <$> mapM (mapOnSOACSubExp tv) acc mapSOACM tv (Scatter len lam ivs as) =   Scatter-  <$> mapOnSOACSubExp tv len-  <*> mapOnSOACLambda tv lam-  <*> mapM (mapOnSOACVName tv) ivs-  <*> mapM (\(aw,an,a) -> (,,) <$> mapOnSOACSubExp tv aw <*>-                          pure an <*> mapOnSOACVName tv a) as+    <$> mapOnSOACSubExp tv len+    <*> mapOnSOACLambda tv lam+    <*> mapM (mapOnSOACVName tv) ivs+    <*> mapM+      ( \(aw, an, a) ->+          (,,) <$> mapOnSOACSubExp tv aw+            <*> pure an+            <*> mapOnSOACVName tv a+      )+      as mapSOACM tv (Hist len ops bucket_fun imgs) =   Hist-  <$> mapOnSOACSubExp tv len-  <*> mapM (\(HistOp e rf arrs nes op) ->-              HistOp <$> mapOnSOACSubExp tv e-              <*> mapOnSOACSubExp tv rf-              <*> mapM (mapOnSOACVName tv) arrs-              <*> mapM (mapOnSOACSubExp tv) nes-              <*> mapOnSOACLambda tv op) ops-  <*> mapOnSOACLambda tv bucket_fun-  <*> mapM (mapOnSOACVName tv) imgs+    <$> mapOnSOACSubExp tv len+    <*> mapM+      ( \(HistOp e rf arrs nes op) ->+          HistOp <$> mapOnSOACSubExp tv e+            <*> mapOnSOACSubExp tv rf+            <*> mapM (mapOnSOACVName tv) arrs+            <*> mapM (mapOnSOACSubExp tv) nes+            <*> mapOnSOACLambda tv op+      )+      ops+    <*> mapOnSOACLambda tv bucket_fun+    <*> mapM (mapOnSOACVName tv) imgs mapSOACM tv (Screma w (ScremaForm scans reds map_lam) arrs) =-  Screma <$> mapOnSOACSubExp tv w <*>-  (ScremaForm <$>-   forM scans (\(Scan red_lam red_nes) ->-                  Scan <$> mapOnSOACLambda tv red_lam <*>-                  mapM (mapOnSOACSubExp tv) red_nes) <*>-   forM reds (\(Reduce comm red_lam red_nes) ->-                 Reduce comm <$> mapOnSOACLambda tv red_lam <*>-                 mapM (mapOnSOACSubExp tv) red_nes) <*>-   mapOnSOACLambda tv map_lam)-  <*> mapM (mapOnSOACVName tv) arrs+  Screma <$> mapOnSOACSubExp tv w+    <*> ( ScremaForm+            <$> forM+              scans+              ( \(Scan red_lam red_nes) ->+                  Scan <$> mapOnSOACLambda tv red_lam+                    <*> mapM (mapOnSOACSubExp tv) red_nes+              )+            <*> forM+              reds+              ( \(Reduce comm red_lam red_nes) ->+                  Reduce comm <$> mapOnSOACLambda tv red_lam+                    <*> mapM (mapOnSOACSubExp tv) red_nes+              )+            <*> mapOnSOACLambda tv map_lam+        )+    <*> mapM (mapOnSOACVName tv) arrs  instance ASTLore lore => FreeIn (SOAC lore) where   freeIn' = flip execState mempty . mapSOACM free-    where walk f x = modify (<>f x) >> return x-          free = SOACMapper { mapOnSOACSubExp = walk freeIn'-                            , mapOnSOACLambda = walk freeIn'-                            , mapOnSOACVName = walk freeIn'-                            }+    where+      walk f x = modify (<> f x) >> return x+      free =+        SOACMapper+          { mapOnSOACSubExp = walk freeIn',+            mapOnSOACLambda = walk freeIn',+            mapOnSOACVName = walk freeIn'+          }  instance ASTLore lore => Substitute (SOAC lore) where   substituteNames subst =     runIdentity . mapSOACM substitute-    where substitute =-            SOACMapper { mapOnSOACSubExp = return . substituteNames subst-                       , mapOnSOACLambda = return . substituteNames subst-                       , mapOnSOACVName = return . substituteNames subst-                       }+    where+      substitute =+        SOACMapper+          { mapOnSOACSubExp = return . substituteNames subst,+            mapOnSOACLambda = return . substituteNames subst,+            mapOnSOACVName = return . substituteNames subst+          }  instance ASTLore lore => Rename (SOAC lore) where   rename = mapSOACM renamer-    where renamer = SOACMapper rename rename rename+    where+      renamer = SOACMapper rename rename rename  -- | The type of a SOAC. soacType :: SOAC lore -> [Type] soacType (Stream outersize form lam _) =   map (substNamesInType substs) rtp-  where nms = map paramName $ take (1 + length accs) params-        substs = M.fromList $ zip nms (outersize:accs)-        Lambda params _ rtp = lam-        accs = case form of-                Parallel _ _ _ acc -> acc-                Sequential  acc -> acc+  where+    nms = map paramName $ take (1 + length accs) params+    substs = M.fromList $ zip nms (outersize : accs)+    Lambda params _ rtp = lam+    accs = case form of+      Parallel _ _ _ acc -> acc+      Sequential acc -> acc soacType (Scatter _w lam _ivs as) =   zipWith arrayOfRow val_ts ws-  where val_ts = concatMap (take 1) $ chunks ns $-                 drop (sum ns) $ lambdaReturnType lam-        (ws, ns, _) = unzip3 as+  where+    val_ts =+      concatMap (take 1) $+        chunks ns $+          drop (sum ns) $ lambdaReturnType lam+    (ws, ns, _) = unzip3 as soacType (Hist _len ops _bucket_fun _imgs) = do   op <- ops   map (`arrayOfRow` histWidth op) (lambdaReturnType $ histOp op)@@ -382,56 +520,79 @@   -- and reduce functions are always considered "fresh".   consumedInOp (Screma _ (ScremaForm _ _ map_lam) arrs) =     mapNames consumedArray $ consumedByLambda map_lam-    where consumedArray v = fromMaybe v $ lookup v params_to_arrs-          params_to_arrs = zip (map paramName $ lambdaParams map_lam) arrs+    where+      consumedArray v = fromMaybe v $ lookup v params_to_arrs+      params_to_arrs = zip (map paramName $ lambdaParams map_lam) arrs   consumedInOp (Stream _ form lam arrs) =-    namesFromList $ subExpVars $-    case form of Sequential accs ->-                   map (consumedArray accs) $ namesToList $ consumedByLambda lam-                 Parallel _ _ _ accs ->-                   map (consumedArray accs) $ namesToList $ consumedByLambda lam-    where consumedArray accs v = fromMaybe (Var v) $ lookup v $ paramsToInput accs-          -- Drop the chunk parameter, which cannot alias anything.-          paramsToInput accs = zip-                               (map paramName $ drop 1 $ lambdaParams lam)-                               (accs++map Var arrs)+    namesFromList $+      subExpVars $+        case form of+          Sequential accs ->+            map (consumedArray accs) $ namesToList $ consumedByLambda lam+          Parallel _ _ _ accs ->+            map (consumedArray accs) $ namesToList $ consumedByLambda lam+    where+      consumedArray accs v = fromMaybe (Var v) $ lookup v $ paramsToInput accs+      -- Drop the chunk parameter, which cannot alias anything.+      paramsToInput accs =+        zip+          (map paramName $ drop 1 $ lambdaParams lam)+          (accs ++ map Var arrs)   consumedInOp (Scatter _ _ _ as) =     namesFromList $ map (\(_, _, a) -> a) as   consumedInOp (Hist _ ops _ _) =     namesFromList $ concatMap histDest ops -mapHistOp :: (Lambda flore -> Lambda tlore)-          -> HistOp flore -> HistOp tlore+mapHistOp ::+  (Lambda flore -> Lambda tlore) ->+  HistOp flore ->+  HistOp tlore mapHistOp f (HistOp w rf dests nes lam) =   HistOp w rf dests nes $ f lam -instance (ASTLore lore,-          ASTLore (Aliases lore),-          CanBeAliased (Op lore)) => CanBeAliased (SOAC lore) where+instance+  ( ASTLore lore,+    ASTLore (Aliases lore),+    CanBeAliased (Op lore)+  ) =>+  CanBeAliased (SOAC lore)+  where   type OpWithAliases (SOAC lore) = SOAC (Aliases lore)    addOpAliases (Stream size form lam arr) =-    Stream size (analyseStreamForm form)-    (Alias.analyseLambda lam) arr-    where analyseStreamForm (Parallel o comm lam0 acc) =-              Parallel o comm (Alias.analyseLambda lam0) acc-          analyseStreamForm (Sequential acc) = Sequential acc+    Stream+      size+      (analyseStreamForm form)+      (Alias.analyseLambda lam)+      arr+    where+      analyseStreamForm (Parallel o comm lam0 acc) =+        Parallel o comm (Alias.analyseLambda lam0) acc+      analyseStreamForm (Sequential acc) = Sequential acc   addOpAliases (Scatter len lam ivs as) =     Scatter len (Alias.analyseLambda lam) ivs as   addOpAliases (Hist len ops bucket_fun imgs) =-    Hist len (map (mapHistOp Alias.analyseLambda) ops)-    (Alias.analyseLambda bucket_fun) imgs+    Hist+      len+      (map (mapHistOp Alias.analyseLambda) ops)+      (Alias.analyseLambda bucket_fun)+      imgs   addOpAliases (Screma w (ScremaForm scans reds map_lam) arrs) =-    Screma w (ScremaForm-                (map onScan scans)-                (map onRed reds)-                (Alias.analyseLambda map_lam))-               arrs-    where onRed red = red { redLambda = Alias.analyseLambda $ redLambda red }-          onScan scan = scan { scanLambda = Alias.analyseLambda $ scanLambda scan }+    Screma+      w+      ( ScremaForm+          (map onScan scans)+          (map onRed reds)+          (Alias.analyseLambda map_lam)+      )+      arrs+    where+      onRed red = red {redLambda = Alias.analyseLambda $ redLambda red}+      onScan scan = scan {scanLambda = Alias.analyseLambda $ scanLambda scan}    removeOpAliases = runIdentity . mapSOACM remove-    where remove = SOACMapper return (return . removeLambdaAliases) return+    where+      remove = SOACMapper return (return . removeLambdaAliases) return  instance ASTLore lore => IsOp (SOAC lore) where   safeOp _ = False@@ -442,7 +603,7 @@ substNamesInType _ (Mem space) = Mem space substNamesInType subs (Array btp shp u) =   let shp' = Shape $ map (substNamesInSubExp subs) (shapeDims shp)-  in  Array btp shp' u+   in Array btp shp' u  substNamesInSubExp :: M.Map VName SubExp -> SubExp -> SubExp substNamesInSubExp _ e@(Constant _) = e@@ -453,77 +614,81 @@   type OpWithWisdom (SOAC lore) = SOAC (Wise lore)    removeOpWisdom = runIdentity . mapSOACM remove-    where remove = SOACMapper return (return . removeLambdaWisdom) return+    where+      remove = SOACMapper return (return . removeLambdaWisdom) return  instance Decorations lore => ST.IndexOp (SOAC lore) where   indexOp vtable k soac [i] = do-    (lam,se,arr_params,arrs) <- lambdaAndSubExp soac+    (lam, se, arr_params, arrs) <- lambdaAndSubExp soac     let arr_indexes = M.fromList $ catMaybes $ zipWith arrIndex arr_params arrs         arr_indexes' = foldl expandPrimExpTable arr_indexes $ bodyStms $ lambdaBody lam     case se of       Var v -> uncurry (flip ST.Indexed) <$> M.lookup v arr_indexes'       _ -> Nothing-      where lambdaAndSubExp (Screma _ (ScremaForm scans reds map_lam) arrs) =-              nthMapOut (scanResults scans + redResults reds) map_lam arrs-            lambdaAndSubExp _ =-              Nothing+    where+      lambdaAndSubExp (Screma _ (ScremaForm scans reds map_lam) arrs) =+        nthMapOut (scanResults scans + redResults reds) map_lam arrs+      lambdaAndSubExp _ =+        Nothing -            nthMapOut num_accs lam arrs = do-              se <- maybeNth (num_accs+k) $ bodyResult $ lambdaBody lam-              return (lam, se, drop num_accs $ lambdaParams lam, arrs)+      nthMapOut num_accs lam arrs = do+        se <- maybeNth (num_accs + k) $ bodyResult $ lambdaBody lam+        return (lam, se, drop num_accs $ lambdaParams lam, arrs) -            arrIndex p arr = do-              ST.Indexed cs pe <- ST.index' arr [i] vtable-              return (paramName p, (pe,cs))+      arrIndex p arr = do+        ST.Indexed cs pe <- ST.index' arr [i] vtable+        return (paramName p, (pe, cs)) -            expandPrimExpTable table stm-              | [v] <- patternNames $ stmPattern stm,-                Just (pe,cs) <--                  runWriterT $ primExpFromExp (asPrimExp table) $ stmExp stm,-                all (`ST.elem` vtable) (unCertificates $ stmCerts stm) =-                  M.insert v (pe, stmCerts stm <> cs) table-              | otherwise =-                  table+      expandPrimExpTable table stm+        | [v] <- patternNames $ stmPattern stm,+          Just (pe, cs) <-+            runWriterT $ primExpFromExp (asPrimExp table) $ stmExp stm,+          all (`ST.elem` vtable) (unCertificates $ stmCerts stm) =+          M.insert v (pe, stmCerts stm <> cs) table+        | otherwise =+          table -            asPrimExp table v-              | Just (e,cs) <- M.lookup v table = tell cs >> return e-              | Just (Prim pt) <- ST.lookupType v vtable =-                  return $ LeafExp v pt-              | otherwise = lift Nothing+      asPrimExp table v+        | Just (e, cs) <- M.lookup v table = tell cs >> return e+        | Just (Prim pt) <- ST.lookupType v vtable =+          return $ LeafExp v pt+        | otherwise = lift Nothing   indexOp _ _ _ _ = Nothing  -- | Type-check a SOAC. typeCheckSOAC :: TC.Checkable lore => SOAC (Aliases lore) -> TC.TypeM lore () typeCheckSOAC (Stream size form lam arrexps) = do   let accexps = getStreamAccums form-  TC.require [Prim int32] size+  TC.require [Prim int64] size   accargs <- mapM TC.checkArg accexps   arrargs <- mapM lookupType arrexps   _ <- TC.checkSOACArrayArgs size arrexps   let chunk = head $ lambdaParams lam   let asArg t = (t, mempty)-      inttp   = Prim int32-      lamarrs'= map (`setOuterSize` Var (paramName chunk)) arrargs-  let acc_len= length accexps+      inttp = Prim int64+      lamarrs' = map (`setOuterSize` Var (paramName chunk)) arrargs+  let acc_len = length accexps   let lamrtp = take acc_len $ lambdaReturnType lam   unless (map TC.argType accargs == lamrtp) $     TC.bad $ TC.TypeError "Stream with inconsistent accumulator type in lambda."   -- check reduce's lambda, if any   _ <- case form of-        Parallel _ _ lam0 _ -> do-            let acct = map TC.argType accargs-                outerRetType = lambdaReturnType lam0-            TC.checkLambda lam0 $ map TC.noArgAliases $ accargs ++ accargs-            unless (acct == outerRetType) $-                TC.bad $ TC.TypeError $-                "Initial value is of type " ++ prettyTuple acct ++-                ", but stream's reduce lambda returns type " ++ prettyTuple outerRetType ++ "."-        _ -> return ()+    Parallel _ _ lam0 _ -> do+      let acct = map TC.argType accargs+          outerRetType = lambdaReturnType lam0+      TC.checkLambda lam0 $ map TC.noArgAliases $ accargs ++ accargs+      unless (acct == outerRetType) $+        TC.bad $+          TC.TypeError $+            "Initial value is of type " ++ prettyTuple acct+              ++ ", but stream's reduce lambda returns type "+              ++ prettyTuple outerRetType+              ++ "."+    _ -> return ()   -- just get the dflow of lambda on the fakearg, which does not alias   -- arr, so we can later check that aliases of arr are not used inside lam.   let fake_lamarrs' = map asArg lamarrs'   TC.checkLambda lam $ asArg inttp : accargs ++ fake_lamarrs'- typeCheckSOAC (Scatter w lam ivs as) = do   -- Requirements:   --@@ -533,7 +698,7 @@   --   1. The number of index types must be equal to the number of value types   --      and the number of writes to arrays in @as@.   ---  --   2. Each index type must have the type i32.+  --   2. Each index type must have the type i64.   --   --   3. Each array in @as@ and the value types must have the same type   --@@ -547,7 +712,7 @@   -- Code:    -- First check the input size.-  TC.require [Prim int32] w+  TC.require [Prim int64] w    -- 0.   let (_as_ws, as_ns, _as_vs) = unzip3 as@@ -557,16 +722,17 @@       rtsV = drop rtsLen rts    -- 1.-  unless (rtsLen == sum as_ns)-    $ TC.bad $ TC.TypeError "Scatter: Uneven number of index types, value types, and arrays outputs."+  unless (rtsLen == sum as_ns) $+    TC.bad $ TC.TypeError "Scatter: Uneven number of index types, value types, and arrays outputs."    -- 2.-  forM_ rtsI $ \rtI -> unless (Prim int32 == rtI) $-    TC.bad $ TC.TypeError "Scatter: Index return type must be i32."+  forM_ rtsI $ \rtI ->+    unless (Prim int64 == rtI) $+      TC.bad $ TC.TypeError "Scatter: Index return type must be i64."    forM_ (zip (chunks as_ns rtsV) as) $ \(rtVs, (aw, _, a)) -> do-    -- All lengths must have type i32.-    TC.require [Prim int32] aw+    -- All lengths must have type i64.+    TC.require [Prim int64] aw      -- 3.     forM_ rtVs $ \rtV -> TC.requireI [rtV `arrayOfRow` aw] a@@ -577,23 +743,25 @@   -- 5.   arrargs <- TC.checkSOACArrayArgs w ivs   TC.checkLambda lam arrargs- typeCheckSOAC (Hist len ops bucket_fun imgs) = do-  TC.require [Prim int32] len+  TC.require [Prim int64] len    -- Check the operators.   forM_ ops $ \(HistOp dest_w rf dests nes op) -> do     nes' <- mapM TC.checkArg nes-    TC.require [Prim int32] dest_w-    TC.require [Prim int32] rf+    TC.require [Prim int64] dest_w+    TC.require [Prim int64] rf      -- Operator type must match the type of neutral elements.     TC.checkLambda op $ map TC.noArgAliases $ nes' ++ nes'     let nes_t = map TC.argType nes'     unless (nes_t == lambdaReturnType op) $-      TC.bad $ TC.TypeError $ "Operator has return type " ++-      prettyTuple (lambdaReturnType op) ++ " but neutral element has type " ++-      prettyTuple nes_t+      TC.bad $+        TC.TypeError $+          "Operator has return type "+            ++ prettyTuple (lambdaReturnType op)+            ++ " but neutral element has type "+            ++ prettyTuple nes_t      -- Arrays must have proper type.     forM_ (zip nes_t dests) $ \(t, dest) -> do@@ -607,48 +775,62 @@   -- Return type of bucket function must be an index for each   -- operation followed by the values to write.   nes_ts <- concat <$> mapM (mapM subExpType . histNeutral) ops-  let bucket_ret_t = replicate (length ops) (Prim int32) ++ nes_ts+  let bucket_ret_t = replicate (length ops) (Prim int64) ++ nes_ts   unless (bucket_ret_t == lambdaReturnType bucket_fun) $-    TC.bad $ TC.TypeError $ "Bucket function has return type " ++-    prettyTuple (lambdaReturnType bucket_fun) ++ " but should have type " ++-    prettyTuple bucket_ret_t-+    TC.bad $+      TC.TypeError $+        "Bucket function has return type "+          ++ prettyTuple (lambdaReturnType bucket_fun)+          ++ " but should have type "+          ++ prettyTuple bucket_ret_t typeCheckSOAC (Screma w (ScremaForm scans reds map_lam) arrs) = do-  TC.require [Prim int32] w+  TC.require [Prim int64] w   arrs' <- TC.checkSOACArrayArgs w arrs   TC.checkLambda map_lam $ map TC.noArgAliases arrs' -  scan_nes' <- fmap concat $ forM scans $ \(Scan scan_lam scan_nes) -> do-    scan_nes' <- mapM TC.checkArg scan_nes-    let scan_t = map TC.argType scan_nes'-    TC.checkLambda scan_lam $ map TC.noArgAliases $ scan_nes' ++ scan_nes'-    unless (scan_t == lambdaReturnType scan_lam) $-      TC.bad $ TC.TypeError $ "Scan function returns type " ++-      prettyTuple (lambdaReturnType scan_lam) ++ " but neutral element has type " ++-      prettyTuple scan_t-    return scan_nes'+  scan_nes' <- fmap concat $+    forM scans $ \(Scan scan_lam scan_nes) -> do+      scan_nes' <- mapM TC.checkArg scan_nes+      let scan_t = map TC.argType scan_nes'+      TC.checkLambda scan_lam $ map TC.noArgAliases $ scan_nes' ++ scan_nes'+      unless (scan_t == lambdaReturnType scan_lam) $+        TC.bad $+          TC.TypeError $+            "Scan function returns type "+              ++ prettyTuple (lambdaReturnType scan_lam)+              ++ " but neutral element has type "+              ++ prettyTuple scan_t+      return scan_nes' -  red_nes' <- fmap concat $ forM reds $ \(Reduce _ red_lam red_nes) -> do-    red_nes' <- mapM TC.checkArg red_nes-    let red_t = map TC.argType red_nes'-    TC.checkLambda red_lam $ map TC.noArgAliases $ red_nes' ++ red_nes'-    unless (red_t == lambdaReturnType red_lam) $-      TC.bad $ TC.TypeError $ "Reduce function returns type " ++-      prettyTuple (lambdaReturnType red_lam) ++ " but neutral element has type " ++-      prettyTuple red_t-    return red_nes'+  red_nes' <- fmap concat $+    forM reds $ \(Reduce _ red_lam red_nes) -> do+      red_nes' <- mapM TC.checkArg red_nes+      let red_t = map TC.argType red_nes'+      TC.checkLambda red_lam $ map TC.noArgAliases $ red_nes' ++ red_nes'+      unless (red_t == lambdaReturnType red_lam) $+        TC.bad $+          TC.TypeError $+            "Reduce function returns type "+              ++ prettyTuple (lambdaReturnType red_lam)+              ++ " but neutral element has type "+              ++ prettyTuple red_t+      return red_nes'    let map_lam_ts = lambdaReturnType map_lam -  unless (take (length scan_nes' + length red_nes') map_lam_ts ==-          map TC.argType (scan_nes'++ red_nes')) $-    TC.bad $ TC.TypeError $ "Map function return type " ++ prettyTuple map_lam_ts ++-    " wrong for given scan and reduction functions."+  unless+    ( take (length scan_nes' + length red_nes') map_lam_ts+        == map TC.argType (scan_nes' ++ red_nes')+    )+    $ TC.bad $+      TC.TypeError $+        "Map function return type " ++ prettyTuple map_lam_ts+          ++ " wrong for given scan and reduction functions."  -- | Get Stream's accumulators as a sub-expression list getStreamAccums :: StreamForm lore -> [SubExp] getStreamAccums (Parallel _ _ _ accs) = accs-getStreamAccums (Sequential  accs) = accs+getStreamAccums (Sequential accs) = accs  instance OpMetrics (Op lore) => OpMetrics (SOAC lore) where   opMetrics (Stream _ _ lam _) =@@ -658,61 +840,77 @@   opMetrics (Hist _len ops bucket_fun _imgs) =     inside "Hist" $ mapM_ (lambdaMetrics . histOp) ops >> lambdaMetrics bucket_fun   opMetrics (Screma _ (ScremaForm scans reds map_lam) _) =-    inside "Screma" $ do mapM_ (lambdaMetrics . scanLambda) scans-                         mapM_ (lambdaMetrics . redLambda) reds-                         lambdaMetrics map_lam+    inside "Screma" $ do+      mapM_ (lambdaMetrics . scanLambda) scans+      mapM_ (lambdaMetrics . redLambda) reds+      lambdaMetrics map_lam  instance PrettyLore lore => PP.Pretty (SOAC lore) where   ppr (Stream size form lam arrs) =     case form of-       Parallel o comm lam0 acc ->-         let ord_str = if o == Disorder then "Per" else ""-             comm_str = case comm of Commutative -> "Comm"-                                     Noncommutative -> ""-         in  text ("streamPar"++ord_str++comm_str) <>-             parens (ppr size <> comma </> ppr lam0 </> comma </> ppr lam </>-                        commasep ( PP.braces (commasep $ map ppr acc) : map ppr arrs ))-       Sequential acc ->-             text "streamSeq" <>-             parens (ppr size <> comma </> ppr lam <> comma </>-                        commasep ( PP.braces (commasep $ map ppr acc) : map ppr arrs ))+      Parallel o comm lam0 acc ->+        let ord_str = if o == Disorder then "Per" else ""+            comm_str = case comm of+              Commutative -> "Comm"+              Noncommutative -> ""+         in text ("streamPar" ++ ord_str ++ comm_str)+              <> parens+                ( ppr size <> comma </> ppr lam0 </> comma </> ppr lam+                    </> commasep (PP.braces (commasep $ map ppr acc) : map ppr arrs)+                )+      Sequential acc ->+        text "streamSeq"+          <> parens+            ( ppr size <> comma </> ppr lam <> comma+                </> commasep (PP.braces (commasep $ map ppr acc) : map ppr arrs)+            )   ppr (Scatter len lam ivs as) =-    ppSOAC "scatter" len [lam] (Just (map Var ivs)) (map (\(_,n,a) -> (n,a)) as)+    ppSOAC "scatter" len [lam] (Just (map Var ivs)) (map (\(_, n, a) -> (n, a)) as)   ppr (Hist len ops bucket_fun imgs) =     ppHist len ops bucket_fun imgs   ppr (Screma w (ScremaForm scans reds map_lam) arrs)-    | null scans, null reds =-        text "map" <>-        parens (ppr w <> comma </>-                ppr map_lam <> comma </>-                commasep (map ppr arrs))-+    | null scans,+      null reds =+      text "map"+        <> parens+          ( ppr w <> comma+              </> ppr map_lam <> comma+              </> commasep (map ppr arrs)+          )     | null scans =-        text "redomap" <>-        parens (ppr w <> comma </>-                PP.braces (mconcat $ intersperse (comma <> PP.line) $ map ppr reds) <> comma </>-                ppr map_lam <> comma </>-                commasep (map ppr arrs))-+      text "redomap"+        <> parens+          ( ppr w <> comma+              </> PP.braces (mconcat $ intersperse (comma <> PP.line) $ map ppr reds) <> comma+              </> ppr map_lam <> comma+              </> commasep (map ppr arrs)+          )     | null reds =-        text "scanomap" <>-        parens (ppr w <> comma </>-                PP.braces (mconcat $ intersperse (comma <> PP.line) $ map ppr scans) <> comma </>-                ppr map_lam <> comma </>-                commasep (map ppr arrs))-+      text "scanomap"+        <> parens+          ( ppr w <> comma+              </> PP.braces (mconcat $ intersperse (comma <> PP.line) $ map ppr scans) <> comma+              </> ppr map_lam <> comma+              </> commasep (map ppr arrs)+          )   ppr (Screma w form arrs) = ppScrema w form arrs  -- | Prettyprint the given Screma.-ppScrema :: (PrettyLore lore, Pretty inp) =>-            SubExp -> ScremaForm lore -> [inp] -> Doc+ppScrema ::+  (PrettyLore lore, Pretty inp) =>+  SubExp ->+  ScremaForm lore ->+  [inp] ->+  Doc ppScrema w (ScremaForm scans reds map_lam) arrs =-  text "screma" <>-  parens (ppr w <> comma </>-          PP.braces (mconcat $ intersperse (comma <> PP.line) $ map ppr scans) <> comma </>-          PP.braces (mconcat $ intersperse (comma <> PP.line) $ map ppr reds) <> comma </>-          ppr map_lam <> comma </>-          commasep (map ppr arrs))+  text "screma"+    <> parens+      ( ppr w <> comma+          </> PP.braces (mconcat $ intersperse (comma <> PP.line) $ map ppr scans) <> comma+          </> PP.braces (mconcat $ intersperse (comma <> PP.line) $ map ppr reds) <> comma+          </> ppr map_lam <> comma+          </> commasep (map ppr arrs)+      )  instance PrettyLore lore => Pretty (Scan lore) where   ppr (Scan scan_lam scan_nes) =@@ -724,31 +922,50 @@  instance PrettyLore lore => Pretty (Reduce lore) where   ppr (Reduce comm red_lam red_nes) =-    ppComm comm <> ppr red_lam <> comma </>-    PP.braces (commasep $ map ppr red_nes)+    ppComm comm <> ppr red_lam <> comma+      </> PP.braces (commasep $ map ppr red_nes)  -- | Prettyprint the given histogram operation.-ppHist :: (PrettyLore lore, Pretty inp) =>-          SubExp -> [HistOp lore] -> Lambda lore -> [inp] -> Doc+ppHist ::+  (PrettyLore lore, Pretty inp) =>+  SubExp ->+  [HistOp lore] ->+  Lambda lore ->+  [inp] ->+  Doc ppHist len ops bucket_fun imgs =-  text "hist" <>-  parens (ppr len <> comma </>-          PP.braces (mconcat $ intersperse (comma <> PP.line) $ map ppOp ops) <> comma </>-          ppr bucket_fun <> comma </>-          commasep (map ppr imgs))-  where ppOp (HistOp w rf dests nes op) =-          ppr w <> comma <+> ppr rf <> comma <+> PP.braces (commasep $ map ppr dests) <> comma </>-          PP.braces (commasep $ map ppr nes) <> comma </> ppr op+  text "hist"+    <> parens+      ( ppr len <> comma+          </> PP.braces (mconcat $ intersperse (comma <> PP.line) $ map ppOp ops) <> comma+          </> ppr bucket_fun <> comma+          </> commasep (map ppr imgs)+      )+  where+    ppOp (HistOp w rf dests nes op) =+      ppr w <> comma <+> ppr rf <> comma <+> PP.braces (commasep $ map ppr dests) <> comma+        </> PP.braces (commasep $ map ppr nes) <> comma+        </> ppr op -ppSOAC :: (Pretty fn, Pretty v) =>-          String -> SubExp -> [fn] -> Maybe [SubExp] -> [v] -> Doc+ppSOAC ::+  (Pretty fn, Pretty v) =>+  String ->+  SubExp ->+  [fn] ->+  Maybe [SubExp] ->+  [v] ->+  Doc ppSOAC name size funs es as =-  text name <> parens (ppr size <> comma </>-                       ppList funs </>-                       commasep (es' ++ map ppr as))-  where es' = maybe [] ((:[]) . ppTuple') es+  text name+    <> parens+      ( ppr size <> comma+          </> ppList funs+          </> commasep (es' ++ map ppr as)+      )+  where+    es' = maybe [] ((: []) . ppTuple') es  ppList :: Pretty a => [a] -> Doc ppList as = case map ppr as of-              []     -> mempty-              a':as' -> foldl (</>) (a' <> comma) $ map (<> comma) as'+  [] -> mempty+  a' : as' -> foldl (</>) (a' <> comma) $ map (<> comma) as'
src/Futhark/IR/SOACS/Simplify.hs view
@@ -1,810 +1,945 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ScopedTypeVariables #-}-module Futhark.IR.SOACS.Simplify-       ( simplifySOACS-       , simplifyLambda-       , simplifyFun-       , simplifyStms-       , simplifyConsts--       , simpleSOACS-       , simplifySOAC--       , soacRules--       , HasSOAC(..)-       , simplifyKnownIterationSOAC-       , removeReplicateMapping-       , liftIdentityMapping--       , SOACS-       )-where--import Control.Monad-import Control.Monad.Identity-import Control.Monad.State-import Control.Monad.Writer-import Data.Foldable-import Data.Either-import Data.List (partition, transpose, unzip6, zip6)-import Data.Maybe-import qualified Data.Map.Strict as M-import qualified Data.Set      as S--import Futhark.IR.SOACS-import qualified Futhark.IR as AST-import Futhark.IR.Prop.Aliases-import qualified Futhark.Optimise.Simplify.Engine as Engine-import qualified Futhark.Optimise.Simplify as Simplify-import Futhark.Optimise.Simplify.Rules-import Futhark.MonadFreshNames-import Futhark.Optimise.Simplify.Rule-import Futhark.Optimise.Simplify.ClosedForm-import Futhark.Optimise.Simplify.Lore-import Futhark.Tools-import Futhark.Pass-import qualified Futhark.Analysis.SymbolTable as ST-import qualified Futhark.Analysis.UsageTable as UT-import Futhark.Analysis.DataDependencies-import Futhark.Transform.Rename-import Futhark.Util--simpleSOACS :: Simplify.SimpleOps SOACS-simpleSOACS = Simplify.bindableSimpleOps simplifySOAC--simplifySOACS :: Prog SOACS -> PassM (Prog SOACS)-simplifySOACS =-  Simplify.simplifyProg simpleSOACS soacRules Engine.noExtraHoistBlockers--simplifyFun :: MonadFreshNames m =>-               ST.SymbolTable (Wise SOACS) -> FunDef SOACS -> m (FunDef SOACS)-simplifyFun =-  Simplify.simplifyFun simpleSOACS soacRules Engine.noExtraHoistBlockers--simplifyLambda :: (HasScope SOACS m, MonadFreshNames m) =>-                  Lambda -> m Lambda-simplifyLambda =-  Simplify.simplifyLambda simpleSOACS soacRules Engine.noExtraHoistBlockers--simplifyStms :: (HasScope SOACS m, MonadFreshNames m) =>-                Stms SOACS -> m (ST.SymbolTable (Wise SOACS), Stms SOACS)-simplifyStms stms = do-  scope <- askScope-  Simplify.simplifyStms simpleSOACS soacRules Engine.noExtraHoistBlockers-    scope stms--simplifyConsts :: MonadFreshNames m =>-                  Stms SOACS -> m (ST.SymbolTable (Wise SOACS), Stms SOACS)-simplifyConsts =-  Simplify.simplifyStms simpleSOACS soacRules Engine.noExtraHoistBlockers mempty--simplifySOAC :: Simplify.SimplifiableLore lore =>-                Simplify.SimplifyOp lore (SOAC lore)-simplifySOAC (Stream outerdim form lam arr) = do-  outerdim' <- Engine.simplify outerdim-  (form', form_hoisted) <- simplifyStreamForm form-  arr' <- mapM Engine.simplify arr-  (lam', lam_hoisted) <- Engine.simplifyLambda lam-  return (Stream outerdim' form' lam' arr', form_hoisted <> lam_hoisted)-  where simplifyStreamForm (Parallel o comm lam0 acc) = do-          acc'  <- mapM Engine.simplify acc-          (lam0', hoisted) <- Engine.simplifyLambda lam0-          return (Parallel o comm lam0' acc', hoisted)-        simplifyStreamForm (Sequential acc) = do-          acc' <- mapM Engine.simplify acc-          return (Sequential acc', mempty)--simplifySOAC (Scatter len lam ivs as) = do-  len' <- Engine.simplify len-  (lam', hoisted) <- Engine.simplifyLambda lam-  ivs' <- mapM Engine.simplify ivs-  as' <- mapM Engine.simplify as-  return (Scatter len' lam' ivs' as', hoisted)--simplifySOAC (Hist w ops bfun imgs) = do-  w' <- Engine.simplify w-  (ops', hoisted) <- fmap unzip $ forM ops $ \(HistOp dests_w rf dests nes op) -> do-    dests_w' <- Engine.simplify dests_w-    rf' <- Engine.simplify rf-    dests' <- Engine.simplify dests-    nes' <- mapM Engine.simplify nes-    (op', hoisted) <- Engine.simplifyLambda op-    return (HistOp dests_w' rf' dests' nes' op', hoisted)-  imgs'  <- mapM Engine.simplify imgs-  (bfun', bfun_hoisted) <- Engine.simplifyLambda bfun-  return (Hist w' ops' bfun' imgs', mconcat hoisted <> bfun_hoisted)--simplifySOAC (Screma w (ScremaForm scans reds map_lam) arrs) = do-  (scans', scans_hoisted) <- fmap unzip $ forM scans $ \(Scan lam nes) -> do-    (lam', hoisted) <- Engine.simplifyLambda lam-    nes' <- Engine.simplify nes-    return (Scan lam' nes', hoisted)--  (reds', reds_hoisted) <- fmap unzip $ forM reds $ \(Reduce comm lam nes) -> do-    (lam', hoisted) <- Engine.simplifyLambda lam-    nes' <- Engine.simplify nes-    return (Reduce comm lam' nes', hoisted)--  (map_lam', map_lam_hoisted) <- Engine.simplifyLambda map_lam--  (,) <$> (Screma <$> Engine.simplify w <*>-           pure (ScremaForm scans' reds' map_lam') <*>-            Engine.simplify arrs) <*>-    pure (mconcat scans_hoisted <> mconcat reds_hoisted <> map_lam_hoisted)--instance BinderOps (Wise SOACS) where--fixLambdaParams :: (MonadBinder m, Bindable (Lore m), BinderOps (Lore m)) =>-                   AST.Lambda (Lore m) -> [Maybe SubExp] -> m (AST.Lambda (Lore m))-fixLambdaParams lam fixes = do-  body <- runBodyBinder $ localScope (scopeOfLParams $ lambdaParams lam) $ do-    zipWithM_ maybeFix (lambdaParams lam) fixes'-    return $ lambdaBody lam-  return lam { lambdaBody = body-             , lambdaParams = map fst $ filter (isNothing . snd) $-                              zip (lambdaParams lam) fixes' }-  where fixes' = fixes ++ repeat Nothing-        maybeFix p (Just x) = letBindNames [paramName p] $ BasicOp $ SubExp x-        maybeFix _ Nothing = return ()--removeLambdaResults :: [Bool] -> AST.Lambda lore -> AST.Lambda lore-removeLambdaResults keep lam = lam { lambdaBody = lam_body'-                                   , lambdaReturnType = ret }-  where keep' :: [a] -> [a]-        keep' = map snd . filter fst . zip (keep ++ repeat True)-        lam_body = lambdaBody lam-        lam_body' = lam_body { bodyResult = keep' $ bodyResult lam_body }-        ret = keep' $ lambdaReturnType lam--soacRules :: RuleBook (Wise SOACS)-soacRules = standardRules <> ruleBook topDownRules bottomUpRules---- | Does this lore contain 'SOAC's in its t'Op's?  A lore must be an--- instance of this class for the simplification rules to work.-class HasSOAC lore where-  asSOAC :: Op lore -> Maybe (SOAC lore)-  soacOp :: SOAC lore -> Op lore--instance HasSOAC (Wise SOACS) where-  asSOAC = Just-  soacOp = id--topDownRules :: [TopDownRule (Wise SOACS)]-topDownRules = [RuleOp hoistCertificates,-                RuleOp removeReplicateMapping,-                RuleOp removeReplicateWrite,-                RuleOp removeUnusedSOACInput,-                RuleOp simplifyClosedFormReduce,-                RuleOp simplifyKnownIterationSOAC,-                RuleOp fuseConcatScatter,-                RuleOp simplifyMapIota,-                RuleOp moveTransformToInput-               ]--bottomUpRules :: [BottomUpRule (Wise SOACS)]-bottomUpRules = [RuleOp removeDeadMapping,-                 RuleOp removeDeadReduction,-                 RuleOp removeDeadWrite,-                 RuleBasicOp removeUnnecessaryCopy,-                 RuleOp liftIdentityMapping,-                 RuleOp liftIdentityStreaming,-                 RuleOp removeDuplicateMapOutput,-                 RuleOp mapOpToOp-                ]---- Any certificates attached to a trivial Stm in the body might as--- well be applied to the SOAC itself.-hoistCertificates :: TopDownRuleOp (Wise SOACS)-hoistCertificates vtable pat aux soac-  | (soac', hoisted) <- runState (mapSOACM mapper soac) mempty,-    hoisted /= mempty =-      Simplify $ auxing aux $ certifying hoisted $ letBind pat $ Op soac'-  where mapper = identitySOACMapper { mapOnSOACLambda = onLambda }-        onLambda lam = do-          stms' <- mapM onStm $ bodyStms $ lambdaBody lam-          return lam { lambdaBody =-                       mkBody stms' $ bodyResult $ lambdaBody lam }-        onStm (Let se_pat se_aux (BasicOp (SubExp se))) = do-          let (invariant, variant) =-                partition (`ST.elem` vtable) $-                unCertificates $ stmAuxCerts se_aux-              se_aux' = se_aux { stmAuxCerts = Certificates variant }-          modify (Certificates invariant<>)-          return $ Let se_pat se_aux' $ BasicOp $ SubExp se-        onStm stm = return stm-hoistCertificates _ _ _ _ =-  Skip--liftIdentityMapping :: forall lore.-                       (Bindable lore, Simplify.SimplifiableLore lore, HasSOAC (Wise lore)) =>-                       BottomUpRuleOp (Wise lore)-liftIdentityMapping (_, usages) pat aux op-  | Just (Screma w form arrs :: SOAC (Wise lore)) <- asSOAC op,-    Just fun <- isMapSOAC form = do-  let inputMap = M.fromList $ zip (map paramName $ lambdaParams fun) arrs-      free = freeIn $ lambdaBody fun-      rettype = lambdaReturnType fun-      ses = bodyResult $ lambdaBody fun--      freeOrConst (Var v)    = v `nameIn` free-      freeOrConst Constant{} = True--      checkInvariance (outId, Var v, _) (invariant, mapresult, rettype')-        | Just inp <- M.lookup v inputMap =-            let e | patElemName outId `UT.isConsumed` usages-                    || inp `UT.isConsumed` usages =-                      Copy inp-                  | otherwise =-                      SubExp $ Var inp-            in ((Pattern [] [outId], BasicOp e) : invariant,-                mapresult,-                rettype')-      checkInvariance (outId, e, t) (invariant, mapresult, rettype')-        | freeOrConst e = ((Pattern [] [outId], BasicOp $ Replicate (Shape [w]) e) : invariant,-                           mapresult,-                           rettype')-        | otherwise = (invariant,-                       (outId, e) : mapresult,-                       t : rettype')--  case foldr checkInvariance ([], [], []) $-       zip3 (patternElements pat) ses rettype of-    ([], _, _) -> Skip-    (invariant, mapresult, rettype') -> Simplify $ do-      let (pat', ses') = unzip mapresult-          fun' = fun { lambdaBody = (lambdaBody fun) { bodyResult = ses' }-                     , lambdaReturnType = rettype'-                     }-      mapM_ (uncurry letBind) invariant-      auxing aux $-        letBindNames (map patElemName pat') $ Op $ soacOp $ Screma w (mapSOAC fun') arrs-liftIdentityMapping _ _ _ _ = Skip--liftIdentityStreaming :: BottomUpRuleOp (Wise SOACS)-liftIdentityStreaming _ (Pattern [] pes) aux (Stream w form lam arrs)-  | (variant_map, invariant_map) <--      partitionEithers $ map isInvariantRes $ zip3 map_ts map_pes map_res,-    not $ null invariant_map = Simplify $ do--      forM_ invariant_map $ \(pe, arr) ->-        letBind (Pattern [] [pe]) $ BasicOp $ Copy arr--      let (variant_map_ts, variant_map_pes, variant_map_res) = unzip3 variant_map-          lam' = lam { lambdaBody = (lambdaBody lam) { bodyResult = fold_res ++ variant_map_res }-                     , lambdaReturnType = fold_ts ++ variant_map_ts }--      auxing aux $ letBind (Pattern [] $ fold_pes ++ variant_map_pes) $-        Op $ Stream w form lam' arrs-  where num_folds = length $ getStreamAccums form-        (fold_pes, map_pes) = splitAt num_folds pes-        (fold_ts, map_ts) = splitAt num_folds $ lambdaReturnType lam-        lam_res = bodyResult $ lambdaBody lam-        (fold_res, map_res) = splitAt num_folds lam_res-        params_to_arrs = zip (map paramName $ drop (1 + num_folds) $ lambdaParams lam) arrs--        isInvariantRes (_, pe, Var v)-          | Just arr <- lookup v params_to_arrs =-              Right (pe, arr)-        isInvariantRes x =-          Left x-liftIdentityStreaming _ _ _ _ = Skip---- | Remove all arguments to the map that are simply replicates.--- These can be turned into free variables instead.-removeReplicateMapping :: (Bindable lore, Simplify.SimplifiableLore lore, HasSOAC (Wise lore)) =>-                          TopDownRuleOp (Wise lore)-removeReplicateMapping vtable pat aux op-  | Just (Screma w form arrs) <- asSOAC op,-    Just fun <- isMapSOAC form,-    Just (bnds, fun', arrs') <- removeReplicateInput vtable fun arrs = Simplify $ do-      forM_ bnds $ \(vs,cs,e) -> certifying cs $ letBindNames vs e-      auxing aux $ letBind pat $ Op $ soacOp $ Screma w (mapSOAC fun') arrs'-removeReplicateMapping _ _ _ _ = Skip---- | Like 'removeReplicateMapping', but for 'Scatter'.-removeReplicateWrite :: TopDownRuleOp (Wise SOACS)-removeReplicateWrite vtable pat aux (Scatter len lam ivs as)-  | Just (bnds, lam', ivs') <- removeReplicateInput vtable lam ivs = Simplify $ do-      forM_ bnds $ \(vs,cs,e) -> certifying cs $ letBindNames vs e-      auxing aux $ letBind pat $ Op $ Scatter len lam' ivs' as-removeReplicateWrite _ _ _ _ = Skip--removeReplicateInput :: Aliased lore =>-                        ST.SymbolTable lore-                     -> AST.Lambda lore -> [VName]-                     -> Maybe ([([VName], Certificates, AST.Exp lore)],-                                AST.Lambda lore, [VName])-removeReplicateInput vtable fun arrs-  | not $ null parameterBnds = do-  let (arr_params', arrs') = unzip params_and_arrs-      fun' = fun { lambdaParams = acc_params <> arr_params' }-  return (parameterBnds, fun', arrs')-  | otherwise = Nothing--  where params = lambdaParams fun-        (acc_params, arr_params) =-          splitAt (length params - length arrs) params-        (params_and_arrs, parameterBnds) =-          partitionEithers $ zipWith isReplicateAndNotConsumed arr_params arrs--        isReplicateAndNotConsumed p v-          | Just (BasicOp (Replicate (Shape (_:ds)) e), v_cs) <--              ST.lookupExp v vtable,-            not $ paramName p `nameIn` consumedByLambda fun =-              Right ([paramName p],-                     v_cs,-                     case ds of-                       [] -> BasicOp $ SubExp e-                       _  -> BasicOp $ Replicate (Shape ds) e)-          | otherwise =-              Left (p, v)---- | Remove inputs that are not used inside the SOAC.-removeUnusedSOACInput :: TopDownRuleOp (Wise SOACS)-removeUnusedSOACInput _ pat aux (Screma w (ScremaForm scan reduce map_lam) arrs)-  | (used,unused) <- partition usedInput params_and_arrs,-    not (null unused) = Simplify $ do-      let (used_params, used_arrs) = unzip used-          map_lam' = map_lam { lambdaParams = used_params }-      auxing aux $ letBind pat $ Op $ Screma w (ScremaForm scan reduce map_lam') used_arrs-  where params_and_arrs = zip (lambdaParams map_lam) arrs-        used_in_body = freeIn $ lambdaBody map_lam-        usedInput (param, _) = paramName param `nameIn` used_in_body-removeUnusedSOACInput _ _ _ _ = Skip--removeDeadMapping :: BottomUpRuleOp (Wise SOACS)-removeDeadMapping (_, used) pat aux (Screma w form arrs)-  | Just fun <- isMapSOAC form =-  let ses = bodyResult $ lambdaBody fun-      isUsed (bindee, _, _) = (`UT.used` used) $ patElemName bindee-      (pat',ses', ts') = unzip3 $ filter isUsed $-                         zip3 (patternElements pat) ses $ lambdaReturnType fun-      fun' = fun { lambdaBody = (lambdaBody fun) { bodyResult = ses' }-                 , lambdaReturnType = ts'-                 }-  in if pat /= Pattern [] pat'-     then Simplify $ auxing aux $-          letBind (Pattern [] pat') $ Op $ Screma w (mapSOAC fun') arrs-     else Skip-removeDeadMapping _ _ _ _ = Skip--removeDuplicateMapOutput :: BottomUpRuleOp (Wise SOACS)-removeDuplicateMapOutput (_, used) pat aux (Screma w form arrs)-  | Just fun <- isMapSOAC form =-  let ses = bodyResult $ lambdaBody fun-      ts = lambdaReturnType fun-      pes = patternValueElements pat-      ses_ts_pes = zip3 ses ts pes-      (ses_ts_pes', copies) =-        foldl checkForDuplicates (mempty,mempty) ses_ts_pes-  in if null copies then Skip-     else Simplify $ do-       let (ses', ts', pes') = unzip3 ses_ts_pes'-           pat' = Pattern [] pes'-           fun' = fun { lambdaBody = (lambdaBody fun) { bodyResult = ses' }-                      , lambdaReturnType = ts' }-       auxing aux $ letBind pat' $ Op $ Screma w (mapSOAC fun') arrs-       forM_ copies $ \(from,to) ->-         if UT.isConsumed (patElemName to) used then-           letBind (Pattern [] [to]) $ BasicOp $ Copy $ patElemName from-         else-           letBind (Pattern [] [to]) $ BasicOp $ SubExp $ Var $ patElemName from-  where checkForDuplicates (ses_ts_pes',copies) (se,t,pe)-          | Just (_,_,pe') <- find (\(x,_,_) -> x == se) ses_ts_pes' =-              -- This subexp has been returned before, producing the-              -- array pe'.-              (ses_ts_pes', (pe', pe) : copies)-          | otherwise = (ses_ts_pes' ++ [(se,t,pe)], copies)-removeDuplicateMapOutput _ _ _ _ = Skip---- Mapping some operations becomes an extension of that operation.-mapOpToOp :: BottomUpRuleOp (Wise SOACS)--mapOpToOp (_, used) pat aux1 e-  | Just (map_pe, cs, w, BasicOp (Reshape newshape reshape_arr), [p], [arr]) <--      isMapWithOp pat e,-    paramName p == reshape_arr,-    not $ UT.isConsumed (patElemName map_pe) used = Simplify $ do-      let redim | isJust $ shapeCoercion newshape = DimCoercion w-                | otherwise                       = DimNew w-      certifying (stmAuxCerts aux1 <> cs) $ letBind pat $-        BasicOp $ Reshape (redim : newshape) arr--  | Just (_, cs, _,-          BasicOp (Concat d arr arrs dw), ps, outer_arr : outer_arrs) <--      isMapWithOp pat e,-    (arr:arrs) == map paramName ps =-      Simplify $ certifying (stmAuxCerts aux1 <> cs) $ letBind pat $-      BasicOp $ Concat (d+1) outer_arr outer_arrs dw--  | Just (map_pe, cs, _,-          BasicOp (Rearrange perm rearrange_arr), [p], [arr]) <--      isMapWithOp pat e,-    paramName p == rearrange_arr,-    not $ UT.isConsumed (patElemName map_pe) used =-      Simplify $ certifying (stmAuxCerts aux1 <> cs) $ letBind pat $-      BasicOp $ Rearrange (0 : map (1+) perm) arr--  | Just (map_pe, cs, _, BasicOp (Rotate rots rotate_arr), [p], [arr]) <--      isMapWithOp pat e,-    paramName p == rotate_arr,-    not $ UT.isConsumed (patElemName map_pe) used =-      Simplify $ certifying (stmAuxCerts aux1 <> cs) $ letBind pat $-      BasicOp $ Rotate (intConst Int32 0 : rots) arr--mapOpToOp _ _ _ _ = Skip--isMapWithOp :: PatternT dec-            -> SOAC (Wise SOACS)-            -> Maybe (PatElemT dec, Certificates, SubExp,-                      AST.Exp (Wise SOACS), [Param Type], [VName])-isMapWithOp pat e-  | Pattern [] [map_pe] <- pat,-    Screma w form arrs <- e,-    Just map_lam <- isMapSOAC form,-    [Let (Pattern [] [pe]) aux2 e'] <--      stmsToList $ bodyStms $ lambdaBody map_lam,-    [Var r] <- bodyResult $ lambdaBody map_lam,-    r == patElemName pe =-      Just (map_pe, stmAuxCerts aux2, w, e', lambdaParams map_lam, arrs)-  | otherwise = Nothing---- | Some of the results of a reduction (or really: Redomap) may be--- dead.  We remove them here.  The trick is that we need to look at--- the data dependencies to see that the "dead" result is not--- actually used for computing one of the live ones.-removeDeadReduction :: BottomUpRuleOp (Wise SOACS)-removeDeadReduction (_, used) pat aux (Screma w form arrs)-  | Just ([Reduce comm redlam nes], maplam) <- isRedomapSOAC form,-    not $ all (`UT.used` used) $ patternNames pat, -- Quick/cheap check--    let (red_pes, map_pes) = splitAt (length nes) $ patternElements pat,-    let redlam_deps = dataDependencies $ lambdaBody redlam,-    let redlam_res = bodyResult $ lambdaBody redlam,-    let redlam_params = lambdaParams redlam,-    let used_after = map snd $ filter ((`UT.used` used) . patElemName . fst) $-                     zip red_pes redlam_params,-    let necessary = findNecessaryForReturned (`elem` used_after)-                    (zip redlam_params $ redlam_res <> redlam_res) redlam_deps,-    let alive_mask = map ((`nameIn` necessary) . paramName) redlam_params,--    not $ all (==True) alive_mask = Simplify $ do--  let fixDeadToNeutral lives ne = if lives then Nothing else Just ne-      dead_fix = zipWith fixDeadToNeutral alive_mask nes-      (used_red_pes, _, used_nes) =-        unzip3 $ filter (\(_,x,_) -> paramName x `nameIn` necessary) $-        zip3 red_pes redlam_params nes--  let maplam' = removeLambdaResults (take (length nes) alive_mask) maplam-  redlam' <- removeLambdaResults (take (length nes) alive_mask) <$> fixLambdaParams redlam (dead_fix++dead_fix)--  auxing aux $ letBind (Pattern [] $ used_red_pes ++ map_pes) $-    Op $ Screma w (redomapSOAC [Reduce comm redlam' used_nes] maplam') arrs--removeDeadReduction _ _ _ _ = Skip---- | If we are writing to an array that is never used, get rid of it.-removeDeadWrite :: BottomUpRuleOp (Wise SOACS)-removeDeadWrite (_, used) pat aux (Scatter w fun arrs dests) =-  let (i_ses, v_ses) = splitAt (length dests) $ bodyResult $ lambdaBody fun-      (i_ts, v_ts) = splitAt (length dests) $ lambdaReturnType fun-      isUsed (bindee, _, _, _, _, _) = (`UT.used` used) $ patElemName bindee-      (pat', i_ses', v_ses', i_ts', v_ts', dests') =-        unzip6 $ filter isUsed $-        zip6 (patternElements pat) i_ses v_ses i_ts v_ts dests-      fun' = fun { lambdaBody = (lambdaBody fun) { bodyResult = i_ses' ++ v_ses' }-                 , lambdaReturnType = i_ts' ++ v_ts'-                 }-  in if pat /= Pattern [] pat'-     then Simplify $ auxing aux $-          letBind (Pattern [] pat') $ Op $ Scatter w fun' arrs dests'-     else Skip-removeDeadWrite _ _ _ _ = Skip---- handles now concatenation of more than two arrays-fuseConcatScatter :: TopDownRuleOp (Wise SOACS)-fuseConcatScatter vtable pat _ (Scatter _ fun arrs dests)-  | Just (ws@(w':_), xss, css) <- unzip3 <$> mapM isConcat arrs,-    xivs <- transpose xss,-    all (w'==) ws = Simplify $ do-      let r = length xivs-      fun2s <- mapM (\_ -> renameLambda fun) [1 .. r-1]-      let fun_n = length $ lambdaReturnType fun-          (fun_is, fun_vs) = unzip $ map (splitAt (fun_n `div` 2) .-                             bodyResult . lambdaBody ) (fun:fun2s)-          (its, vts) = unzip $ replicate r $-                       splitAt (fun_n `div` 2) $ lambdaReturnType fun-          new_stmts  = mconcat $ map (bodyStms . lambdaBody) (fun:fun2s)-      let fun' = Lambda-                 { lambdaParams = mconcat $ map lambdaParams (fun:fun2s)-                 , lambdaBody = mkBody new_stmts $-                                mix fun_is <> mix fun_vs-                 , lambdaReturnType = mix its <> mix vts-                 }-      certifying (mconcat css) $-        letBind pat $ Op $ Scatter w' fun' (concat xivs) $ map (incWrites r) dests-  where sizeOf :: VName -> Maybe SubExp-        sizeOf x = arraySize 0 . typeOf <$> ST.lookup x vtable-        mix = concat . transpose-        incWrites r (w, n, a) = (w, n*r, a) -- ToDO: is it (n*r) or (n+r-1)??-        isConcat v = case ST.lookupExp v vtable of-          Just (BasicOp (Concat 0 x ys _), cs) -> do-            x_w <- sizeOf x-            y_ws<- mapM sizeOf ys-            guard $ all (x_w==) y_ws-            return (x_w, x:ys, cs)-          Just (BasicOp (Reshape reshape arr), cs) -> do-            guard $ isJust $ shapeCoercion reshape-            (a, b, cs') <- isConcat arr-            return (a, b, cs <> cs')-          _ -> Nothing--fuseConcatScatter _ _ _ _ = Skip--simplifyClosedFormReduce :: TopDownRuleOp (Wise SOACS)-simplifyClosedFormReduce _ pat _ (Screma (Constant w) form _)-  | Just nes <- concatMap redNeutral . fst <$> isRedomapSOAC form,-    zeroIsh w =-      Simplify $ forM_ (zip (patternNames pat) nes) $ \(v, ne) ->-      letBindNames [v] $ BasicOp $ SubExp ne-simplifyClosedFormReduce vtable pat _ (Screma _ form arrs)-  | Just [Reduce _ red_fun nes] <- isReduceSOAC form =-      Simplify $ foldClosedForm (`ST.lookupExp` vtable) pat red_fun nes arrs-simplifyClosedFormReduce _ _ _ _ = Skip---- For now we just remove singleton SOACs.-simplifyKnownIterationSOAC :: (Bindable lore, Simplify.SimplifiableLore lore, HasSOAC (Wise lore)) =>-                              TopDownRuleOp (Wise lore)-simplifyKnownIterationSOAC _ pat _ op-  | Just (Screma (Constant k) (ScremaForm scans reds map_lam) arrs) <- asSOAC op,-    oneIsh k = Simplify $ do--      let (Reduce _ red_lam red_nes) = singleReduce reds-          (Scan scan_lam scan_nes) = singleScan scans-          (scan_pes, red_pes, map_pes) = splitAt3 (length scan_nes) (length red_nes) $-                                         patternElements pat-          bindMapParam p a = do-            a_t <- lookupType a-            letBindNames [paramName p] $-              BasicOp $ Index a $ fullSlice a_t [DimFix $ constant (0::Int32)]-          bindArrayResult pe se =-            letBindNames [patElemName pe] $-            BasicOp $ ArrayLit [se] $ rowType $ patElemType pe-          bindResult pe se =-            letBindNames [patElemName pe] $ BasicOp $ SubExp se--      zipWithM_ bindMapParam (lambdaParams map_lam) arrs-      (to_scan, to_red, map_res) <- splitAt3 (length scan_nes) (length red_nes) <$>-                                    bodyBind (lambdaBody map_lam)-      scan_res <- eLambda scan_lam $ map eSubExp $ scan_nes ++ to_scan-      red_res <- eLambda red_lam $ map eSubExp $ red_nes ++ to_red--      zipWithM_ bindArrayResult scan_pes scan_res-      zipWithM_ bindResult red_pes red_res-      zipWithM_ bindArrayResult map_pes map_res--simplifyKnownIterationSOAC _ pat _ op-  | Just (Stream (Constant k) form fold_lam arrs) <- asSOAC op,-    oneIsh k = Simplify $ do-      let nes = getStreamAccums form-          (chunk_param, acc_params, slice_params) =-            partitionChunkedFoldParameters (length nes) (lambdaParams fold_lam)--      letBindNames [paramName chunk_param] $-        BasicOp $ SubExp $ intConst Int32 1--      forM_ (zip acc_params nes) $ \(p, ne) ->-        letBindNames [paramName p] $ BasicOp $ SubExp ne--      forM_ (zip slice_params arrs) $ \(p, arr) ->-        letBindNames [paramName p] $ BasicOp $ SubExp $ Var arr--      res <- bodyBind $ lambdaBody fold_lam--      forM_ (zip (patternNames pat) res) $ \(v, se) ->-        letBindNames [v] $ BasicOp $ SubExp se--simplifyKnownIterationSOAC _ _ _ _ = Skip--data ArrayOp = ArrayIndexing Certificates VName (Slice SubExp)-             | ArrayRearrange Certificates VName [Int]-             | ArrayRotate Certificates VName [SubExp]-             | ArrayVar Certificates VName -- ^ Never constructed.-  deriving (Eq, Ord, Show)--arrayOpArr :: ArrayOp -> VName-arrayOpArr (ArrayIndexing _ arr _) = arr-arrayOpArr (ArrayRearrange _ arr _) = arr-arrayOpArr (ArrayRotate _ arr _) = arr-arrayOpArr (ArrayVar _ arr) = arr--arrayOpCerts :: ArrayOp -> Certificates-arrayOpCerts (ArrayIndexing cs _ _) = cs-arrayOpCerts (ArrayRearrange cs _ _) = cs-arrayOpCerts (ArrayRotate cs _ _) = cs-arrayOpCerts (ArrayVar cs _) = cs--isArrayOp :: Certificates -> AST.Exp (Wise SOACS) -> Maybe ArrayOp-isArrayOp cs (BasicOp (Index arr slice)) =-  Just $ ArrayIndexing cs arr slice-isArrayOp cs (BasicOp (Rearrange perm arr)) =-  Just $ ArrayRearrange cs arr perm-isArrayOp cs (BasicOp (Rotate rots arr)) =-  Just $ ArrayRotate cs arr rots-isArrayOp _ _ =-  Nothing--fromArrayOp :: ArrayOp -> (Certificates, AST.Exp (Wise SOACS))-fromArrayOp (ArrayIndexing cs arr slice) = (cs, BasicOp $ Index arr slice)-fromArrayOp (ArrayRearrange cs arr perm) = (cs, BasicOp $ Rearrange perm arr)-fromArrayOp (ArrayRotate cs arr rots) = (cs, BasicOp $ Rotate rots arr)-fromArrayOp (ArrayVar cs arr) = (cs, BasicOp $ SubExp $ Var arr)--arrayOps :: AST.Body (Wise SOACS) -> S.Set (AST.Pattern (Wise SOACS), ArrayOp)-arrayOps = mconcat . map onStm . stmsToList . bodyStms-  where onStm (Let pat aux e) =-          case isArrayOp (stmAuxCerts aux) e of-            Just op -> S.singleton (pat, op)-            Nothing -> execState (walkExpM walker e) mempty-        onOp = execWriter . mapSOACM identitySOACMapper { mapOnSOACLambda = onLambda }-        onLambda lam = do tell $ arrayOps $ lambdaBody lam-                          return lam-        walker = identityWalker { walkOnBody = const $ modify . (<>) . arrayOps-                                , walkOnOp = modify . (<>) . onOp }--replaceArrayOps :: M.Map ArrayOp ArrayOp-                -> AST.Body (Wise SOACS) -> AST.Body (Wise SOACS)-replaceArrayOps substs (Body _ stms res) =-  mkBody (fmap onStm stms) res-  where onStm (Let pat aux e) =-          let (cs', e') = onExp (stmAuxCerts aux) e-          in certify cs' $-             mkLet' (patternContextIdents pat) (patternValueIdents pat) aux e'-        onExp cs e-          | Just op <- isArrayOp cs e,-            Just op' <- M.lookup op substs =-              fromArrayOp op'-        onExp cs e = (cs, mapExp mapper e)-        mapper = identityMapper { mapOnBody = const $ return . replaceArrayOps substs-                                , mapOnOp = return . onOp }-        onOp = runIdentity . mapSOACM identitySOACMapper { mapOnSOACLambda = return . onLambda }-        onLambda lam = lam { lambdaBody = replaceArrayOps substs $ lambdaBody lam }---- Turn------    map (\i -> ... xs[i] ...) (iota n)------ into------    map (\i x -> ... x ...) (iota n) xs------ This is not because we want to encourage the map-iota pattern, but--- it may be present in generated code.  This is an unfortunately--- expensive simplification rule, since it requires multiple passes--- over the entire lambda body.  It only handles the very simplest--- case - if you find yourself planning to extend it to handle more--- complex situations (rotate or whatnot), consider turning it into a--- separate compiler pass instead.-simplifyMapIota :: TopDownRuleOp (Wise SOACS)-simplifyMapIota vtable pat aux (Screma w (ScremaForm scan reduce map_lam) arrs)-  | Just (p, _) <- find isIota (zip (lambdaParams map_lam) arrs),-    indexings <- filter (indexesWith (paramName p)) $ map snd $ S.toList $-                 arrayOps $ lambdaBody map_lam,-    not $ null indexings = Simplify $ do-      -- For each indexing with iota, add the corresponding array to-      -- the Screma, and construct a new lambda parameter.-      (more_arrs, more_params, replacements) <--        unzip3 . catMaybes <$> mapM mapOverArr indexings-      let substs = M.fromList $ zip indexings replacements-          map_lam' = map_lam { lambdaParams = lambdaParams map_lam <> more_params-                             , lambdaBody = replaceArrayOps substs $-                                            lambdaBody map_lam-                             }--      auxing aux $-        letBind pat $ Op $ Screma w (ScremaForm scan reduce map_lam') (arrs <> more_arrs)-  where isIota (_, arr) = case ST.lookupBasicOp arr vtable of-                            Just (Iota _ (Constant o) (Constant s) _, _) ->-                              zeroIsh o && oneIsh s-                            _ -> False--        indexesWith v (ArrayIndexing cs arr (DimFix (Var i) : _))-          | arr `ST.elem` vtable,-            all (`ST.elem` vtable) $ unCertificates cs =-              i == v-        indexesWith _ _ = False--        mapOverArr (ArrayIndexing cs arr slice) = do-          arr_elem <- newVName $ baseString arr ++ "_elem"-          arr_t <- lookupType arr-          arr' <- if arraySize 0 arr_t == w-                  then return arr-                  else certifying cs $ letExp (baseString arr ++ "_prefix") $-                       BasicOp $ Index arr $-                       fullSlice arr_t [DimSlice (intConst Int32 0) w (intConst Int32 1)]-          return $ Just (arr',-                         Param arr_elem (rowType arr_t),-                         ArrayIndexing cs arr_elem (drop 1 slice))--        mapOverArr _ = return Nothing--simplifyMapIota  _ _ _ _ = Skip---- If a Screma's map function contains a transformation--- (e.g. transpose) on a parameter, create a new parameter--- corresponding to that transformation performed on the rows of the--- full array.-moveTransformToInput :: TopDownRuleOp (Wise SOACS)-moveTransformToInput vtable pat aux (Screma w (ScremaForm scan reduce map_lam) arrs)-  | ops <- map snd $ filter arrayIsMapParam $ S.toList $ arrayOps $ lambdaBody map_lam,-    not $ null ops = Simplify $ do-      (more_arrs, more_params, replacements) <--        unzip3 . catMaybes <$> mapM mapOverArr ops--      when (null more_arrs) cannotSimplify--      let substs = M.fromList $ zip ops replacements-          map_lam' = map_lam { lambdaParams = lambdaParams map_lam <> more_params-                             , lambdaBody = replaceArrayOps substs $-                                            lambdaBody map_lam-                             }--      auxing aux $-        letBind pat $ Op $ Screma w (ScremaForm scan reduce map_lam') (arrs <> more_arrs)--  where map_param_names = map paramName (lambdaParams map_lam)-        topLevelPattern = (`elem` fmap stmPattern (bodyStms (lambdaBody map_lam)))-        onlyUsedOnce arr =-          case filter ((arr `nameIn`) . freeIn) $ stmsToList $ bodyStms $ lambdaBody map_lam of-            _ : _ : _ -> False-            _ -> True--        -- It's not just about whether the array is a parameter;-        -- everything else must be map-invariant.-        arrayIsMapParam (pat', ArrayIndexing cs arr slice) =-          arr `elem` map_param_names &&-          all (`ST.elem` vtable) (namesToList $ freeIn cs <> freeIn slice) &&-          not (null slice) &&-          (not (null $ sliceDims slice) || (topLevelPattern pat' && onlyUsedOnce arr))-        arrayIsMapParam (_, ArrayRearrange cs arr perm) =-          arr `elem` map_param_names &&-          all (`ST.elem` vtable) (namesToList $ freeIn cs) &&-          not (null perm)-        arrayIsMapParam (_, ArrayRotate cs arr rots) =-          arr `elem` map_param_names &&-          all (`ST.elem` vtable) (namesToList $ freeIn cs <> freeIn rots)-        arrayIsMapParam (_, ArrayVar{}) =-          False--        mapOverArr op-         | Just (_, arr) <- find ((==arrayOpArr op) . fst) (zip map_param_names arrs) = do-             arr_t <- lookupType arr-             let whole_dim = DimSlice (intConst Int32 0) (arraySize 0 arr_t) (intConst Int32 1)-             arr_transformed <- certifying (arrayOpCerts op) $-                                letExp (baseString arr ++ "_transformed") $-                                case op of-                                  ArrayIndexing _ _ slice ->-                                    BasicOp $ Index arr $ whole_dim : slice-                                  ArrayRearrange _ _ perm ->-                                    BasicOp $ Rearrange (0 : map (+1) perm) arr-                                  ArrayRotate _ _ rots ->-                                    BasicOp $ Rotate (intConst Int32 0 : rots) arr-                                  ArrayVar{} ->-                                    BasicOp $ SubExp $ Var arr-             arr_transformed_t <- lookupType arr_transformed-             arr_transformed_row <- newVName $ baseString arr ++ "_transformed_row"-             return $ Just (arr_transformed,-                            Param arr_transformed_row (rowType arr_transformed_t),-                            ArrayVar mempty arr_transformed_row)--        mapOverArr _ = return Nothing-+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Futhark.IR.SOACS.Simplify+  ( simplifySOACS,+    simplifyLambda,+    simplifyFun,+    simplifyStms,+    simplifyConsts,+    simpleSOACS,+    simplifySOAC,+    soacRules,+    HasSOAC (..),+    simplifyKnownIterationSOAC,+    removeReplicateMapping,+    liftIdentityMapping,+    SOACS,+  )+where++import Control.Monad+import Control.Monad.Identity+import Control.Monad.State+import Control.Monad.Writer+import Data.Either+import Data.Foldable+import Data.List (partition, transpose, unzip6, zip6)+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Data.Set as S+import Futhark.Analysis.DataDependencies+import qualified Futhark.Analysis.SymbolTable as ST+import qualified Futhark.Analysis.UsageTable as UT+import qualified Futhark.IR as AST+import Futhark.IR.Prop.Aliases+import Futhark.IR.SOACS+import Futhark.MonadFreshNames+import qualified Futhark.Optimise.Simplify as Simplify+import Futhark.Optimise.Simplify.ClosedForm+import qualified Futhark.Optimise.Simplify.Engine as Engine+import Futhark.Optimise.Simplify.Lore+import Futhark.Optimise.Simplify.Rule+import Futhark.Optimise.Simplify.Rules+import Futhark.Pass+import Futhark.Tools+import Futhark.Transform.Rename+import Futhark.Util++simpleSOACS :: Simplify.SimpleOps SOACS+simpleSOACS = Simplify.bindableSimpleOps simplifySOAC++simplifySOACS :: Prog SOACS -> PassM (Prog SOACS)+simplifySOACS =+  Simplify.simplifyProg simpleSOACS soacRules Engine.noExtraHoistBlockers++simplifyFun ::+  MonadFreshNames m =>+  ST.SymbolTable (Wise SOACS) ->+  FunDef SOACS ->+  m (FunDef SOACS)+simplifyFun =+  Simplify.simplifyFun simpleSOACS soacRules Engine.noExtraHoistBlockers++simplifyLambda ::+  (HasScope SOACS m, MonadFreshNames m) =>+  Lambda ->+  m Lambda+simplifyLambda =+  Simplify.simplifyLambda simpleSOACS soacRules Engine.noExtraHoistBlockers++simplifyStms ::+  (HasScope SOACS m, MonadFreshNames m) =>+  Stms SOACS ->+  m (ST.SymbolTable (Wise SOACS), Stms SOACS)+simplifyStms stms = do+  scope <- askScope+  Simplify.simplifyStms+    simpleSOACS+    soacRules+    Engine.noExtraHoistBlockers+    scope+    stms++simplifyConsts ::+  MonadFreshNames m =>+  Stms SOACS ->+  m (ST.SymbolTable (Wise SOACS), Stms SOACS)+simplifyConsts =+  Simplify.simplifyStms simpleSOACS soacRules Engine.noExtraHoistBlockers mempty++simplifySOAC ::+  Simplify.SimplifiableLore lore =>+  Simplify.SimplifyOp lore (SOAC lore)+simplifySOAC (Stream outerdim form lam arr) = do+  outerdim' <- Engine.simplify outerdim+  (form', form_hoisted) <- simplifyStreamForm form+  arr' <- mapM Engine.simplify arr+  (lam', lam_hoisted) <- Engine.simplifyLambda lam+  return (Stream outerdim' form' lam' arr', form_hoisted <> lam_hoisted)+  where+    simplifyStreamForm (Parallel o comm lam0 acc) = do+      acc' <- mapM Engine.simplify acc+      (lam0', hoisted) <- Engine.simplifyLambda lam0+      return (Parallel o comm lam0' acc', hoisted)+    simplifyStreamForm (Sequential acc) = do+      acc' <- mapM Engine.simplify acc+      return (Sequential acc', mempty)+simplifySOAC (Scatter len lam ivs as) = do+  len' <- Engine.simplify len+  (lam', hoisted) <- Engine.simplifyLambda lam+  ivs' <- mapM Engine.simplify ivs+  as' <- mapM Engine.simplify as+  return (Scatter len' lam' ivs' as', hoisted)+simplifySOAC (Hist w ops bfun imgs) = do+  w' <- Engine.simplify w+  (ops', hoisted) <- fmap unzip $+    forM ops $ \(HistOp dests_w rf dests nes op) -> do+      dests_w' <- Engine.simplify dests_w+      rf' <- Engine.simplify rf+      dests' <- Engine.simplify dests+      nes' <- mapM Engine.simplify nes+      (op', hoisted) <- Engine.simplifyLambda op+      return (HistOp dests_w' rf' dests' nes' op', hoisted)+  imgs' <- mapM Engine.simplify imgs+  (bfun', bfun_hoisted) <- Engine.simplifyLambda bfun+  return (Hist w' ops' bfun' imgs', mconcat hoisted <> bfun_hoisted)+simplifySOAC (Screma w (ScremaForm scans reds map_lam) arrs) = do+  (scans', scans_hoisted) <- fmap unzip $+    forM scans $ \(Scan lam nes) -> do+      (lam', hoisted) <- Engine.simplifyLambda lam+      nes' <- Engine.simplify nes+      return (Scan lam' nes', hoisted)++  (reds', reds_hoisted) <- fmap unzip $+    forM reds $ \(Reduce comm lam nes) -> do+      (lam', hoisted) <- Engine.simplifyLambda lam+      nes' <- Engine.simplify nes+      return (Reduce comm lam' nes', hoisted)++  (map_lam', map_lam_hoisted) <- Engine.simplifyLambda map_lam++  (,)+    <$> ( Screma <$> Engine.simplify w+            <*> pure (ScremaForm scans' reds' map_lam')+            <*> Engine.simplify arrs+        )+    <*> pure (mconcat scans_hoisted <> mconcat reds_hoisted <> map_lam_hoisted)++instance BinderOps (Wise SOACS)++fixLambdaParams ::+  (MonadBinder m, Bindable (Lore m), BinderOps (Lore m)) =>+  AST.Lambda (Lore m) ->+  [Maybe SubExp] ->+  m (AST.Lambda (Lore m))+fixLambdaParams lam fixes = do+  body <- runBodyBinder $+    localScope (scopeOfLParams $ lambdaParams lam) $ do+      zipWithM_ maybeFix (lambdaParams lam) fixes'+      return $ lambdaBody lam+  return+    lam+      { lambdaBody = body,+        lambdaParams =+          map fst $+            filter (isNothing . snd) $+              zip (lambdaParams lam) fixes'+      }+  where+    fixes' = fixes ++ repeat Nothing+    maybeFix p (Just x) = letBindNames [paramName p] $ BasicOp $ SubExp x+    maybeFix _ Nothing = return ()++removeLambdaResults :: [Bool] -> AST.Lambda lore -> AST.Lambda lore+removeLambdaResults keep lam =+  lam+    { lambdaBody = lam_body',+      lambdaReturnType = ret+    }+  where+    keep' :: [a] -> [a]+    keep' = map snd . filter fst . zip (keep ++ repeat True)+    lam_body = lambdaBody lam+    lam_body' = lam_body {bodyResult = keep' $ bodyResult lam_body}+    ret = keep' $ lambdaReturnType lam++soacRules :: RuleBook (Wise SOACS)+soacRules = standardRules <> ruleBook topDownRules bottomUpRules++-- | Does this lore contain 'SOAC's in its t'Op's?  A lore must be an+-- instance of this class for the simplification rules to work.+class HasSOAC lore where+  asSOAC :: Op lore -> Maybe (SOAC lore)+  soacOp :: SOAC lore -> Op lore++instance HasSOAC (Wise SOACS) where+  asSOAC = Just+  soacOp = id++topDownRules :: [TopDownRule (Wise SOACS)]+topDownRules =+  [ RuleOp hoistCertificates,+    RuleOp removeReplicateMapping,+    RuleOp removeReplicateWrite,+    RuleOp removeUnusedSOACInput,+    RuleOp simplifyClosedFormReduce,+    RuleOp simplifyKnownIterationSOAC,+    RuleOp fuseConcatScatter,+    RuleOp simplifyMapIota,+    RuleOp moveTransformToInput+  ]++bottomUpRules :: [BottomUpRule (Wise SOACS)]+bottomUpRules =+  [ RuleOp removeDeadMapping,+    RuleOp removeDeadReduction,+    RuleOp removeDeadWrite,+    RuleBasicOp removeUnnecessaryCopy,+    RuleOp liftIdentityMapping,+    RuleOp liftIdentityStreaming,+    RuleOp removeDuplicateMapOutput,+    RuleOp mapOpToOp+  ]++-- Any certificates attached to a trivial Stm in the body might as+-- well be applied to the SOAC itself.+hoistCertificates :: TopDownRuleOp (Wise SOACS)+hoistCertificates vtable pat aux soac+  | (soac', hoisted) <- runState (mapSOACM mapper soac) mempty,+    hoisted /= mempty =+    Simplify $ auxing aux $ certifying hoisted $ letBind pat $ Op soac'+  where+    mapper = identitySOACMapper {mapOnSOACLambda = onLambda}+    onLambda lam = do+      stms' <- mapM onStm $ bodyStms $ lambdaBody lam+      return+        lam+          { lambdaBody =+              mkBody stms' $ bodyResult $ lambdaBody lam+          }+    onStm (Let se_pat se_aux (BasicOp (SubExp se))) = do+      let (invariant, variant) =+            partition (`ST.elem` vtable) $+              unCertificates $ stmAuxCerts se_aux+          se_aux' = se_aux {stmAuxCerts = Certificates variant}+      modify (Certificates invariant <>)+      return $ Let se_pat se_aux' $ BasicOp $ SubExp se+    onStm stm = return stm+hoistCertificates _ _ _ _ =+  Skip++liftIdentityMapping ::+  forall lore.+  (Bindable lore, Simplify.SimplifiableLore lore, HasSOAC (Wise lore)) =>+  BottomUpRuleOp (Wise lore)+liftIdentityMapping (_, usages) pat aux op+  | Just (Screma w form arrs :: SOAC (Wise lore)) <- asSOAC op,+    Just fun <- isMapSOAC form = do+    let inputMap = M.fromList $ zip (map paramName $ lambdaParams fun) arrs+        free = freeIn $ lambdaBody fun+        rettype = lambdaReturnType fun+        ses = bodyResult $ lambdaBody fun++        freeOrConst (Var v) = v `nameIn` free+        freeOrConst Constant {} = True++        checkInvariance (outId, Var v, _) (invariant, mapresult, rettype')+          | Just inp <- M.lookup v inputMap =+            let e+                  | patElemName outId `UT.isConsumed` usages+                      || inp `UT.isConsumed` usages =+                    Copy inp+                  | otherwise =+                    SubExp $ Var inp+             in ( (Pattern [] [outId], BasicOp e) : invariant,+                  mapresult,+                  rettype'+                )+        checkInvariance (outId, e, t) (invariant, mapresult, rettype')+          | freeOrConst e =+            ( (Pattern [] [outId], BasicOp $ Replicate (Shape [w]) e) : invariant,+              mapresult,+              rettype'+            )+          | otherwise =+            ( invariant,+              (outId, e) : mapresult,+              t : rettype'+            )++    case foldr checkInvariance ([], [], []) $+      zip3 (patternElements pat) ses rettype of+      ([], _, _) -> Skip+      (invariant, mapresult, rettype') -> Simplify $ do+        let (pat', ses') = unzip mapresult+            fun' =+              fun+                { lambdaBody = (lambdaBody fun) {bodyResult = ses'},+                  lambdaReturnType = rettype'+                }+        mapM_ (uncurry letBind) invariant+        auxing aux $+          letBindNames (map patElemName pat') $ Op $ soacOp $ Screma w (mapSOAC fun') arrs+liftIdentityMapping _ _ _ _ = Skip++liftIdentityStreaming :: BottomUpRuleOp (Wise SOACS)+liftIdentityStreaming _ (Pattern [] pes) aux (Stream w form lam arrs)+  | (variant_map, invariant_map) <-+      partitionEithers $ map isInvariantRes $ zip3 map_ts map_pes map_res,+    not $ null invariant_map = Simplify $ do+    forM_ invariant_map $ \(pe, arr) ->+      letBind (Pattern [] [pe]) $ BasicOp $ Copy arr++    let (variant_map_ts, variant_map_pes, variant_map_res) = unzip3 variant_map+        lam' =+          lam+            { lambdaBody = (lambdaBody lam) {bodyResult = fold_res ++ variant_map_res},+              lambdaReturnType = fold_ts ++ variant_map_ts+            }++    auxing aux $+      letBind (Pattern [] $ fold_pes ++ variant_map_pes) $+        Op $ Stream w form lam' arrs+  where+    num_folds = length $ getStreamAccums form+    (fold_pes, map_pes) = splitAt num_folds pes+    (fold_ts, map_ts) = splitAt num_folds $ lambdaReturnType lam+    lam_res = bodyResult $ lambdaBody lam+    (fold_res, map_res) = splitAt num_folds lam_res+    params_to_arrs = zip (map paramName $ drop (1 + num_folds) $ lambdaParams lam) arrs++    isInvariantRes (_, pe, Var v)+      | Just arr <- lookup v params_to_arrs =+        Right (pe, arr)+    isInvariantRes x =+      Left x+liftIdentityStreaming _ _ _ _ = Skip++-- | Remove all arguments to the map that are simply replicates.+-- These can be turned into free variables instead.+removeReplicateMapping ::+  (Bindable lore, Simplify.SimplifiableLore lore, HasSOAC (Wise lore)) =>+  TopDownRuleOp (Wise lore)+removeReplicateMapping vtable pat aux op+  | Just (Screma w form arrs) <- asSOAC op,+    Just fun <- isMapSOAC form,+    Just (bnds, fun', arrs') <- removeReplicateInput vtable fun arrs = Simplify $ do+    forM_ bnds $ \(vs, cs, e) -> certifying cs $ letBindNames vs e+    auxing aux $ letBind pat $ Op $ soacOp $ Screma w (mapSOAC fun') arrs'+removeReplicateMapping _ _ _ _ = Skip++-- | Like 'removeReplicateMapping', but for 'Scatter'.+removeReplicateWrite :: TopDownRuleOp (Wise SOACS)+removeReplicateWrite vtable pat aux (Scatter len lam ivs as)+  | Just (bnds, lam', ivs') <- removeReplicateInput vtable lam ivs = Simplify $ do+    forM_ bnds $ \(vs, cs, e) -> certifying cs $ letBindNames vs e+    auxing aux $ letBind pat $ Op $ Scatter len lam' ivs' as+removeReplicateWrite _ _ _ _ = Skip++removeReplicateInput ::+  Aliased lore =>+  ST.SymbolTable lore ->+  AST.Lambda lore ->+  [VName] ->+  Maybe+    ( [([VName], Certificates, AST.Exp lore)],+      AST.Lambda lore,+      [VName]+    )+removeReplicateInput vtable fun arrs+  | not $ null parameterBnds = do+    let (arr_params', arrs') = unzip params_and_arrs+        fun' = fun {lambdaParams = acc_params <> arr_params'}+    return (parameterBnds, fun', arrs')+  | otherwise = Nothing+  where+    params = lambdaParams fun+    (acc_params, arr_params) =+      splitAt (length params - length arrs) params+    (params_and_arrs, parameterBnds) =+      partitionEithers $ zipWith isReplicateAndNotConsumed arr_params arrs++    isReplicateAndNotConsumed p v+      | Just (BasicOp (Replicate (Shape (_ : ds)) e), v_cs) <-+          ST.lookupExp v vtable,+        not $ paramName p `nameIn` consumedByLambda fun =+        Right+          ( [paramName p],+            v_cs,+            case ds of+              [] -> BasicOp $ SubExp e+              _ -> BasicOp $ Replicate (Shape ds) e+          )+      | otherwise =+        Left (p, v)++-- | Remove inputs that are not used inside the SOAC.+removeUnusedSOACInput :: TopDownRuleOp (Wise SOACS)+removeUnusedSOACInput _ pat aux (Screma w (ScremaForm scan reduce map_lam) arrs)+  | (used, unused) <- partition usedInput params_and_arrs,+    not (null unused) = Simplify $ do+    let (used_params, used_arrs) = unzip used+        map_lam' = map_lam {lambdaParams = used_params}+    auxing aux $ letBind pat $ Op $ Screma w (ScremaForm scan reduce map_lam') used_arrs+  where+    params_and_arrs = zip (lambdaParams map_lam) arrs+    used_in_body = freeIn $ lambdaBody map_lam+    usedInput (param, _) = paramName param `nameIn` used_in_body+removeUnusedSOACInput _ _ _ _ = Skip++removeDeadMapping :: BottomUpRuleOp (Wise SOACS)+removeDeadMapping (_, used) pat aux (Screma w form arrs)+  | Just fun <- isMapSOAC form =+    let ses = bodyResult $ lambdaBody fun+        isUsed (bindee, _, _) = (`UT.used` used) $ patElemName bindee+        (pat', ses', ts') =+          unzip3 $+            filter isUsed $+              zip3 (patternElements pat) ses $ lambdaReturnType fun+        fun' =+          fun+            { lambdaBody = (lambdaBody fun) {bodyResult = ses'},+              lambdaReturnType = ts'+            }+     in if pat /= Pattern [] pat'+          then+            Simplify $+              auxing aux $+                letBind (Pattern [] pat') $ Op $ Screma w (mapSOAC fun') arrs+          else Skip+removeDeadMapping _ _ _ _ = Skip++removeDuplicateMapOutput :: BottomUpRuleOp (Wise SOACS)+removeDuplicateMapOutput (_, used) pat aux (Screma w form arrs)+  | Just fun <- isMapSOAC form =+    let ses = bodyResult $ lambdaBody fun+        ts = lambdaReturnType fun+        pes = patternValueElements pat+        ses_ts_pes = zip3 ses ts pes+        (ses_ts_pes', copies) =+          foldl checkForDuplicates (mempty, mempty) ses_ts_pes+     in if null copies+          then Skip+          else Simplify $ do+            let (ses', ts', pes') = unzip3 ses_ts_pes'+                pat' = Pattern [] pes'+                fun' =+                  fun+                    { lambdaBody = (lambdaBody fun) {bodyResult = ses'},+                      lambdaReturnType = ts'+                    }+            auxing aux $ letBind pat' $ Op $ Screma w (mapSOAC fun') arrs+            forM_ copies $ \(from, to) ->+              if UT.isConsumed (patElemName to) used+                then letBind (Pattern [] [to]) $ BasicOp $ Copy $ patElemName from+                else letBind (Pattern [] [to]) $ BasicOp $ SubExp $ Var $ patElemName from+  where+    checkForDuplicates (ses_ts_pes', copies) (se, t, pe)+      | Just (_, _, pe') <- find (\(x, _, _) -> x == se) ses_ts_pes' =+        -- This subexp has been returned before, producing the+        -- array pe'.+        (ses_ts_pes', (pe', pe) : copies)+      | otherwise = (ses_ts_pes' ++ [(se, t, pe)], copies)+removeDuplicateMapOutput _ _ _ _ = Skip++-- Mapping some operations becomes an extension of that operation.+mapOpToOp :: BottomUpRuleOp (Wise SOACS)+mapOpToOp (_, used) pat aux1 e+  | Just (map_pe, cs, w, BasicOp (Reshape newshape reshape_arr), [p], [arr]) <-+      isMapWithOp pat e,+    paramName p == reshape_arr,+    not $ UT.isConsumed (patElemName map_pe) used = Simplify $ do+    let redim+          | isJust $ shapeCoercion newshape = DimCoercion w+          | otherwise = DimNew w+    certifying (stmAuxCerts aux1 <> cs) $+      letBind pat $+        BasicOp $ Reshape (redim : newshape) arr+  | Just+      ( _,+        cs,+        _,+        BasicOp (Concat d arr arrs dw),+        ps,+        outer_arr : outer_arrs+        ) <-+      isMapWithOp pat e,+    (arr : arrs) == map paramName ps =+    Simplify $+      certifying (stmAuxCerts aux1 <> cs) $+        letBind pat $+          BasicOp $ Concat (d + 1) outer_arr outer_arrs dw+  | Just+      ( map_pe,+        cs,+        _,+        BasicOp (Rearrange perm rearrange_arr),+        [p],+        [arr]+        ) <-+      isMapWithOp pat e,+    paramName p == rearrange_arr,+    not $ UT.isConsumed (patElemName map_pe) used =+    Simplify $+      certifying (stmAuxCerts aux1 <> cs) $+        letBind pat $+          BasicOp $ Rearrange (0 : map (1 +) perm) arr+  | Just (map_pe, cs, _, BasicOp (Rotate rots rotate_arr), [p], [arr]) <-+      isMapWithOp pat e,+    paramName p == rotate_arr,+    not $ UT.isConsumed (patElemName map_pe) used =+    Simplify $+      certifying (stmAuxCerts aux1 <> cs) $+        letBind pat $+          BasicOp $ Rotate (intConst Int64 0 : rots) arr+mapOpToOp _ _ _ _ = Skip++isMapWithOp ::+  PatternT dec ->+  SOAC (Wise SOACS) ->+  Maybe+    ( PatElemT dec,+      Certificates,+      SubExp,+      AST.Exp (Wise SOACS),+      [Param Type],+      [VName]+    )+isMapWithOp pat e+  | Pattern [] [map_pe] <- pat,+    Screma w form arrs <- e,+    Just map_lam <- isMapSOAC form,+    [Let (Pattern [] [pe]) aux2 e'] <-+      stmsToList $ bodyStms $ lambdaBody map_lam,+    [Var r] <- bodyResult $ lambdaBody map_lam,+    r == patElemName pe =+    Just (map_pe, stmAuxCerts aux2, w, e', lambdaParams map_lam, arrs)+  | otherwise = Nothing++-- | Some of the results of a reduction (or really: Redomap) may be+-- dead.  We remove them here.  The trick is that we need to look at+-- the data dependencies to see that the "dead" result is not+-- actually used for computing one of the live ones.+removeDeadReduction :: BottomUpRuleOp (Wise SOACS)+removeDeadReduction (_, used) pat aux (Screma w form arrs)+  | Just ([Reduce comm redlam nes], maplam) <- isRedomapSOAC form,+    not $ all (`UT.used` used) $ patternNames pat, -- Quick/cheap check+    let (red_pes, map_pes) = splitAt (length nes) $ patternElements pat,+    let redlam_deps = dataDependencies $ lambdaBody redlam,+    let redlam_res = bodyResult $ lambdaBody redlam,+    let redlam_params = lambdaParams redlam,+    let used_after =+          map snd $+            filter ((`UT.used` used) . patElemName . fst) $+              zip red_pes redlam_params,+    let necessary =+          findNecessaryForReturned+            (`elem` used_after)+            (zip redlam_params $ redlam_res <> redlam_res)+            redlam_deps,+    let alive_mask = map ((`nameIn` necessary) . paramName) redlam_params,+    not $ all (== True) alive_mask = Simplify $ do+    let fixDeadToNeutral lives ne = if lives then Nothing else Just ne+        dead_fix = zipWith fixDeadToNeutral alive_mask nes+        (used_red_pes, _, used_nes) =+          unzip3 $+            filter (\(_, x, _) -> paramName x `nameIn` necessary) $+              zip3 red_pes redlam_params nes++    let maplam' = removeLambdaResults (take (length nes) alive_mask) maplam+    redlam' <- removeLambdaResults (take (length nes) alive_mask) <$> fixLambdaParams redlam (dead_fix ++ dead_fix)++    auxing aux $+      letBind (Pattern [] $ used_red_pes ++ map_pes) $+        Op $ Screma w (redomapSOAC [Reduce comm redlam' used_nes] maplam') arrs+removeDeadReduction _ _ _ _ = Skip++-- | If we are writing to an array that is never used, get rid of it.+removeDeadWrite :: BottomUpRuleOp (Wise SOACS)+removeDeadWrite (_, used) pat aux (Scatter w fun arrs dests) =+  let (i_ses, v_ses) = splitAt (length dests) $ bodyResult $ lambdaBody fun+      (i_ts, v_ts) = splitAt (length dests) $ lambdaReturnType fun+      isUsed (bindee, _, _, _, _, _) = (`UT.used` used) $ patElemName bindee+      (pat', i_ses', v_ses', i_ts', v_ts', dests') =+        unzip6 $+          filter isUsed $+            zip6 (patternElements pat) i_ses v_ses i_ts v_ts dests+      fun' =+        fun+          { lambdaBody = (lambdaBody fun) {bodyResult = i_ses' ++ v_ses'},+            lambdaReturnType = i_ts' ++ v_ts'+          }+   in if pat /= Pattern [] pat'+        then+          Simplify $+            auxing aux $+              letBind (Pattern [] pat') $ Op $ Scatter w fun' arrs dests'+        else Skip+removeDeadWrite _ _ _ _ = Skip++-- handles now concatenation of more than two arrays+fuseConcatScatter :: TopDownRuleOp (Wise SOACS)+fuseConcatScatter vtable pat _ (Scatter _ fun arrs dests)+  | Just (ws@(w' : _), xss, css) <- unzip3 <$> mapM isConcat arrs,+    xivs <- transpose xss,+    all (w' ==) ws = Simplify $ do+    let r = length xivs+    fun2s <- mapM (\_ -> renameLambda fun) [1 .. r -1]+    let fun_n = length $ lambdaReturnType fun+        (fun_is, fun_vs) =+          unzip $+            map+              ( splitAt (fun_n `div` 2)+                  . bodyResult+                  . lambdaBody+              )+              (fun : fun2s)+        (its, vts) =+          unzip $+            replicate r $+              splitAt (fun_n `div` 2) $ lambdaReturnType fun+        new_stmts = mconcat $ map (bodyStms . lambdaBody) (fun : fun2s)+    let fun' =+          Lambda+            { lambdaParams = mconcat $ map lambdaParams (fun : fun2s),+              lambdaBody =+                mkBody new_stmts $+                  mix fun_is <> mix fun_vs,+              lambdaReturnType = mix its <> mix vts+            }+    certifying (mconcat css) $+      letBind pat $ Op $ Scatter w' fun' (concat xivs) $ map (incWrites r) dests+  where+    sizeOf :: VName -> Maybe SubExp+    sizeOf x = arraySize 0 . typeOf <$> ST.lookup x vtable+    mix = concat . transpose+    incWrites r (w, n, a) = (w, n * r, a) -- ToDO: is it (n*r) or (n+r-1)??+    isConcat v = case ST.lookupExp v vtable of+      Just (BasicOp (Concat 0 x ys _), cs) -> do+        x_w <- sizeOf x+        y_ws <- mapM sizeOf ys+        guard $ all (x_w ==) y_ws+        return (x_w, x : ys, cs)+      Just (BasicOp (Reshape reshape arr), cs) -> do+        guard $ isJust $ shapeCoercion reshape+        (a, b, cs') <- isConcat arr+        return (a, b, cs <> cs')+      _ -> Nothing+fuseConcatScatter _ _ _ _ = Skip++simplifyClosedFormReduce :: TopDownRuleOp (Wise SOACS)+simplifyClosedFormReduce _ pat _ (Screma (Constant w) form _)+  | Just nes <- concatMap redNeutral . fst <$> isRedomapSOAC form,+    zeroIsh w =+    Simplify $+      forM_ (zip (patternNames pat) nes) $ \(v, ne) ->+        letBindNames [v] $ BasicOp $ SubExp ne+simplifyClosedFormReduce vtable pat _ (Screma _ form arrs)+  | Just [Reduce _ red_fun nes] <- isReduceSOAC form =+    Simplify $ foldClosedForm (`ST.lookupExp` vtable) pat red_fun nes arrs+simplifyClosedFormReduce _ _ _ _ = Skip++-- For now we just remove singleton SOACs.+simplifyKnownIterationSOAC ::+  (Bindable lore, Simplify.SimplifiableLore lore, HasSOAC (Wise lore)) =>+  TopDownRuleOp (Wise lore)+simplifyKnownIterationSOAC _ pat _ op+  | Just (Screma (Constant k) (ScremaForm scans reds map_lam) arrs) <- asSOAC op,+    oneIsh k = Simplify $ do+    let (Reduce _ red_lam red_nes) = singleReduce reds+        (Scan scan_lam scan_nes) = singleScan scans+        (scan_pes, red_pes, map_pes) =+          splitAt3 (length scan_nes) (length red_nes) $+            patternElements pat+        bindMapParam p a = do+          a_t <- lookupType a+          letBindNames [paramName p] $+            BasicOp $ Index a $ fullSlice a_t [DimFix $ constant (0 :: Int64)]+        bindArrayResult pe se =+          letBindNames [patElemName pe] $+            BasicOp $ ArrayLit [se] $ rowType $ patElemType pe+        bindResult pe se =+          letBindNames [patElemName pe] $ BasicOp $ SubExp se++    zipWithM_ bindMapParam (lambdaParams map_lam) arrs+    (to_scan, to_red, map_res) <-+      splitAt3 (length scan_nes) (length red_nes)+        <$> bodyBind (lambdaBody map_lam)+    scan_res <- eLambda scan_lam $ map eSubExp $ scan_nes ++ to_scan+    red_res <- eLambda red_lam $ map eSubExp $ red_nes ++ to_red++    zipWithM_ bindArrayResult scan_pes scan_res+    zipWithM_ bindResult red_pes red_res+    zipWithM_ bindArrayResult map_pes map_res+simplifyKnownIterationSOAC _ pat _ op+  | Just (Stream (Constant k) form fold_lam arrs) <- asSOAC op,+    oneIsh k = Simplify $ do+    let nes = getStreamAccums form+        (chunk_param, acc_params, slice_params) =+          partitionChunkedFoldParameters (length nes) (lambdaParams fold_lam)++    letBindNames [paramName chunk_param] $+      BasicOp $ SubExp $ intConst Int64 1++    forM_ (zip acc_params nes) $ \(p, ne) ->+      letBindNames [paramName p] $ BasicOp $ SubExp ne++    forM_ (zip slice_params arrs) $ \(p, arr) ->+      letBindNames [paramName p] $ BasicOp $ SubExp $ Var arr++    res <- bodyBind $ lambdaBody fold_lam++    forM_ (zip (patternNames pat) res) $ \(v, se) ->+      letBindNames [v] $ BasicOp $ SubExp se+simplifyKnownIterationSOAC _ _ _ _ = Skip++data ArrayOp+  = ArrayIndexing Certificates VName (Slice SubExp)+  | ArrayRearrange Certificates VName [Int]+  | ArrayRotate Certificates VName [SubExp]+  | -- | Never constructed.+    ArrayVar Certificates VName+  deriving (Eq, Ord, Show)++arrayOpArr :: ArrayOp -> VName+arrayOpArr (ArrayIndexing _ arr _) = arr+arrayOpArr (ArrayRearrange _ arr _) = arr+arrayOpArr (ArrayRotate _ arr _) = arr+arrayOpArr (ArrayVar _ arr) = arr++arrayOpCerts :: ArrayOp -> Certificates+arrayOpCerts (ArrayIndexing cs _ _) = cs+arrayOpCerts (ArrayRearrange cs _ _) = cs+arrayOpCerts (ArrayRotate cs _ _) = cs+arrayOpCerts (ArrayVar cs _) = cs++isArrayOp :: Certificates -> AST.Exp (Wise SOACS) -> Maybe ArrayOp+isArrayOp cs (BasicOp (Index arr slice)) =+  Just $ ArrayIndexing cs arr slice+isArrayOp cs (BasicOp (Rearrange perm arr)) =+  Just $ ArrayRearrange cs arr perm+isArrayOp cs (BasicOp (Rotate rots arr)) =+  Just $ ArrayRotate cs arr rots+isArrayOp _ _ =+  Nothing++fromArrayOp :: ArrayOp -> (Certificates, AST.Exp (Wise SOACS))+fromArrayOp (ArrayIndexing cs arr slice) = (cs, BasicOp $ Index arr slice)+fromArrayOp (ArrayRearrange cs arr perm) = (cs, BasicOp $ Rearrange perm arr)+fromArrayOp (ArrayRotate cs arr rots) = (cs, BasicOp $ Rotate rots arr)+fromArrayOp (ArrayVar cs arr) = (cs, BasicOp $ SubExp $ Var arr)++arrayOps :: AST.Body (Wise SOACS) -> S.Set (AST.Pattern (Wise SOACS), ArrayOp)+arrayOps = mconcat . map onStm . stmsToList . bodyStms+  where+    onStm (Let pat aux e) =+      case isArrayOp (stmAuxCerts aux) e of+        Just op -> S.singleton (pat, op)+        Nothing -> execState (walkExpM walker e) mempty+    onOp = execWriter . mapSOACM identitySOACMapper {mapOnSOACLambda = onLambda}+    onLambda lam = do+      tell $ arrayOps $ lambdaBody lam+      return lam+    walker =+      identityWalker+        { walkOnBody = const $ modify . (<>) . arrayOps,+          walkOnOp = modify . (<>) . onOp+        }++replaceArrayOps ::+  M.Map ArrayOp ArrayOp ->+  AST.Body (Wise SOACS) ->+  AST.Body (Wise SOACS)+replaceArrayOps substs (Body _ stms res) =+  mkBody (fmap onStm stms) res+  where+    onStm (Let pat aux e) =+      let (cs', e') = onExp (stmAuxCerts aux) e+       in certify cs' $+            mkLet' (patternContextIdents pat) (patternValueIdents pat) aux e'+    onExp cs e+      | Just op <- isArrayOp cs e,+        Just op' <- M.lookup op substs =+        fromArrayOp op'+    onExp cs e = (cs, mapExp mapper e)+    mapper =+      identityMapper+        { mapOnBody = const $ return . replaceArrayOps substs,+          mapOnOp = return . onOp+        }+    onOp = runIdentity . mapSOACM identitySOACMapper {mapOnSOACLambda = return . onLambda}+    onLambda lam = lam {lambdaBody = replaceArrayOps substs $ lambdaBody lam}++-- Turn+--+--    map (\i -> ... xs[i] ...) (iota n)+--+-- into+--+--    map (\i x -> ... x ...) (iota n) xs+--+-- This is not because we want to encourage the map-iota pattern, but+-- it may be present in generated code.  This is an unfortunately+-- expensive simplification rule, since it requires multiple passes+-- over the entire lambda body.  It only handles the very simplest+-- case - if you find yourself planning to extend it to handle more+-- complex situations (rotate or whatnot), consider turning it into a+-- separate compiler pass instead.+simplifyMapIota :: TopDownRuleOp (Wise SOACS)+simplifyMapIota vtable pat aux (Screma w (ScremaForm scan reduce map_lam) arrs)+  | Just (p, _) <- find isIota (zip (lambdaParams map_lam) arrs),+    indexings <-+      filter (indexesWith (paramName p)) $+        map snd $+          S.toList $+            arrayOps $ lambdaBody map_lam,+    not $ null indexings = Simplify $ do+    -- For each indexing with iota, add the corresponding array to+    -- the Screma, and construct a new lambda parameter.+    (more_arrs, more_params, replacements) <-+      unzip3 . catMaybes <$> mapM mapOverArr indexings+    let substs = M.fromList $ zip indexings replacements+        map_lam' =+          map_lam+            { lambdaParams = lambdaParams map_lam <> more_params,+              lambdaBody =+                replaceArrayOps substs $+                  lambdaBody map_lam+            }++    auxing aux $+      letBind pat $ Op $ Screma w (ScremaForm scan reduce map_lam') (arrs <> more_arrs)+  where+    isIota (_, arr) = case ST.lookupBasicOp arr vtable of+      Just (Iota _ (Constant o) (Constant s) _, _) ->+        zeroIsh o && oneIsh s+      _ -> False++    indexesWith v (ArrayIndexing cs arr (DimFix (Var i) : _))+      | arr `ST.elem` vtable,+        all (`ST.elem` vtable) $ unCertificates cs =+        i == v+    indexesWith _ _ = False++    mapOverArr (ArrayIndexing cs arr slice) = do+      arr_elem <- newVName $ baseString arr ++ "_elem"+      arr_t <- lookupType arr+      arr' <-+        if arraySize 0 arr_t == w+          then return arr+          else+            certifying cs $+              letExp (baseString arr ++ "_prefix") $+                BasicOp $+                  Index arr $+                    fullSlice arr_t [DimSlice (intConst Int64 0) w (intConst Int64 1)]+      return $+        Just+          ( arr',+            Param arr_elem (rowType arr_t),+            ArrayIndexing cs arr_elem (drop 1 slice)+          )+    mapOverArr _ = return Nothing+simplifyMapIota _ _ _ _ = Skip++-- If a Screma's map function contains a transformation+-- (e.g. transpose) on a parameter, create a new parameter+-- corresponding to that transformation performed on the rows of the+-- full array.+moveTransformToInput :: TopDownRuleOp (Wise SOACS)+moveTransformToInput vtable pat aux (Screma w (ScremaForm scan reduce map_lam) arrs)+  | ops <- map snd $ filter arrayIsMapParam $ S.toList $ arrayOps $ lambdaBody map_lam,+    not $ null ops = Simplify $ do+    (more_arrs, more_params, replacements) <-+      unzip3 . catMaybes <$> mapM mapOverArr ops++    when (null more_arrs) cannotSimplify++    let substs = M.fromList $ zip ops replacements+        map_lam' =+          map_lam+            { lambdaParams = lambdaParams map_lam <> more_params,+              lambdaBody =+                replaceArrayOps substs $+                  lambdaBody map_lam+            }++    auxing aux $+      letBind pat $ Op $ Screma w (ScremaForm scan reduce map_lam') (arrs <> more_arrs)+  where+    map_param_names = map paramName (lambdaParams map_lam)+    topLevelPattern = (`elem` fmap stmPattern (bodyStms (lambdaBody map_lam)))+    onlyUsedOnce arr =+      case filter ((arr `nameIn`) . freeIn) $ stmsToList $ bodyStms $ lambdaBody map_lam of+        _ : _ : _ -> False+        _ -> True++    -- It's not just about whether the array is a parameter;+    -- everything else must be map-invariant.+    arrayIsMapParam (pat', ArrayIndexing cs arr slice) =+      arr `elem` map_param_names+        && all (`ST.elem` vtable) (namesToList $ freeIn cs <> freeIn slice)+        && not (null slice)+        && (not (null $ sliceDims slice) || (topLevelPattern pat' && onlyUsedOnce arr))+    arrayIsMapParam (_, ArrayRearrange cs arr perm) =+      arr `elem` map_param_names+        && all (`ST.elem` vtable) (namesToList $ freeIn cs)+        && not (null perm)+    arrayIsMapParam (_, ArrayRotate cs arr rots) =+      arr `elem` map_param_names+        && all (`ST.elem` vtable) (namesToList $ freeIn cs <> freeIn rots)+    arrayIsMapParam (_, ArrayVar {}) =+      False++    mapOverArr op+      | Just (_, arr) <- find ((== arrayOpArr op) . fst) (zip map_param_names arrs) = do+        arr_t <- lookupType arr+        let whole_dim = DimSlice (intConst Int64 0) (arraySize 0 arr_t) (intConst Int64 1)+        arr_transformed <- certifying (arrayOpCerts op) $+          letExp (baseString arr ++ "_transformed") $+            case op of+              ArrayIndexing _ _ slice ->+                BasicOp $ Index arr $ whole_dim : slice+              ArrayRearrange _ _ perm ->+                BasicOp $ Rearrange (0 : map (+ 1) perm) arr+              ArrayRotate _ _ rots ->+                BasicOp $ Rotate (intConst Int64 0 : rots) arr+              ArrayVar {} ->+                BasicOp $ SubExp $ Var arr+        arr_transformed_t <- lookupType arr_transformed+        arr_transformed_row <- newVName $ baseString arr ++ "_transformed_row"+        return $+          Just+            ( arr_transformed,+              Param arr_transformed_row (rowType arr_transformed_t),+              ArrayVar mempty arr_transformed_row+            )+    mapOverArr _ = return Nothing moveTransformToInput _ _ _ _ =   Skip
src/Futhark/IR/SegOp.hs view
@@ -1,1145 +1,1445 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE ScopedTypeVariables #-}--- | Segmented operations.  These correspond to perfect @map@ nests on--- top of /something/, except that the @map@s are conceptually only--- over @iota@s (so there will be explicit indexing inside them).-module Futhark.IR.SegOp-  ( SegOp(..)-  , SegVirt(..)-  , segLevel-  , segSpace-  , typeCheckSegOp-  , SegSpace(..)-  , scopeOfSegSpace-  , segSpaceDims--    -- * Details-  , HistOp(..)-  , histType-  , SegBinOp(..)-  , segBinOpResults-  , segBinOpChunks-  , KernelBody(..)-  , aliasAnalyseKernelBody-  , consumedInKernelBody-  , ResultManifest(..)-  , KernelResult(..)-  , kernelResultSubExp-  , SplitOrdering(..)--    -- ** Generic traversal-  , SegOpMapper(..)-  , identitySegOpMapper-  , mapSegOpM--    -- * Simplification-  , simplifySegOp-  , HasSegOp(..)-  , segOpRules--    -- * Memory-  , segOpReturns-  )-where--import Control.Monad.State.Strict-import Control.Monad.Writer hiding (mapM_)-import Control.Monad.Identity hiding (mapM_)-import Data.Bifunctor (first)-import qualified Data.Map.Strict as M-import Data.Maybe-import Data.List-  (intersperse, foldl', partition, isPrefixOf, groupBy)--import Futhark.IR-import qualified Futhark.Analysis.Alias as Alias-import qualified Futhark.Analysis.SymbolTable as ST-import qualified Futhark.Analysis.UsageTable as UT-import Futhark.Analysis.PrimExp.Convert-import qualified Futhark.Util.Pretty as PP-import Futhark.Util.Pretty-  ((</>), (<+>), ppr, commasep, Pretty, parens, text)-import Futhark.Transform.Substitute-import Futhark.Transform.Rename-import Futhark.Optimise.Simplify.Lore-import Futhark.IR.Prop.Aliases-import Futhark.IR.Aliases-  (Aliases, removeLambdaAliases, removeStmAliases)-import Futhark.IR.Mem-import qualified Futhark.TypeCheck as TC-import Futhark.Analysis.Metrics-import Futhark.Util (maybeNth, chunks)-import Futhark.Optimise.Simplify.Rule-import qualified Futhark.Optimise.Simplify.Engine as Engine-import Futhark.Tools---- | How an array is split into chunks.-data SplitOrdering = SplitContiguous-                   | SplitStrided SubExp-                   deriving (Eq, Ord, Show)--instance FreeIn SplitOrdering where-  freeIn' SplitContiguous = mempty-  freeIn' (SplitStrided stride) = freeIn' stride--instance Substitute SplitOrdering where-  substituteNames _ SplitContiguous =-    SplitContiguous-  substituteNames subst (SplitStrided stride) =-    SplitStrided $ substituteNames subst stride--instance Rename SplitOrdering where-  rename SplitContiguous =-    pure SplitContiguous-  rename (SplitStrided stride) =-    SplitStrided <$> rename stride---- | An operator for 'SegHist'.-data HistOp lore =-  HistOp { histWidth :: SubExp-         , histRaceFactor :: SubExp-         , histDest :: [VName]-         , histNeutral :: [SubExp]-         , histShape :: Shape-           -- ^ In case this operator is semantically a vectorised-           -- operator (corresponding to a perfect map nest in the-           -- SOACS representation), these are the logical-           -- "dimensions".  This is used to generate more efficient-           -- code.-         , histOp :: Lambda lore-         }-  deriving (Eq, Ord, Show)---- | The type of a histogram produced by a 'HistOp'.  This can be--- different from the type of the 'histDest's in case we are--- dealing with a segmented histogram.-histType :: HistOp lore -> [Type]-histType op = map ((`arrayOfRow` histWidth op) .-                        (`arrayOfShape` histShape op)) $-                   lambdaReturnType $ histOp op---- | An operator for 'SegScan' and 'SegRed'.-data SegBinOp lore =-  SegBinOp { segBinOpComm :: Commutativity-           , segBinOpLambda :: Lambda lore-           , segBinOpNeutral :: [SubExp]-           , segBinOpShape :: Shape-             -- ^ In case this operator is semantically a vectorised-             -- operator (corresponding to a perfect map nest in the-             -- SOACS representation), these are the logical-             -- "dimensions".  This is used to generate more efficient-             -- code.-           }-  deriving (Eq, Ord, Show)---- | How many reduction results are produced by these 'SegBinOp's?-segBinOpResults :: [SegBinOp lore] -> Int-segBinOpResults = sum . map (length . segBinOpNeutral)---- | Split some list into chunks equal to the number of values--- returned by each 'SegBinOp'-segBinOpChunks :: [SegBinOp lore] -> [a] -> [[a]]-segBinOpChunks = chunks . map (length . segBinOpNeutral)---- | The body of a 'SegOp'.-data KernelBody lore = KernelBody { kernelBodyLore :: BodyDec lore-                                  , kernelBodyStms :: Stms lore-                                  , kernelBodyResult :: [KernelResult]-                                  }--deriving instance Decorations lore => Ord (KernelBody lore)-deriving instance Decorations lore => Show (KernelBody lore)-deriving instance Decorations lore => Eq (KernelBody lore)---- | Metadata about whether there is a subtle point to this--- 'KernelResult'.  This is used to protect things like tiling, which--- might otherwise be removed by the simplifier because they're--- semantically redundant.  This has no semantic effect and can be--- ignored at code generation.-data ResultManifest-  = ResultNoSimplify-    -- ^ Don't simplify this one!-  | ResultMaySimplify-    -- ^ Go nuts.-  | ResultPrivate-    -- ^ The results produced are only used within the-    -- same physical thread later on, and can thus be-    -- kept in registers.-  deriving (Eq, Show, Ord)---- | A 'KernelBody' does not return an ordinary 'Result'.  Instead, it--- returns a list of these.-data KernelResult = Returns ResultManifest SubExp-                    -- ^ Each "worker" in the kernel returns this.-                    -- Whether this is a result-per-thread or a-                    -- result-per-group depends on where the 'SegOp' occurs.-                  | WriteReturns-                    [SubExp] -- Size of array.  Must match number of dims.-                    VName -- Which array-                    [(Slice SubExp, SubExp)]-                    -- Arbitrary number of index/value pairs.-                  | ConcatReturns-                    SplitOrdering -- Permuted?-                    SubExp -- The final size.-                    SubExp -- Per-thread/group (max) chunk size.-                    VName -- Chunk by this worker.-                  | TileReturns-                    [(SubExp, SubExp)] -- Total/tile for each dimension-                    VName -- Tile written by this worker.-                    -- The TileReturns must not expect more than one-                    -- result to be written per physical thread.-                  deriving (Eq, Show, Ord)---- | Get the root t'SubExp' corresponding values for a 'KernelResult'.-kernelResultSubExp :: KernelResult -> SubExp-kernelResultSubExp (Returns _ se) = se-kernelResultSubExp (WriteReturns _ arr _) = Var arr-kernelResultSubExp (ConcatReturns _ _ _ v) = Var v-kernelResultSubExp (TileReturns _ v) = Var v--instance FreeIn KernelResult where-  freeIn' (Returns _ what) = freeIn' what-  freeIn' (WriteReturns rws arr res) = freeIn' rws <> freeIn' arr <> freeIn' res-  freeIn' (ConcatReturns o w per_thread_elems v) =-    freeIn' o <> freeIn' w <> freeIn' per_thread_elems <> freeIn' v-  freeIn' (TileReturns dims v) =-    freeIn' dims <> freeIn' v--instance ASTLore lore => FreeIn (KernelBody lore) where-  freeIn' (KernelBody dec stms res) =-    fvBind bound_in_stms $ freeIn' dec <> freeIn' stms <> freeIn' res-    where bound_in_stms = foldMap boundByStm stms--instance ASTLore lore => Substitute (KernelBody lore) where-  substituteNames subst (KernelBody dec stms res) =-    KernelBody-    (substituteNames subst dec)-    (substituteNames subst stms)-    (substituteNames subst res)--instance Substitute KernelResult where-  substituteNames subst (Returns manifest se) =-    Returns manifest (substituteNames subst se)-  substituteNames subst (WriteReturns rws arr res) =-    WriteReturns-    (substituteNames subst rws) (substituteNames subst arr)-    (substituteNames subst res)-  substituteNames subst (ConcatReturns o w per_thread_elems v) =-    ConcatReturns-    (substituteNames subst o)-    (substituteNames subst w)-    (substituteNames subst per_thread_elems)-    (substituteNames subst v)-  substituteNames subst (TileReturns dims v) =-    TileReturns (substituteNames subst dims) (substituteNames subst v)--instance ASTLore lore => Rename (KernelBody lore) where-  rename (KernelBody dec stms res) = do-    dec' <- rename dec-    renamingStms stms $ \stms' ->-      KernelBody dec' stms' <$> rename res--instance Rename KernelResult where-  rename = substituteRename---- | Perform alias analysis on a 'KernelBody'.-aliasAnalyseKernelBody :: (ASTLore lore,-                           CanBeAliased (Op lore)) =>-                          KernelBody lore-                       -> KernelBody (Aliases lore)-aliasAnalyseKernelBody (KernelBody dec stms res) =-  let Body dec' stms' _ = Alias.analyseBody mempty $ Body dec stms []-  in KernelBody dec' stms' res--removeKernelBodyAliases :: CanBeAliased (Op lore) =>-                           KernelBody (Aliases lore) -> KernelBody lore-removeKernelBodyAliases (KernelBody (_, dec) stms res) =-  KernelBody dec (fmap removeStmAliases stms) res--removeKernelBodyWisdom :: CanBeWise (Op lore) =>-                          KernelBody (Wise lore) -> KernelBody lore-removeKernelBodyWisdom (KernelBody dec stms res) =-  let Body dec' stms' _ = removeBodyWisdom $ Body dec stms []-  in KernelBody dec' stms' res---- | The variables consumed in the kernel body.-consumedInKernelBody :: Aliased lore =>-                        KernelBody lore -> Names-consumedInKernelBody (KernelBody dec stms res) =-  consumedInBody (Body dec stms []) <> mconcat (map consumedByReturn res)-  where consumedByReturn (WriteReturns _ a _) = oneName a-        consumedByReturn _                    = mempty--checkKernelBody :: TC.Checkable lore =>-                   [Type] -> KernelBody (Aliases lore) -> TC.TypeM lore ()-checkKernelBody ts (KernelBody (_, dec) stms kres) = do-  TC.checkBodyLore dec-  TC.checkStms stms $ do-    unless (length ts == length kres) $-      TC.bad $ TC.TypeError $ "Kernel return type is " ++ prettyTuple ts ++-      ", but body returns " ++ show (length kres) ++ " values."-    zipWithM_ checkKernelResult kres ts--  where checkKernelResult (Returns _ what) t =-          TC.require [t] what-        checkKernelResult (WriteReturns rws arr res) t = do-          mapM_ (TC.require [Prim int32]) rws-          arr_t <- lookupType arr-          forM_ res $ \(slice, e) -> do-            mapM_ (traverse $ TC.require [Prim int32]) slice-            TC.require [t] e-            unless (arr_t == t `arrayOfShape` Shape rws) $-              TC.bad $ TC.TypeError $ "WriteReturns returning " ++-              pretty e ++ " of type " ++ pretty t ++ ", shape=" ++ pretty rws ++-              ", but destination array has type " ++ pretty arr_t-          TC.consume =<< TC.lookupAliases arr-        checkKernelResult (ConcatReturns o w per_thread_elems v) t = do-          case o of-            SplitContiguous     -> return ()-            SplitStrided stride -> TC.require [Prim int32] stride-          TC.require [Prim int32] w-          TC.require [Prim int32] per_thread_elems-          vt <- lookupType v-          unless (vt == t `arrayOfRow` arraySize 0 vt) $-            TC.bad $ TC.TypeError $ "Invalid type for ConcatReturns " ++ pretty v-        checkKernelResult (TileReturns dims v) t = do-          forM_ dims $ \(dim, tile) -> do-            TC.require [Prim int32] dim-            TC.require [Prim int32] tile-          vt <- lookupType v-          unless (vt == t `arrayOfShape` Shape (map snd dims)) $-            TC.bad $ TC.TypeError $ "Invalid type for TileReturns " ++ pretty v--kernelBodyMetrics :: OpMetrics (Op lore) => KernelBody lore -> MetricsM ()-kernelBodyMetrics = mapM_ stmMetrics . kernelBodyStms--instance PrettyLore lore => Pretty (KernelBody lore) where-  ppr (KernelBody _ stms res) =-    PP.stack (map ppr (stmsToList stms)) </>-    text "return" <+> PP.braces (PP.commasep $ map ppr res)--instance Pretty KernelResult where-  ppr (Returns ResultNoSimplify what) =-    text "returns (manifest)" <+> ppr what-  ppr (Returns ResultPrivate what) =-    text "returns (private)" <+> ppr what-  ppr (Returns ResultMaySimplify what) =-    text "returns" <+> ppr what-  ppr (WriteReturns rws arr res) =-    ppr arr <+> text "with" <+> PP.apply (map ppRes res)-    where ppRes (is, e) =-            PP.brackets (PP.commasep $ zipWith f is rws) <+> text "<-" <+> ppr e-          f i rw = ppr i <+> text "<" <+> ppr rw-  ppr (ConcatReturns o w per_thread_elems v) =-    text "concat" <> suff <>-    parens (commasep [ppr w, ppr per_thread_elems]) <+> ppr v-    where suff = case o of SplitContiguous     -> mempty-                           SplitStrided stride -> text "Strided" <> parens (ppr stride)-  ppr (TileReturns dims v) =-    text "tile" <>-    parens (commasep $ map onDim dims) <+> ppr v-    where onDim (dim, tile) = ppr dim <+> text "/" <+> ppr tile----- | Do we need group-virtualisation when generating code for the--- segmented operation?  In most cases, we do, but for some simple--- kernels, we compute the full number of groups in advance, and then--- virtualisation is an unnecessary (but generally very small)--- overhead.  This only really matters for fairly trivial but very--- wide @map@ kernels where each thread performs constant-time work on--- scalars.-data SegVirt-  = SegVirt-  | SegNoVirt-  | SegNoVirtFull-    -- ^ Not only do we not need virtualisation, but we _guarantee_-    -- that all physical threads participate in the work.  This can-    -- save some checks in code generation.-  deriving (Eq, Ord, Show)---- | Index space of a 'SegOp'.-data SegSpace = SegSpace { segFlat :: VName-                         -- ^ Flat physical index corresponding to the-                         -- dimensions (at code generation used for a-                         -- thread ID or similar).-                         , unSegSpace :: [(VName, SubExp)]-                         }-              deriving (Eq, Ord, Show)----- | The sizes spanned by the indexes of the 'SegSpace'.-segSpaceDims :: SegSpace -> [SubExp]-segSpaceDims (SegSpace _ space) = map snd space---- | A 'Scope' containing all the identifiers brought into scope by--- this 'SegSpace'.-scopeOfSegSpace :: SegSpace -> Scope lore-scopeOfSegSpace (SegSpace phys space) =-  M.fromList $ zip (phys : map fst space) $ repeat $ IndexName Int32--checkSegSpace :: TC.Checkable lore => SegSpace -> TC.TypeM lore ()-checkSegSpace (SegSpace _ dims) =-  mapM_ (TC.require [Prim int32] . snd) dims---- | A 'SegOp' is semantically a perfectly nested stack of maps, on--- top of some bottommost computation (scalar computation, reduction,--- scan, or histogram).  The 'SegSpace' encodes the original map--- structure.------ All 'SegOp's are parameterised by the representation of their body,--- as well as a *level*.  The *level* is a representation-specific bit--- of information.  For example, in GPU backends, it is used to--- indicate whether the 'SegOp' is expected to run at the thread-level--- or the group-level.-data SegOp lvl lore-  = SegMap lvl SegSpace [Type] (KernelBody lore)-  | SegRed lvl SegSpace [SegBinOp lore] [Type] (KernelBody lore)-    -- ^ The KernelSpace must always have at least two dimensions,-    -- implying that the result of a SegRed is always an array.-  | SegScan lvl SegSpace [SegBinOp lore] [Type] (KernelBody lore)-  | SegHist lvl SegSpace [HistOp lore] [Type] (KernelBody lore)-  deriving (Eq, Ord, Show)---- | The level of a 'SegOp'.-segLevel :: SegOp lvl lore -> lvl-segLevel (SegMap lvl _ _ _) = lvl-segLevel (SegRed lvl _ _ _ _) = lvl-segLevel (SegScan lvl _ _ _ _) = lvl-segLevel (SegHist lvl _ _ _ _) = lvl---- | The space of a 'SegOp'.-segSpace :: SegOp lvl lore -> SegSpace-segSpace (SegMap _ lvl _ _) = lvl-segSpace (SegRed _ lvl _ _ _) = lvl-segSpace (SegScan _ lvl _ _ _) = lvl-segSpace (SegHist _ lvl _ _ _) = lvl--segResultShape :: SegSpace -> Type -> KernelResult -> Type-segResultShape _ t (WriteReturns rws _ _) =-  t `arrayOfShape` Shape rws-segResultShape space t (Returns _ _) =-  foldr (flip arrayOfRow) t $ segSpaceDims space-segResultShape _ t (ConcatReturns _ w _ _) =-  t `arrayOfRow` w-segResultShape _ t (TileReturns dims _) =-  t `arrayOfShape` Shape (map fst dims)---- | The return type of a 'SegOp'.-segOpType :: SegOp lvl lore -> [Type]-segOpType (SegMap _ space ts kbody) =-  zipWith (segResultShape space) ts $ kernelBodyResult kbody-segOpType (SegRed _ space reds ts kbody) =-  red_ts ++-  zipWith (segResultShape space) map_ts-  (drop (length red_ts) $ kernelBodyResult kbody)-  where map_ts = drop (length red_ts) ts-        segment_dims = init $ segSpaceDims space-        red_ts = do-          op <- reds-          let shape = Shape segment_dims <> segBinOpShape op-          map (`arrayOfShape` shape) (lambdaReturnType $ segBinOpLambda op)-segOpType (SegScan _ space scans ts kbody) =-  scan_ts ++-  zipWith (segResultShape space) map_ts-  (drop (length scan_ts) $ kernelBodyResult kbody)-  where map_ts = drop (length scan_ts) ts-        scan_ts = do-          op <- scans-          let shape = Shape (segSpaceDims space) <> segBinOpShape op-          map (`arrayOfShape` shape) (lambdaReturnType $ segBinOpLambda op)-segOpType (SegHist _ space ops _ _) = do-  op <- ops-  let shape = Shape (segment_dims <> [histWidth op]) <> histShape op-  map (`arrayOfShape` shape) (lambdaReturnType $ histOp op)-  where dims = segSpaceDims space-        segment_dims = init dims--instance TypedOp (SegOp lvl lore) where-  opType = pure . staticShapes . segOpType--instance (ASTLore lore, Aliased lore, ASTConstraints lvl) =>-         AliasedOp (SegOp lvl lore) where-  opAliases = map (const mempty) . segOpType--  consumedInOp (SegMap _ _ _ kbody) =-    consumedInKernelBody kbody-  consumedInOp (SegRed _ _ _ _ kbody) =-    consumedInKernelBody kbody-  consumedInOp (SegScan _ _ _ _ kbody) =-    consumedInKernelBody kbody-  consumedInOp (SegHist _ _ ops _ kbody) =-    namesFromList (concatMap histDest ops) <> consumedInKernelBody kbody---- | Type check a 'SegOp', given a checker for its level.-typeCheckSegOp :: TC.Checkable lore =>-                  (lvl -> TC.TypeM lore ())-               -> SegOp lvl (Aliases lore) -> TC.TypeM lore ()-typeCheckSegOp checkLvl (SegMap lvl space ts kbody) = do-  checkLvl lvl-  checkScanRed space [] ts kbody--typeCheckSegOp checkLvl (SegRed lvl space reds ts body) = do-  checkLvl lvl-  checkScanRed space reds' ts body-  where reds' = zip3-                (map segBinOpLambda reds)-                (map segBinOpNeutral reds)-                (map segBinOpShape reds)--typeCheckSegOp checkLvl (SegScan lvl space scans ts body) = do-  checkLvl lvl-  checkScanRed space scans' ts body-  where scans' = zip3-                 (map segBinOpLambda scans)-                 (map segBinOpNeutral scans)-                 (map segBinOpShape scans)--typeCheckSegOp checkLvl (SegHist lvl space ops ts kbody) = do-  checkLvl lvl-  checkSegSpace space-  mapM_ TC.checkType ts--  TC.binding (scopeOfSegSpace space) $ do-    nes_ts <- forM ops $ \(HistOp dest_w rf dests nes shape op) -> do-      TC.require [Prim int32] dest_w-      TC.require [Prim int32] rf-      nes' <- mapM TC.checkArg nes-      mapM_ (TC.require [Prim int32]) $ shapeDims shape--      -- Operator type must match the type of neutral elements.-      let stripVecDims = stripArray $ shapeRank shape-      TC.checkLambda op $ map (TC.noArgAliases . first stripVecDims) $ nes' ++ nes'-      let nes_t = map TC.argType nes'-      unless (nes_t == lambdaReturnType op) $-        TC.bad $ TC.TypeError $ "SegHist operator has return type " ++-        prettyTuple (lambdaReturnType op) ++ " but neutral element has type " ++-        prettyTuple nes_t--      -- Arrays must have proper type.-      let dest_shape = Shape (segment_dims <> [dest_w]) <> shape-      forM_ (zip nes_t dests) $ \(t, dest) -> do-        TC.requireI [t `arrayOfShape` dest_shape] dest-        TC.consume =<< TC.lookupAliases dest--      return $ map (`arrayOfShape` shape) nes_t--    checkKernelBody ts kbody--    -- Return type of bucket function must be an index for each-    -- operation followed by the values to write.-    let bucket_ret_t = replicate (length ops) (Prim int32) ++ concat nes_ts-    unless (bucket_ret_t == ts) $-      TC.bad $ TC.TypeError $ "SegHist body has return type " ++-      prettyTuple ts ++ " but should have type " ++-      prettyTuple bucket_ret_t--  where segment_dims = init $ segSpaceDims space--checkScanRed :: TC.Checkable lore =>-                SegSpace-             -> [(Lambda (Aliases lore), [SubExp], Shape)]-             -> [Type]-             -> KernelBody (Aliases lore)-             -> TC.TypeM lore ()-checkScanRed space ops ts kbody = do-  checkSegSpace space-  mapM_ TC.checkType ts--  TC.binding (scopeOfSegSpace space) $ do-    ne_ts <- forM ops $ \(lam, nes, shape) -> do-      mapM_ (TC.require [Prim int32]) $ shapeDims shape-      nes' <- mapM TC.checkArg nes--      -- Operator type must match the type of neutral elements.-      TC.checkLambda lam $ map TC.noArgAliases $ nes' ++ nes'-      let nes_t = map TC.argType nes'--      unless (lambdaReturnType lam == nes_t) $-        TC.bad $ TC.TypeError "wrong type for operator or neutral elements."--      return $ map (`arrayOfShape` shape) nes_t--    let expecting = concat ne_ts-        got = take (length expecting) ts-    unless (expecting == got) $-      TC.bad $ TC.TypeError $-      "Wrong return for body (does not match neutral elements; expected " ++-      pretty expecting ++ "; found " ++-      pretty got ++ ")"--    checkKernelBody ts kbody---- | Like 'Mapper', but just for 'SegOp's.-data SegOpMapper lvl flore tlore m = SegOpMapper {-    mapOnSegOpSubExp :: SubExp -> m SubExp-  , mapOnSegOpLambda :: Lambda flore -> m (Lambda tlore)-  , mapOnSegOpBody :: KernelBody flore -> m (KernelBody tlore)-  , mapOnSegOpVName :: VName -> m VName-  , mapOnSegOpLevel :: lvl -> m lvl-  }---- | A mapper that simply returns the 'SegOp' verbatim.-identitySegOpMapper :: Monad m => SegOpMapper lvl lore lore m-identitySegOpMapper = SegOpMapper { mapOnSegOpSubExp = return-                                  , mapOnSegOpLambda = return-                                  , mapOnSegOpBody = return-                                  , mapOnSegOpVName = return-                                  , mapOnSegOpLevel = return-                                  }--mapOnSegSpace :: Monad f =>-                 SegOpMapper lvl flore tlore f -> SegSpace -> f SegSpace-mapOnSegSpace tv (SegSpace phys dims) =-  SegSpace phys <$> traverse (traverse $ mapOnSegOpSubExp tv) dims--mapSegBinOp :: Monad m =>-               SegOpMapper lvl flore tlore m-            -> SegBinOp flore -> m (SegBinOp tlore)-mapSegBinOp tv (SegBinOp comm red_op nes shape) =-  SegBinOp comm-  <$> mapOnSegOpLambda tv red_op-  <*> mapM (mapOnSegOpSubExp tv) nes-  <*> (Shape <$> mapM (mapOnSegOpSubExp tv) (shapeDims shape))---- | Apply a 'SegOpMapper' to the given 'SegOp'.-mapSegOpM :: (Applicative m, Monad m) =>-             SegOpMapper lvl flore tlore m-          -> SegOp lvl flore -> m (SegOp lvl tlore)-mapSegOpM tv (SegMap lvl space ts body) =-  SegMap-  <$> mapOnSegOpLevel tv lvl-  <*> mapOnSegSpace tv space-  <*> mapM (mapOnSegOpType tv) ts-  <*> mapOnSegOpBody tv body-mapSegOpM tv (SegRed lvl space reds ts lam) =-  SegRed-  <$> mapOnSegOpLevel tv lvl-  <*> mapOnSegSpace tv space-  <*> mapM (mapSegBinOp tv) reds-  <*> mapM (mapOnType $ mapOnSegOpSubExp tv) ts-  <*> mapOnSegOpBody tv lam-mapSegOpM tv (SegScan lvl space scans ts body) =-  SegScan-  <$> mapOnSegOpLevel tv lvl-  <*> mapOnSegSpace tv space-  <*> mapM (mapSegBinOp tv) scans-  <*> mapM (mapOnType $ mapOnSegOpSubExp tv) ts-  <*> mapOnSegOpBody tv body-mapSegOpM tv (SegHist lvl space ops ts body) =-  SegHist-  <$> mapOnSegOpLevel tv lvl-  <*> mapOnSegSpace tv space-  <*> mapM onHistOp ops-  <*> mapM (mapOnType $ mapOnSegOpSubExp tv) ts-  <*> mapOnSegOpBody tv body-  where onHistOp (HistOp w rf arrs nes shape op) =-          HistOp <$> mapOnSegOpSubExp tv w-          <*> mapOnSegOpSubExp tv rf-          <*> mapM (mapOnSegOpVName tv) arrs-          <*> mapM (mapOnSegOpSubExp tv) nes-          <*> (Shape <$> mapM (mapOnSegOpSubExp tv) (shapeDims shape))-          <*> mapOnSegOpLambda tv op--mapOnSegOpType :: Monad m =>-                  SegOpMapper lvl flore tlore m -> Type -> m Type-mapOnSegOpType _tv t@Prim{} = pure t-mapOnSegOpType tv (Array pt shape u) = Array pt <$> f shape <*> pure u-  where f (Shape dims) = Shape <$> mapM (mapOnSegOpSubExp tv) dims-mapOnSegOpType _tv (Mem s) = pure $ Mem s--instance (ASTLore lore, Substitute lvl) =>-         Substitute (SegOp lvl lore) where-  substituteNames subst = runIdentity . mapSegOpM substitute-    where substitute =-            SegOpMapper { mapOnSegOpSubExp = return . substituteNames subst-                        , mapOnSegOpLambda = return . substituteNames subst-                        , mapOnSegOpBody = return . substituteNames subst-                        , mapOnSegOpVName = return . substituteNames subst-                        , mapOnSegOpLevel = return . substituteNames subst-                        }--instance (ASTLore lore, ASTConstraints lvl) =>-         Rename (SegOp lvl lore) where-  rename = mapSegOpM renamer-    where renamer = SegOpMapper rename rename rename rename rename--instance (ASTLore lore, FreeIn (LParamInfo lore), FreeIn lvl) =>-         FreeIn (SegOp lvl lore) where-  freeIn' e = flip execState mempty $ mapSegOpM free e-    where walk f x = modify (<>f x) >> return x-          free = SegOpMapper { mapOnSegOpSubExp = walk freeIn'-                             , mapOnSegOpLambda = walk freeIn'-                             , mapOnSegOpBody = walk freeIn'-                             , mapOnSegOpVName = walk freeIn'-                             , mapOnSegOpLevel = walk freeIn'-                             }--instance OpMetrics (Op lore) => OpMetrics (SegOp lvl lore) where-  opMetrics (SegMap _ _ _ body) =-    inside "SegMap" $ kernelBodyMetrics body-  opMetrics (SegRed _ _ reds _ body) =-    inside "SegRed" $ do mapM_ (lambdaMetrics . segBinOpLambda) reds-                         kernelBodyMetrics body-  opMetrics (SegScan _ _ scans _ body) =-    inside "SegScan" $ do mapM_ (lambdaMetrics . segBinOpLambda) scans-                          kernelBodyMetrics body-  opMetrics (SegHist _ _ ops _ body) =-    inside "SegHist" $ do mapM_ (lambdaMetrics . histOp) ops-                          kernelBodyMetrics body--instance Pretty SegSpace where-  ppr (SegSpace phys dims) = parens (commasep $ do (i,d) <- dims-                                                   return $ ppr i <+> "<" <+> ppr d) <+>-                             parens (text "~" <> ppr phys)--instance PrettyLore lore => Pretty (SegBinOp lore) where-  ppr (SegBinOp comm lam nes shape) =-    PP.braces (PP.commasep $ map ppr nes) <> PP.comma </>-    ppr shape <> PP.comma </>-    comm' <> ppr lam-    where comm' = case comm of Commutative -> text "commutative "-                               Noncommutative -> mempty--instance (PrettyLore lore, PP.Pretty lvl) => PP.Pretty (SegOp lvl lore) where-  ppr (SegMap lvl space ts body) =-    text "segmap" <> ppr lvl </>-    PP.align (ppr space) <+>-    PP.colon <+> ppTuple' ts <+> PP.nestedBlock "{" "}" (ppr body)--  ppr (SegRed lvl space reds ts body) =-    text "segred" <> ppr lvl </>-    PP.parens (PP.braces (mconcat $ intersperse (PP.comma <> PP.line) $ map ppr reds)) </>-    PP.align (ppr space) <+> PP.colon <+> ppTuple' ts <+>-    PP.nestedBlock "{" "}" (ppr body)--  ppr (SegScan lvl space scans ts body) =-    text "segscan" <> ppr lvl </>-    PP.parens (PP.braces (mconcat $ intersperse (PP.comma <> PP.line) $ map ppr scans)) </>-    PP.align (ppr space) <+> PP.colon <+> ppTuple' ts <+>-    PP.nestedBlock "{" "}" (ppr body)--  ppr (SegHist lvl space ops ts body) =-    text "seghist" <> ppr lvl </>-    ppr lvl </>-    PP.parens (PP.braces (mconcat $ intersperse (PP.comma <> PP.line) $ map ppOp ops)) </>-    PP.align (ppr space) <+> PP.colon <+> ppTuple' ts <+>-    PP.nestedBlock "{" "}" (ppr body)-    where ppOp (HistOp w rf dests nes shape op) =-            ppr w <> PP.comma <+> ppr rf <> PP.comma </>-            PP.braces (PP.commasep $ map ppr dests) <> PP.comma </>-            PP.braces (PP.commasep $ map ppr nes) <> PP.comma </>-            ppr shape <> PP.comma </>-            ppr op--instance (ASTLore lore, ASTLore (Aliases lore),-          CanBeAliased (Op lore), ASTConstraints lvl) =>-         CanBeAliased (SegOp lvl lore) where-  type OpWithAliases (SegOp lvl lore) = SegOp lvl (Aliases lore)--  addOpAliases = runIdentity . mapSegOpM alias-    where alias = SegOpMapper return (return . Alias.analyseLambda)-                  (return . aliasAnalyseKernelBody) return return--  removeOpAliases = runIdentity . mapSegOpM remove-    where remove = SegOpMapper return (return . removeLambdaAliases)-                   (return . removeKernelBodyAliases) return return--instance (CanBeWise (Op lore), ASTLore lore, ASTConstraints lvl) =>-         CanBeWise (SegOp lvl lore) where-  type OpWithWisdom (SegOp lvl lore) = SegOp lvl (Wise lore)--  removeOpWisdom = runIdentity . mapSegOpM remove-    where remove = SegOpMapper return-                   (return . removeLambdaWisdom)-                   (return . removeKernelBodyWisdom)-                   return return--instance ASTLore lore => ST.IndexOp (SegOp lvl lore) where-  indexOp vtable k (SegMap _ space _ kbody) is = do-    Returns ResultMaySimplify se <- maybeNth k $ kernelBodyResult kbody-    guard $ length gtids <= length is-    let idx_table = M.fromList $ zip gtids $ map (ST.Indexed mempty) is-        idx_table' = foldl expandIndexedTable idx_table $ kernelBodyStms kbody-    case se of-      Var v -> M.lookup v idx_table'-      _ -> Nothing--    where (gtids, _) = unzip $ unSegSpace space-          -- Indexes in excess of what is used to index through the-          -- segment dimensions.-          excess_is = drop (length gtids) is--          expandIndexedTable table stm-            | [v] <- patternNames $ stmPattern stm,-              Just (pe,cs) <--                  runWriterT $ primExpFromExp (asPrimExp table) $ stmExp stm =-                M.insert v (ST.Indexed (stmCerts stm <> cs) pe) table--            | [v] <- patternNames $ stmPattern stm,-              BasicOp (Index arr slice) <- stmExp stm,-              length (sliceDims slice) == length excess_is,-              arr `ST.elem` vtable,-              Just (slice', cs) <- asPrimExpSlice table slice =-                let idx = ST.IndexedArray (stmCerts stm <> cs)-                          arr (fixSlice slice' excess_is)-                in M.insert v idx table--            | otherwise =-                table--          asPrimExpSlice table =-            runWriterT . mapM (traverse (primExpFromSubExpM (asPrimExp table)))--          asPrimExp table v-            | Just (ST.Indexed cs e) <- M.lookup v table = tell cs >> return e-            | Just (Prim pt) <- ST.lookupType v vtable =-                return $ LeafExp v pt-            | otherwise = lift Nothing--  indexOp _ _ _ _ = Nothing--instance (ASTLore lore, ASTConstraints lvl) =>-         IsOp (SegOp lvl lore) where-  cheapOp _ = False-  safeOp _ = True----- Simplification--instance Engine.Simplifiable SplitOrdering where-  simplify SplitContiguous =-    return SplitContiguous-  simplify (SplitStrided stride) =-    SplitStrided <$> Engine.simplify stride--instance Engine.Simplifiable SegSpace where-  simplify (SegSpace phys dims) =-    SegSpace phys <$> mapM (traverse Engine.simplify) dims--instance Engine.Simplifiable KernelResult where-  simplify (Returns manifest what) =-    Returns manifest <$> Engine.simplify what-  simplify (WriteReturns ws a res) =-    WriteReturns <$> Engine.simplify ws <*> Engine.simplify a <*> Engine.simplify res-  simplify (ConcatReturns o w pte what) =-    ConcatReturns-    <$> Engine.simplify o-    <*> Engine.simplify w-    <*> Engine.simplify pte-    <*> Engine.simplify what-  simplify (TileReturns dims what) =-    TileReturns <$> Engine.simplify dims <*> Engine.simplify what--mkWiseKernelBody :: (ASTLore lore, CanBeWise (Op lore)) =>-                    BodyDec lore -> Stms (Wise lore) -> [KernelResult] -> KernelBody (Wise lore)-mkWiseKernelBody dec bnds res =-  let Body dec' _ _ = mkWiseBody dec bnds res_vs-  in KernelBody dec' bnds res-  where res_vs = map kernelResultSubExp res--mkKernelBodyM :: MonadBinder m =>-                 Stms (Lore m) -> [KernelResult]-              -> m (KernelBody (Lore m))-mkKernelBodyM stms kres = do-  Body dec' _ _ <- mkBodyM stms res_ses-  return $ KernelBody dec' stms kres-  where res_ses = map kernelResultSubExp kres--simplifyKernelBody :: (Engine.SimplifiableLore lore, BodyDec lore ~ ()) =>-                      SegSpace -> KernelBody lore-                   -> Engine.SimpleM lore (KernelBody (Wise lore), Stms (Wise lore))-simplifyKernelBody space (KernelBody _ stms res) = do-  par_blocker <- Engine.asksEngineEnv $ Engine.blockHoistPar . Engine.envHoistBlockers--  ((body_stms, body_res), hoisted) <--    Engine.localVtable (<>scope_vtable) $-    Engine.localVtable (\vtable -> vtable { ST.simplifyMemory = True }) $-    Engine.blockIf (Engine.hasFree bound_here-                    `Engine.orIf` Engine.isOp-                    `Engine.orIf` par_blocker-                    `Engine.orIf` Engine.isConsumed) $-    Engine.simplifyStms stms $ do-    res' <- Engine.localVtable (ST.hideCertified $ namesFromList $ M.keys $ scopeOf stms) $-            mapM Engine.simplify res-    return ((res', UT.usages $ freeIn res'), mempty)--  return (mkWiseKernelBody () body_stms body_res, hoisted)--  where scope_vtable = segSpaceSymbolTable space-        bound_here = namesFromList $ M.keys $ scopeOfSegSpace space--segSpaceSymbolTable :: ASTLore lore => SegSpace -> ST.SymbolTable lore-segSpaceSymbolTable (SegSpace flat gtids_and_dims) =-  foldl' f (ST.fromScope $ M.singleton flat $ IndexName Int32) gtids_and_dims-  where f vtable (gtid, dim) = ST.insertLoopVar gtid Int32 dim vtable--simplifySegBinOp :: Engine.SimplifiableLore lore =>-                    SegBinOp lore-                 -> Engine.SimpleM lore (SegBinOp (Wise lore), Stms (Wise lore))-simplifySegBinOp (SegBinOp comm lam nes shape) = do-  (lam', hoisted) <--    Engine.localVtable (\vtable -> vtable { ST.simplifyMemory = True }) $-    Engine.simplifyLambda lam-  shape' <- Engine.simplify shape-  nes' <- mapM Engine.simplify nes-  return (SegBinOp comm lam' nes' shape', hoisted)---- | Simplify the given 'SegOp'.-simplifySegOp :: (Engine.SimplifiableLore lore,-                  BodyDec lore ~ (),-                  Engine.Simplifiable lvl) =>-                 SegOp lvl lore-              -> Engine.SimpleM lore (SegOp lvl (Wise lore), Stms (Wise lore))-simplifySegOp (SegMap lvl space ts kbody) = do-  (lvl', space', ts') <- Engine.simplify (lvl, space, ts)-  (kbody', body_hoisted) <- simplifyKernelBody space kbody-  return (SegMap lvl' space' ts' kbody',-          body_hoisted)--simplifySegOp (SegRed lvl space reds ts kbody) = do-  (lvl', space', ts') <- Engine.simplify (lvl, space, ts)-  (reds', reds_hoisted) <- Engine.localVtable (<>scope_vtable) $-    unzip <$> mapM simplifySegBinOp reds-  (kbody', body_hoisted) <- simplifyKernelBody space kbody--  return (SegRed lvl' space' reds' ts' kbody',-          mconcat reds_hoisted <> body_hoisted)-  where scope = scopeOfSegSpace space-        scope_vtable = ST.fromScope scope--simplifySegOp (SegScan lvl space scans ts kbody) = do-  (lvl', space', ts') <- Engine.simplify (lvl, space, ts)-  (scans', scans_hoisted) <- Engine.localVtable (<>scope_vtable) $-    unzip <$> mapM simplifySegBinOp scans-  (kbody', body_hoisted) <- simplifyKernelBody space kbody--  return (SegScan lvl' space' scans' ts' kbody',-          mconcat scans_hoisted <> body_hoisted)-  where scope = scopeOfSegSpace space-        scope_vtable = ST.fromScope scope--simplifySegOp (SegHist lvl space ops ts kbody) = do-  (lvl', space', ts') <- Engine.simplify (lvl, space, ts)--  (ops', ops_hoisted) <- fmap unzip $ forM ops $-    \(HistOp w rf arrs nes dims lam) -> do-      w' <- Engine.simplify w-      rf' <- Engine.simplify rf-      arrs' <- Engine.simplify arrs-      nes' <- Engine.simplify nes-      dims' <- Engine.simplify dims-      (lam', op_hoisted) <--        Engine.localVtable (<>scope_vtable) $-        Engine.localVtable (\vtable -> vtable { ST.simplifyMemory = True }) $-        Engine.simplifyLambda lam-      return (HistOp w' rf' arrs' nes' dims' lam',-              op_hoisted)--  (kbody', body_hoisted) <- simplifyKernelBody space kbody--  return (SegHist lvl' space' ops' ts' kbody',-          mconcat ops_hoisted <> body_hoisted)--  where scope = scopeOfSegSpace space-        scope_vtable = ST.fromScope scope---- | Does this lore contain 'SegOp's in its t'Op's?  A lore must be an--- instance of this class for the simplification rules to work.-class HasSegOp lore where-  type SegOpLevel lore-  asSegOp :: Op lore -> Maybe (SegOp (SegOpLevel lore) lore)-  segOp :: SegOp (SegOpLevel lore) lore -> Op lore---- | Simplification rules for simplifying 'SegOp's.-segOpRules :: (HasSegOp lore, BinderOps lore, Bindable lore) =>-              RuleBook lore-segOpRules =-  ruleBook [ RuleOp segOpRuleTopDown ] [ RuleOp segOpRuleBottomUp ]--segOpRuleTopDown :: (HasSegOp lore, BinderOps lore, Bindable lore) =>-                    TopDownRuleOp lore-segOpRuleTopDown vtable pat dec op-  | Just op' <- asSegOp op =-      topDownSegOp vtable pat dec op'-  | otherwise =-      Skip--segOpRuleBottomUp :: (HasSegOp lore, BinderOps lore) =>-                     BottomUpRuleOp lore-segOpRuleBottomUp vtable pat dec op-  | Just op' <- asSegOp op =-      bottomUpSegOp vtable pat dec op'-  | otherwise =-      Skip--topDownSegOp :: (HasSegOp lore, BinderOps lore, Bindable lore) =>-                ST.SymbolTable lore-             -> Pattern lore-             -> StmAux (ExpDec lore)-             -> SegOp (SegOpLevel lore) lore-             -> Rule lore---- If a SegOp produces something invariant to the SegOp, turn it--- into a replicate.-topDownSegOp vtable (Pattern [] kpes) dec (SegMap lvl space ts (KernelBody _ kstms kres)) = Simplify $ do-  (ts', kpes', kres') <--    unzip3 <$> filterM checkForInvarianceResult (zip3 ts kpes kres)--  -- Check if we did anything at all.-  when (kres == kres')-    cannotSimplify--  kbody <- mkKernelBodyM kstms kres'-  addStm $ Let (Pattern [] kpes') dec $ Op $ segOp $-    SegMap lvl space ts' kbody--  where isInvariant Constant{} = True-        isInvariant (Var v) = isJust $ ST.lookup v vtable--        checkForInvarianceResult (_, pe, Returns rm se)-          | rm == ResultMaySimplify,-            isInvariant se = do-              letBindNames [patElemName pe] $-                BasicOp $ Replicate (Shape $ segSpaceDims space) se-              return False-        checkForInvarianceResult _ =-          return True---- If a SegRed contains two reduction operations that have the same--- vector shape, merge them together.  This saves on communication--- overhead, but can in principle lead to more local memory usage.-topDownSegOp _ (Pattern [] pes) _ (SegRed lvl space ops ts kbody)-  | length ops > 1,-    op_groupings <- groupBy sameShape $ zip ops $ chunks (map (length . segBinOpNeutral) ops) $-                    zip3 red_pes red_ts red_res,-    any ((>1) . length) op_groupings = Simplify $ do-      let (ops', aux) = unzip $ mapMaybe combineOps op_groupings-          (red_pes', red_ts', red_res') = unzip3 $ concat aux-          pes' = red_pes' ++ map_pes-          ts' = red_ts' ++ map_ts-          kbody' = kbody { kernelBodyResult = red_res' ++ map_res }-      letBind (Pattern [] pes') $ Op $ segOp $ SegRed lvl space ops' ts' kbody'-  where (red_pes, map_pes) = splitAt (segBinOpResults ops) pes-        (red_ts, map_ts) = splitAt (segBinOpResults ops) ts-        (red_res, map_res) = splitAt (segBinOpResults ops) $ kernelBodyResult kbody--        sameShape (op1, _) (op2, _) = segBinOpShape op1 == segBinOpShape op2--        combineOps [] = Nothing-        combineOps (x:xs) = Just $ foldl' combine x xs--        combine (op1, op1_aux) (op2, op2_aux) =-          let lam1 = segBinOpLambda op1-              lam2 = segBinOpLambda op2-              (op1_xparams, op1_yparams) =-                splitAt (length (segBinOpNeutral op1)) $ lambdaParams lam1-              (op2_xparams, op2_yparams) =-                splitAt (length (segBinOpNeutral op2)) $ lambdaParams lam2-              lam = Lambda { lambdaParams = op1_xparams ++ op2_xparams ++-                                            op1_yparams ++ op2_yparams-                           , lambdaReturnType = lambdaReturnType lam1 ++ lambdaReturnType lam2-                           , lambdaBody =-                               mkBody (bodyStms (lambdaBody lam1) <> bodyStms (lambdaBody lam2)) $-                               bodyResult (lambdaBody lam1) <> bodyResult (lambdaBody lam2)-                           }-          in (SegBinOp { segBinOpComm = segBinOpComm op1 <> segBinOpComm op2-                       , segBinOpLambda = lam-                       , segBinOpNeutral = segBinOpNeutral op1 ++ segBinOpNeutral op2-                       , segBinOpShape = segBinOpShape op1 -- Same as shape of op2 due to the grouping.-                       },-               op1_aux ++ op2_aux)-topDownSegOp _ _ _ _ = Skip--bottomUpSegOp :: (HasSegOp lore, BinderOps lore) =>-                 (ST.SymbolTable lore, UT.UsageTable)-              -> Pattern lore-              -> StmAux (ExpDec lore)-              -> SegOp (SegOpLevel lore) lore-              -> Rule lore---- Some SegOp results can be moved outside the SegOp, which can--- simplify further analysis.-bottomUpSegOp (vtable, used) (Pattern [] kpes) dec (SegMap lvl space kts (KernelBody _ kstms kres)) = Simplify $ do--  -- Iterate through the bindings.  For each, we check whether it is-  -- in kres and can be moved outside.  If so, we remove it from kres-  -- and kpes and make it a binding outside.-  (kpes', kts', kres', kstms') <- localScope (scopeOfSegSpace space) $-    foldM distribute (kpes, kts, kres, mempty) kstms--  when (kpes' == kpes)-    cannotSimplify--  kbody <- localScope (scopeOfSegSpace space) $-           mkKernelBodyM kstms' kres'--  addStm $ Let (Pattern [] kpes') dec $ Op $ segOp $-    SegMap lvl space kts' kbody-  where-    free_in_kstms = foldMap freeIn kstms--    sliceWithGtidsFixed stm-      | Let _ _ (BasicOp (Index arr slice)) <- stm,-        space_slice <- map (DimFix . Var . fst) $ unSegSpace space,-        space_slice `isPrefixOf` slice,-        remaining_slice <- drop (length space_slice) slice,-        all (isJust . flip ST.lookup vtable) $ namesToList $-          freeIn arr <> freeIn remaining_slice =-          Just (remaining_slice, arr)--      | otherwise =-          Nothing--    distribute (kpes', kts', kres', kstms') stm-      | Let (Pattern [] [pe]) _ _ <- stm,-        Just (remaining_slice, arr) <- sliceWithGtidsFixed stm,-        Just (kpe, kpes'', kts'', kres'') <- isResult kpes' kts' kres' pe = do-          let outer_slice = map (\d -> DimSlice-                                       (constant (0::Int32))-                                       d-                                       (constant (1::Int32))) $-                            segSpaceDims space-              index kpe' = letBind (Pattern [] [kpe']) $ BasicOp $ Index arr $-                           outer_slice <> remaining_slice-          if patElemName kpe `UT.isConsumed` used-            then do precopy <- newVName $ baseString (patElemName kpe) <> "_precopy"-                    index kpe { patElemName = precopy }-                    letBind (Pattern [] [kpe]) $ BasicOp $ Copy precopy-            else index kpe-          return (kpes'', kts'', kres'',-                  if patElemName pe `nameIn` free_in_kstms-                  then kstms' <> oneStm stm-                  else kstms')--    distribute (kpes', kts', kres', kstms') stm =-      return (kpes', kts', kres', kstms' <> oneStm stm)--    isResult kpes' kts' kres' pe =-      case partition matches $ zip3 kpes' kts' kres' of-        ([(kpe,_,_)], kpes_and_kres)-          | (kpes'', kts'', kres'') <- unzip3 kpes_and_kres ->-              Just (kpe, kpes'', kts'', kres'')-        _ -> Nothing-      where matches (_, _, Returns _ (Var v)) = v == patElemName pe-            matches _ = False-bottomUpSegOp _ _ _ _ = Skip----- Memory--kernelBodyReturns :: (Mem lore, HasScope lore m, Monad m) =>-                     KernelBody lore -> [ExpReturns] -> m [ExpReturns]-kernelBodyReturns = zipWithM correct . kernelBodyResult-  where correct (WriteReturns _ arr _) _ = varReturns arr-        correct _ ret = return ret---- | Like 'segOpType', but for memory representations.-segOpReturns :: (Mem lore, Monad m, HasScope lore m) =>-                SegOp lvl lore -> m [ExpReturns]+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}++-- | Segmented operations.  These correspond to perfect @map@ nests on+-- top of /something/, except that the @map@s are conceptually only+-- over @iota@s (so there will be explicit indexing inside them).+module Futhark.IR.SegOp+  ( SegOp (..),+    SegVirt (..),+    segLevel,+    segSpace,+    typeCheckSegOp,+    SegSpace (..),+    scopeOfSegSpace,+    segSpaceDims,++    -- * Details+    HistOp (..),+    histType,+    SegBinOp (..),+    segBinOpResults,+    segBinOpChunks,+    KernelBody (..),+    aliasAnalyseKernelBody,+    consumedInKernelBody,+    ResultManifest (..),+    KernelResult (..),+    kernelResultSubExp,+    SplitOrdering (..),++    -- ** Generic traversal+    SegOpMapper (..),+    identitySegOpMapper,+    mapSegOpM,++    -- * Simplification+    simplifySegOp,+    HasSegOp (..),+    segOpRules,++    -- * Memory+    segOpReturns,+  )+where++import Control.Category+import Control.Monad.Identity hiding (mapM_)+import Control.Monad.State.Strict+import Control.Monad.Writer hiding (mapM_)+import Data.Bifunctor (first)+import Data.List+  ( foldl',+    groupBy,+    intersperse,+    isPrefixOf,+    partition,+  )+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Futhark.Analysis.Alias as Alias+import Futhark.Analysis.Metrics+import Futhark.Analysis.PrimExp.Convert+import qualified Futhark.Analysis.SymbolTable as ST+import qualified Futhark.Analysis.UsageTable as UT+import Futhark.IR+import Futhark.IR.Aliases+  ( Aliases,+    removeLambdaAliases,+    removeStmAliases,+  )+import Futhark.IR.Mem+import Futhark.IR.Prop.Aliases+import qualified Futhark.Optimise.Simplify.Engine as Engine+import Futhark.Optimise.Simplify.Lore+import Futhark.Optimise.Simplify.Rule+import Futhark.Tools+import Futhark.Transform.Rename+import Futhark.Transform.Substitute+import qualified Futhark.TypeCheck as TC+import Futhark.Util (chunks, maybeNth)+import Futhark.Util.Pretty+  ( Pretty,+    commasep,+    parens,+    ppr,+    text,+    (<+>),+    (</>),+  )+import qualified Futhark.Util.Pretty as PP+import GHC.Generics (Generic)+import Language.SexpGrammar as Sexp+import Language.SexpGrammar.Generic+import Prelude hiding (id, (.))++-- | How an array is split into chunks.+data SplitOrdering+  = SplitContiguous+  | SplitStrided SubExp+  deriving (Eq, Ord, Show, Generic)++instance SexpIso SplitOrdering where+  sexpIso =+    match $+      With (. Sexp.sym "contiguous") $+        With+          (. Sexp.list (Sexp.el (Sexp.sym "strided") >>> Sexp.el sexpIso))+          End++instance FreeIn SplitOrdering where+  freeIn' SplitContiguous = mempty+  freeIn' (SplitStrided stride) = freeIn' stride++instance Substitute SplitOrdering where+  substituteNames _ SplitContiguous =+    SplitContiguous+  substituteNames subst (SplitStrided stride) =+    SplitStrided $ substituteNames subst stride++instance Rename SplitOrdering where+  rename SplitContiguous =+    pure SplitContiguous+  rename (SplitStrided stride) =+    SplitStrided <$> rename stride++-- | An operator for 'SegHist'.+data HistOp lore = HistOp+  { histWidth :: SubExp,+    histRaceFactor :: SubExp,+    histDest :: [VName],+    histNeutral :: [SubExp],+    -- | In case this operator is semantically a vectorised+    -- operator (corresponding to a perfect map nest in the+    -- SOACS representation), these are the logical+    -- "dimensions".  This is used to generate more efficient+    -- code.+    histShape :: Shape,+    histOp :: Lambda lore+  }+  deriving (Eq, Ord, Show, Generic)++instance Decorations lore => SexpIso (HistOp lore) where+  sexpIso = with $ \histop ->+    Sexp.list+      ( Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> histop++-- | The type of a histogram produced by a 'HistOp'.  This can be+-- different from the type of the 'histDest's in case we are+-- dealing with a segmented histogram.+histType :: HistOp lore -> [Type]+histType op =+  map+    ( (`arrayOfRow` histWidth op)+        . (`arrayOfShape` histShape op)+    )+    $ lambdaReturnType $ histOp op++-- | An operator for 'SegScan' and 'SegRed'.+data SegBinOp lore = SegBinOp+  { segBinOpComm :: Commutativity,+    segBinOpLambda :: Lambda lore,+    segBinOpNeutral :: [SubExp],+    -- | In case this operator is semantically a vectorised+    -- operator (corresponding to a perfect map nest in the+    -- SOACS representation), these are the logical+    -- "dimensions".  This is used to generate more efficient+    -- code.+    segBinOpShape :: Shape+  }+  deriving (Eq, Ord, Show, Generic)++instance Decorations lore => SexpIso (SegBinOp lore) where+  sexpIso = with $ \segbinop ->+    Sexp.list+      ( Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> segbinop++-- | How many reduction results are produced by these 'SegBinOp's?+segBinOpResults :: [SegBinOp lore] -> Int+segBinOpResults = sum . map (length . segBinOpNeutral)++-- | Split some list into chunks equal to the number of values+-- returned by each 'SegBinOp'+segBinOpChunks :: [SegBinOp lore] -> [a] -> [[a]]+segBinOpChunks = chunks . map (length . segBinOpNeutral)++-- | The body of a 'SegOp'.+data KernelBody lore = KernelBody+  { kernelBodyLore :: BodyDec lore,+    kernelBodyStms :: Stms lore,+    kernelBodyResult :: [KernelResult]+  }+  deriving (Generic)++instance Decorations lore => SexpIso (KernelBody lore) where+  sexpIso = with $ \kernelbody ->+    Sexp.list+      ( Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> kernelbody++deriving instance Decorations lore => Ord (KernelBody lore)++deriving instance Decorations lore => Show (KernelBody lore)++deriving instance Decorations lore => Eq (KernelBody lore)++-- | Metadata about whether there is a subtle point to this+-- 'KernelResult'.  This is used to protect things like tiling, which+-- might otherwise be removed by the simplifier because they're+-- semantically redundant.  This has no semantic effect and can be+-- ignored at code generation.+data ResultManifest+  = -- | Don't simplify this one!+    ResultNoSimplify+  | -- | Go nuts.+    ResultMaySimplify+  | -- | The results produced are only used within the+    -- same physical thread later on, and can thus be+    -- kept in registers.+    ResultPrivate+  deriving (Eq, Show, Ord, Generic)++instance SexpIso ResultManifest where+  sexpIso =+    match $+      With (. Sexp.sym "no-simplify") $+        With (. Sexp.sym "may-simplify") $+          With+            (. Sexp.sym "private")+            End++-- | A 'KernelBody' does not return an ordinary 'Result'.  Instead, it+-- returns a list of these.+data KernelResult+  = -- | Each "worker" in the kernel returns this.+    -- Whether this is a result-per-thread or a+    -- result-per-group depends on where the 'SegOp' occurs.+    Returns ResultManifest SubExp+  | WriteReturns+      [SubExp] -- Size of array.  Must match number of dims.+      VName -- Which array+      [(Slice SubExp, SubExp)]+  | -- Arbitrary number of index/value pairs.+    ConcatReturns+      SplitOrdering -- Permuted?+      SubExp -- The final size.+      SubExp -- Per-thread/group (max) chunk size.+      VName -- Chunk by this worker.+  | TileReturns+      [(SubExp, SubExp)] -- Total/tile for each dimension+      VName -- Tile written by this worker.+      -- The TileReturns must not expect more than one+      -- result to be written per physical thread.+  deriving (Eq, Show, Ord, Generic)++instance SexpIso KernelResult where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "returns") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+        With (. Sexp.list (Sexp.el (Sexp.sym "write-returns") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+          With (. Sexp.list (Sexp.el (Sexp.sym "concat-returns") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+            With+              (. Sexp.list (Sexp.el (Sexp.sym "tile-returns") >>> Sexp.el sexpIso >>> Sexp.el sexpIso))+              End++-- | Get the root t'SubExp' corresponding values for a 'KernelResult'.+kernelResultSubExp :: KernelResult -> SubExp+kernelResultSubExp (Returns _ se) = se+kernelResultSubExp (WriteReturns _ arr _) = Var arr+kernelResultSubExp (ConcatReturns _ _ _ v) = Var v+kernelResultSubExp (TileReturns _ v) = Var v++instance FreeIn KernelResult where+  freeIn' (Returns _ what) = freeIn' what+  freeIn' (WriteReturns rws arr res) = freeIn' rws <> freeIn' arr <> freeIn' res+  freeIn' (ConcatReturns o w per_thread_elems v) =+    freeIn' o <> freeIn' w <> freeIn' per_thread_elems <> freeIn' v+  freeIn' (TileReturns dims v) =+    freeIn' dims <> freeIn' v++instance ASTLore lore => FreeIn (KernelBody lore) where+  freeIn' (KernelBody dec stms res) =+    fvBind bound_in_stms $ freeIn' dec <> freeIn' stms <> freeIn' res+    where+      bound_in_stms = foldMap boundByStm stms++instance ASTLore lore => Substitute (KernelBody lore) where+  substituteNames subst (KernelBody dec stms res) =+    KernelBody+      (substituteNames subst dec)+      (substituteNames subst stms)+      (substituteNames subst res)++instance Substitute KernelResult where+  substituteNames subst (Returns manifest se) =+    Returns manifest (substituteNames subst se)+  substituteNames subst (WriteReturns rws arr res) =+    WriteReturns+      (substituteNames subst rws)+      (substituteNames subst arr)+      (substituteNames subst res)+  substituteNames subst (ConcatReturns o w per_thread_elems v) =+    ConcatReturns+      (substituteNames subst o)+      (substituteNames subst w)+      (substituteNames subst per_thread_elems)+      (substituteNames subst v)+  substituteNames subst (TileReturns dims v) =+    TileReturns (substituteNames subst dims) (substituteNames subst v)++instance ASTLore lore => Rename (KernelBody lore) where+  rename (KernelBody dec stms res) = do+    dec' <- rename dec+    renamingStms stms $ \stms' ->+      KernelBody dec' stms' <$> rename res++instance Rename KernelResult where+  rename = substituteRename++-- | Perform alias analysis on a 'KernelBody'.+aliasAnalyseKernelBody ::+  ( ASTLore lore,+    CanBeAliased (Op lore)+  ) =>+  KernelBody lore ->+  KernelBody (Aliases lore)+aliasAnalyseKernelBody (KernelBody dec stms res) =+  let Body dec' stms' _ = Alias.analyseBody mempty $ Body dec stms []+   in KernelBody dec' stms' res++removeKernelBodyAliases ::+  CanBeAliased (Op lore) =>+  KernelBody (Aliases lore) ->+  KernelBody lore+removeKernelBodyAliases (KernelBody (_, dec) stms res) =+  KernelBody dec (fmap removeStmAliases stms) res++removeKernelBodyWisdom ::+  CanBeWise (Op lore) =>+  KernelBody (Wise lore) ->+  KernelBody lore+removeKernelBodyWisdom (KernelBody dec stms res) =+  let Body dec' stms' _ = removeBodyWisdom $ Body dec stms []+   in KernelBody dec' stms' res++-- | The variables consumed in the kernel body.+consumedInKernelBody ::+  Aliased lore =>+  KernelBody lore ->+  Names+consumedInKernelBody (KernelBody dec stms res) =+  consumedInBody (Body dec stms []) <> mconcat (map consumedByReturn res)+  where+    consumedByReturn (WriteReturns _ a _) = oneName a+    consumedByReturn _ = mempty++checkKernelBody ::+  TC.Checkable lore =>+  [Type] ->+  KernelBody (Aliases lore) ->+  TC.TypeM lore ()+checkKernelBody ts (KernelBody (_, dec) stms kres) = do+  TC.checkBodyLore dec+  TC.checkStms stms $ do+    unless (length ts == length kres) $+      TC.bad $+        TC.TypeError $+          "Kernel return type is " ++ prettyTuple ts+            ++ ", but body returns "+            ++ show (length kres)+            ++ " values."+    zipWithM_ checkKernelResult kres ts+  where+    checkKernelResult (Returns _ what) t =+      TC.require [t] what+    checkKernelResult (WriteReturns rws arr res) t = do+      mapM_ (TC.require [Prim int64]) rws+      arr_t <- lookupType arr+      forM_ res $ \(slice, e) -> do+        mapM_ (traverse $ TC.require [Prim int64]) slice+        TC.require [t] e+        unless (arr_t == t `arrayOfShape` Shape rws) $+          TC.bad $+            TC.TypeError $+              "WriteReturns returning "+                ++ pretty e+                ++ " of type "+                ++ pretty t+                ++ ", shape="+                ++ pretty rws+                ++ ", but destination array has type "+                ++ pretty arr_t+      TC.consume =<< TC.lookupAliases arr+    checkKernelResult (ConcatReturns o w per_thread_elems v) t = do+      case o of+        SplitContiguous -> return ()+        SplitStrided stride -> TC.require [Prim int64] stride+      TC.require [Prim int64] w+      TC.require [Prim int64] per_thread_elems+      vt <- lookupType v+      unless (vt == t `arrayOfRow` arraySize 0 vt) $+        TC.bad $ TC.TypeError $ "Invalid type for ConcatReturns " ++ pretty v+    checkKernelResult (TileReturns dims v) t = do+      forM_ dims $ \(dim, tile) -> do+        TC.require [Prim int64] dim+        TC.require [Prim int64] tile+      vt <- lookupType v+      unless (vt == t `arrayOfShape` Shape (map snd dims)) $+        TC.bad $ TC.TypeError $ "Invalid type for TileReturns " ++ pretty v++kernelBodyMetrics :: OpMetrics (Op lore) => KernelBody lore -> MetricsM ()+kernelBodyMetrics = mapM_ stmMetrics . kernelBodyStms++instance PrettyLore lore => Pretty (KernelBody lore) where+  ppr (KernelBody _ stms res) =+    PP.stack (map ppr (stmsToList stms))+      </> text "return" <+> PP.braces (PP.commasep $ map ppr res)++instance Pretty KernelResult where+  ppr (Returns ResultNoSimplify what) =+    text "returns (manifest)" <+> ppr what+  ppr (Returns ResultPrivate what) =+    text "returns (private)" <+> ppr what+  ppr (Returns ResultMaySimplify what) =+    text "returns" <+> ppr what+  ppr (WriteReturns rws arr res) =+    ppr arr <+> text "with" <+> PP.apply (map ppRes res)+    where+      ppRes (is, e) =+        PP.brackets (PP.commasep $ zipWith f is rws) <+> text "<-" <+> ppr e+      f i rw = ppr i <+> text "<" <+> ppr rw+  ppr (ConcatReturns o w per_thread_elems v) =+    text "concat" <> suff+      <> parens (commasep [ppr w, ppr per_thread_elems]) <+> ppr v+    where+      suff = case o of+        SplitContiguous -> mempty+        SplitStrided stride -> text "Strided" <> parens (ppr stride)+  ppr (TileReturns dims v) =+    text "tile"+      <> parens (commasep $ map onDim dims) <+> ppr v+    where+      onDim (dim, tile) = ppr dim <+> text "/" <+> ppr tile++-- | Do we need group-virtualisation when generating code for the+-- segmented operation?  In most cases, we do, but for some simple+-- kernels, we compute the full number of groups in advance, and then+-- virtualisation is an unnecessary (but generally very small)+-- overhead.  This only really matters for fairly trivial but very+-- wide @map@ kernels where each thread performs constant-time work on+-- scalars.+data SegVirt+  = SegVirt+  | SegNoVirt+  | -- | Not only do we not need virtualisation, but we _guarantee_+    -- that all physical threads participate in the work.  This can+    -- save some checks in code generation.+    SegNoVirtFull+  deriving (Eq, Ord, Show, Generic)++instance SexpIso SegVirt where+  sexpIso =+    match $+      With (. Sexp.sym "virt") $+        With (. Sexp.sym "no-virt") $+          With+            (. Sexp.sym "no-virt-ful")+            End++-- | Index space of a 'SegOp'.+data SegSpace = SegSpace+  { -- | Flat physical index corresponding to the+    -- dimensions (at code generation used for a+    -- thread ID or similar).+    segFlat :: VName,+    unSegSpace :: [(VName, SubExp)]+  }+  deriving (Eq, Ord, Show, Generic)++instance SexpIso SegSpace where+  sexpIso = with $ \segspace ->+    Sexp.list+      ( Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> segspace++-- | The sizes spanned by the indexes of the 'SegSpace'.+segSpaceDims :: SegSpace -> [SubExp]+segSpaceDims (SegSpace _ space) = map snd space++-- | A 'Scope' containing all the identifiers brought into scope by+-- this 'SegSpace'.+scopeOfSegSpace :: SegSpace -> Scope lore+scopeOfSegSpace (SegSpace phys space) =+  M.fromList $ zip (phys : map fst space) $ repeat $ IndexName Int64++checkSegSpace :: TC.Checkable lore => SegSpace -> TC.TypeM lore ()+checkSegSpace (SegSpace _ dims) =+  mapM_ (TC.require [Prim int64] . snd) dims++-- | A 'SegOp' is semantically a perfectly nested stack of maps, on+-- top of some bottommost computation (scalar computation, reduction,+-- scan, or histogram).  The 'SegSpace' encodes the original map+-- structure.+--+-- All 'SegOp's are parameterised by the representation of their body,+-- as well as a *level*.  The *level* is a representation-specific bit+-- of information.  For example, in GPU backends, it is used to+-- indicate whether the 'SegOp' is expected to run at the thread-level+-- or the group-level.+data SegOp lvl lore+  = SegMap lvl SegSpace [Type] (KernelBody lore)+  | -- | The KernelSpace must always have at least two dimensions,+    -- implying that the result of a SegRed is always an array.+    SegRed lvl SegSpace [SegBinOp lore] [Type] (KernelBody lore)+  | SegScan lvl SegSpace [SegBinOp lore] [Type] (KernelBody lore)+  | SegHist lvl SegSpace [HistOp lore] [Type] (KernelBody lore)+  deriving (Eq, Ord, Show, Generic)++instance (SexpIso lvl, Decorations lore) => SexpIso (SegOp lvl lore) where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "segmap") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+        With (. Sexp.list (Sexp.el (Sexp.sym "segred") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+          With (. Sexp.list (Sexp.el (Sexp.sym "segscan") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+            With+              (. Sexp.list (Sexp.el (Sexp.sym "seghist") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso))+              End++-- | The level of a 'SegOp'.+segLevel :: SegOp lvl lore -> lvl+segLevel (SegMap lvl _ _ _) = lvl+segLevel (SegRed lvl _ _ _ _) = lvl+segLevel (SegScan lvl _ _ _ _) = lvl+segLevel (SegHist lvl _ _ _ _) = lvl++-- | The space of a 'SegOp'.+segSpace :: SegOp lvl lore -> SegSpace+segSpace (SegMap _ lvl _ _) = lvl+segSpace (SegRed _ lvl _ _ _) = lvl+segSpace (SegScan _ lvl _ _ _) = lvl+segSpace (SegHist _ lvl _ _ _) = lvl++segResultShape :: SegSpace -> Type -> KernelResult -> Type+segResultShape _ t (WriteReturns rws _ _) =+  t `arrayOfShape` Shape rws+segResultShape space t (Returns _ _) =+  foldr (flip arrayOfRow) t $ segSpaceDims space+segResultShape _ t (ConcatReturns _ w _ _) =+  t `arrayOfRow` w+segResultShape _ t (TileReturns dims _) =+  t `arrayOfShape` Shape (map fst dims)++-- | The return type of a 'SegOp'.+segOpType :: SegOp lvl lore -> [Type]+segOpType (SegMap _ space ts kbody) =+  zipWith (segResultShape space) ts $ kernelBodyResult kbody+segOpType (SegRed _ space reds ts kbody) =+  red_ts+    ++ zipWith+      (segResultShape space)+      map_ts+      (drop (length red_ts) $ kernelBodyResult kbody)+  where+    map_ts = drop (length red_ts) ts+    segment_dims = init $ segSpaceDims space+    red_ts = do+      op <- reds+      let shape = Shape segment_dims <> segBinOpShape op+      map (`arrayOfShape` shape) (lambdaReturnType $ segBinOpLambda op)+segOpType (SegScan _ space scans ts kbody) =+  scan_ts+    ++ zipWith+      (segResultShape space)+      map_ts+      (drop (length scan_ts) $ kernelBodyResult kbody)+  where+    map_ts = drop (length scan_ts) ts+    scan_ts = do+      op <- scans+      let shape = Shape (segSpaceDims space) <> segBinOpShape op+      map (`arrayOfShape` shape) (lambdaReturnType $ segBinOpLambda op)+segOpType (SegHist _ space ops _ _) = do+  op <- ops+  let shape = Shape (segment_dims <> [histWidth op]) <> histShape op+  map (`arrayOfShape` shape) (lambdaReturnType $ histOp op)+  where+    dims = segSpaceDims space+    segment_dims = init dims++instance TypedOp (SegOp lvl lore) where+  opType = pure . staticShapes . segOpType++instance+  (ASTLore lore, Aliased lore, ASTConstraints lvl) =>+  AliasedOp (SegOp lvl lore)+  where+  opAliases = map (const mempty) . segOpType++  consumedInOp (SegMap _ _ _ kbody) =+    consumedInKernelBody kbody+  consumedInOp (SegRed _ _ _ _ kbody) =+    consumedInKernelBody kbody+  consumedInOp (SegScan _ _ _ _ kbody) =+    consumedInKernelBody kbody+  consumedInOp (SegHist _ _ ops _ kbody) =+    namesFromList (concatMap histDest ops) <> consumedInKernelBody kbody++-- | Type check a 'SegOp', given a checker for its level.+typeCheckSegOp ::+  TC.Checkable lore =>+  (lvl -> TC.TypeM lore ()) ->+  SegOp lvl (Aliases lore) ->+  TC.TypeM lore ()+typeCheckSegOp checkLvl (SegMap lvl space ts kbody) = do+  checkLvl lvl+  checkScanRed space [] ts kbody+typeCheckSegOp checkLvl (SegRed lvl space reds ts body) = do+  checkLvl lvl+  checkScanRed space reds' ts body+  where+    reds' =+      zip3+        (map segBinOpLambda reds)+        (map segBinOpNeutral reds)+        (map segBinOpShape reds)+typeCheckSegOp checkLvl (SegScan lvl space scans ts body) = do+  checkLvl lvl+  checkScanRed space scans' ts body+  where+    scans' =+      zip3+        (map segBinOpLambda scans)+        (map segBinOpNeutral scans)+        (map segBinOpShape scans)+typeCheckSegOp checkLvl (SegHist lvl space ops ts kbody) = do+  checkLvl lvl+  checkSegSpace space+  mapM_ TC.checkType ts++  TC.binding (scopeOfSegSpace space) $ do+    nes_ts <- forM ops $ \(HistOp dest_w rf dests nes shape op) -> do+      TC.require [Prim int64] dest_w+      TC.require [Prim int64] rf+      nes' <- mapM TC.checkArg nes+      mapM_ (TC.require [Prim int64]) $ shapeDims shape++      -- Operator type must match the type of neutral elements.+      let stripVecDims = stripArray $ shapeRank shape+      TC.checkLambda op $ map (TC.noArgAliases . first stripVecDims) $ nes' ++ nes'+      let nes_t = map TC.argType nes'+      unless (nes_t == lambdaReturnType op) $+        TC.bad $+          TC.TypeError $+            "SegHist operator has return type "+              ++ prettyTuple (lambdaReturnType op)+              ++ " but neutral element has type "+              ++ prettyTuple nes_t++      -- Arrays must have proper type.+      let dest_shape = Shape (segment_dims <> [dest_w]) <> shape+      forM_ (zip nes_t dests) $ \(t, dest) -> do+        TC.requireI [t `arrayOfShape` dest_shape] dest+        TC.consume =<< TC.lookupAliases dest++      return $ map (`arrayOfShape` shape) nes_t++    checkKernelBody ts kbody++    -- Return type of bucket function must be an index for each+    -- operation followed by the values to write.+    let bucket_ret_t = replicate (length ops) (Prim int64) ++ concat nes_ts+    unless (bucket_ret_t == ts) $+      TC.bad $+        TC.TypeError $+          "SegHist body has return type "+            ++ prettyTuple ts+            ++ " but should have type "+            ++ prettyTuple bucket_ret_t+  where+    segment_dims = init $ segSpaceDims space++checkScanRed ::+  TC.Checkable lore =>+  SegSpace ->+  [(Lambda (Aliases lore), [SubExp], Shape)] ->+  [Type] ->+  KernelBody (Aliases lore) ->+  TC.TypeM lore ()+checkScanRed space ops ts kbody = do+  checkSegSpace space+  mapM_ TC.checkType ts++  TC.binding (scopeOfSegSpace space) $ do+    ne_ts <- forM ops $ \(lam, nes, shape) -> do+      mapM_ (TC.require [Prim int64]) $ shapeDims shape+      nes' <- mapM TC.checkArg nes++      -- Operator type must match the type of neutral elements.+      TC.checkLambda lam $ map TC.noArgAliases $ nes' ++ nes'+      let nes_t = map TC.argType nes'++      unless (lambdaReturnType lam == nes_t) $+        TC.bad $ TC.TypeError "wrong type for operator or neutral elements."++      return $ map (`arrayOfShape` shape) nes_t++    let expecting = concat ne_ts+        got = take (length expecting) ts+    unless (expecting == got) $+      TC.bad $+        TC.TypeError $+          "Wrong return for body (does not match neutral elements; expected "+            ++ pretty expecting+            ++ "; found "+            ++ pretty got+            ++ ")"++    checkKernelBody ts kbody++-- | Like 'Mapper', but just for 'SegOp's.+data SegOpMapper lvl flore tlore m = SegOpMapper+  { mapOnSegOpSubExp :: SubExp -> m SubExp,+    mapOnSegOpLambda :: Lambda flore -> m (Lambda tlore),+    mapOnSegOpBody :: KernelBody flore -> m (KernelBody tlore),+    mapOnSegOpVName :: VName -> m VName,+    mapOnSegOpLevel :: lvl -> m lvl+  }++-- | A mapper that simply returns the 'SegOp' verbatim.+identitySegOpMapper :: Monad m => SegOpMapper lvl lore lore m+identitySegOpMapper =+  SegOpMapper+    { mapOnSegOpSubExp = return,+      mapOnSegOpLambda = return,+      mapOnSegOpBody = return,+      mapOnSegOpVName = return,+      mapOnSegOpLevel = return+    }++mapOnSegSpace ::+  Monad f =>+  SegOpMapper lvl flore tlore f ->+  SegSpace ->+  f SegSpace+mapOnSegSpace tv (SegSpace phys dims) =+  SegSpace phys <$> traverse (traverse $ mapOnSegOpSubExp tv) dims++mapSegBinOp ::+  Monad m =>+  SegOpMapper lvl flore tlore m ->+  SegBinOp flore ->+  m (SegBinOp tlore)+mapSegBinOp tv (SegBinOp comm red_op nes shape) =+  SegBinOp comm+    <$> mapOnSegOpLambda tv red_op+    <*> mapM (mapOnSegOpSubExp tv) nes+    <*> (Shape <$> mapM (mapOnSegOpSubExp tv) (shapeDims shape))++-- | Apply a 'SegOpMapper' to the given 'SegOp'.+mapSegOpM ::+  (Applicative m, Monad m) =>+  SegOpMapper lvl flore tlore m ->+  SegOp lvl flore ->+  m (SegOp lvl tlore)+mapSegOpM tv (SegMap lvl space ts body) =+  SegMap+    <$> mapOnSegOpLevel tv lvl+    <*> mapOnSegSpace tv space+    <*> mapM (mapOnSegOpType tv) ts+    <*> mapOnSegOpBody tv body+mapSegOpM tv (SegRed lvl space reds ts lam) =+  SegRed+    <$> mapOnSegOpLevel tv lvl+    <*> mapOnSegSpace tv space+    <*> mapM (mapSegBinOp tv) reds+    <*> mapM (mapOnType $ mapOnSegOpSubExp tv) ts+    <*> mapOnSegOpBody tv lam+mapSegOpM tv (SegScan lvl space scans ts body) =+  SegScan+    <$> mapOnSegOpLevel tv lvl+    <*> mapOnSegSpace tv space+    <*> mapM (mapSegBinOp tv) scans+    <*> mapM (mapOnType $ mapOnSegOpSubExp tv) ts+    <*> mapOnSegOpBody tv body+mapSegOpM tv (SegHist lvl space ops ts body) =+  SegHist+    <$> mapOnSegOpLevel tv lvl+    <*> mapOnSegSpace tv space+    <*> mapM onHistOp ops+    <*> mapM (mapOnType $ mapOnSegOpSubExp tv) ts+    <*> mapOnSegOpBody tv body+  where+    onHistOp (HistOp w rf arrs nes shape op) =+      HistOp <$> mapOnSegOpSubExp tv w+        <*> mapOnSegOpSubExp tv rf+        <*> mapM (mapOnSegOpVName tv) arrs+        <*> mapM (mapOnSegOpSubExp tv) nes+        <*> (Shape <$> mapM (mapOnSegOpSubExp tv) (shapeDims shape))+        <*> mapOnSegOpLambda tv op++mapOnSegOpType ::+  Monad m =>+  SegOpMapper lvl flore tlore m ->+  Type ->+  m Type+mapOnSegOpType _tv t@Prim {} = pure t+mapOnSegOpType tv (Array pt shape u) = Array pt <$> f shape <*> pure u+  where+    f (Shape dims) = Shape <$> mapM (mapOnSegOpSubExp tv) dims+mapOnSegOpType _tv (Mem s) = pure $ Mem s++instance+  (ASTLore lore, Substitute lvl) =>+  Substitute (SegOp lvl lore)+  where+  substituteNames subst = runIdentity . mapSegOpM substitute+    where+      substitute =+        SegOpMapper+          { mapOnSegOpSubExp = return . substituteNames subst,+            mapOnSegOpLambda = return . substituteNames subst,+            mapOnSegOpBody = return . substituteNames subst,+            mapOnSegOpVName = return . substituteNames subst,+            mapOnSegOpLevel = return . substituteNames subst+          }++instance+  (ASTLore lore, ASTConstraints lvl) =>+  Rename (SegOp lvl lore)+  where+  rename = mapSegOpM renamer+    where+      renamer = SegOpMapper rename rename rename rename rename++instance+  (ASTLore lore, FreeIn (LParamInfo lore), FreeIn lvl) =>+  FreeIn (SegOp lvl lore)+  where+  freeIn' e = flip execState mempty $ mapSegOpM free e+    where+      walk f x = modify (<> f x) >> return x+      free =+        SegOpMapper+          { mapOnSegOpSubExp = walk freeIn',+            mapOnSegOpLambda = walk freeIn',+            mapOnSegOpBody = walk freeIn',+            mapOnSegOpVName = walk freeIn',+            mapOnSegOpLevel = walk freeIn'+          }++instance OpMetrics (Op lore) => OpMetrics (SegOp lvl lore) where+  opMetrics (SegMap _ _ _ body) =+    inside "SegMap" $ kernelBodyMetrics body+  opMetrics (SegRed _ _ reds _ body) =+    inside "SegRed" $ do+      mapM_ (lambdaMetrics . segBinOpLambda) reds+      kernelBodyMetrics body+  opMetrics (SegScan _ _ scans _ body) =+    inside "SegScan" $ do+      mapM_ (lambdaMetrics . segBinOpLambda) scans+      kernelBodyMetrics body+  opMetrics (SegHist _ _ ops _ body) =+    inside "SegHist" $ do+      mapM_ (lambdaMetrics . histOp) ops+      kernelBodyMetrics body++instance Pretty SegSpace where+  ppr (SegSpace phys dims) =+    parens+      ( commasep $ do+          (i, d) <- dims+          return $ ppr i <+> "<" <+> ppr d+      )+      <+> parens (text "~" <> ppr phys)++instance PrettyLore lore => Pretty (SegBinOp lore) where+  ppr (SegBinOp comm lam nes shape) =+    PP.braces (PP.commasep $ map ppr nes) <> PP.comma+      </> ppr shape <> PP.comma+      </> comm' <> ppr lam+    where+      comm' = case comm of+        Commutative -> text "commutative "+        Noncommutative -> mempty++instance (PrettyLore lore, PP.Pretty lvl) => PP.Pretty (SegOp lvl lore) where+  ppr (SegMap lvl space ts body) =+    text "segmap" <> ppr lvl+      </> PP.align (ppr space)+      <+> PP.colon+      <+> ppTuple' ts+      <+> PP.nestedBlock "{" "}" (ppr body)+  ppr (SegRed lvl space reds ts body) =+    text "segred" <> ppr lvl+      </> PP.parens (PP.braces (mconcat $ intersperse (PP.comma <> PP.line) $ map ppr reds))+      </> PP.align (ppr space)+      <+> PP.colon+      <+> ppTuple' ts+      <+> PP.nestedBlock "{" "}" (ppr body)+  ppr (SegScan lvl space scans ts body) =+    text "segscan" <> ppr lvl+      </> PP.parens (PP.braces (mconcat $ intersperse (PP.comma <> PP.line) $ map ppr scans))+      </> PP.align (ppr space)+      <+> PP.colon+      <+> ppTuple' ts+      <+> PP.nestedBlock "{" "}" (ppr body)+  ppr (SegHist lvl space ops ts body) =+    text "seghist" <> ppr lvl+      </> ppr lvl+      </> PP.parens (PP.braces (mconcat $ intersperse (PP.comma <> PP.line) $ map ppOp ops))+      </> PP.align (ppr space)+      <+> PP.colon+      <+> ppTuple' ts+      <+> PP.nestedBlock "{" "}" (ppr body)+    where+      ppOp (HistOp w rf dests nes shape op) =+        ppr w <> PP.comma <+> ppr rf <> PP.comma+          </> PP.braces (PP.commasep $ map ppr dests) <> PP.comma+          </> PP.braces (PP.commasep $ map ppr nes) <> PP.comma+          </> ppr shape <> PP.comma+          </> ppr op++instance+  ( ASTLore lore,+    ASTLore (Aliases lore),+    CanBeAliased (Op lore),+    ASTConstraints lvl+  ) =>+  CanBeAliased (SegOp lvl lore)+  where+  type OpWithAliases (SegOp lvl lore) = SegOp lvl (Aliases lore)++  addOpAliases = runIdentity . mapSegOpM alias+    where+      alias =+        SegOpMapper+          return+          (return . Alias.analyseLambda)+          (return . aliasAnalyseKernelBody)+          return+          return++  removeOpAliases = runIdentity . mapSegOpM remove+    where+      remove =+        SegOpMapper+          return+          (return . removeLambdaAliases)+          (return . removeKernelBodyAliases)+          return+          return++instance+  (CanBeWise (Op lore), ASTLore lore, ASTConstraints lvl) =>+  CanBeWise (SegOp lvl lore)+  where+  type OpWithWisdom (SegOp lvl lore) = SegOp lvl (Wise lore)++  removeOpWisdom = runIdentity . mapSegOpM remove+    where+      remove =+        SegOpMapper+          return+          (return . removeLambdaWisdom)+          (return . removeKernelBodyWisdom)+          return+          return++instance ASTLore lore => ST.IndexOp (SegOp lvl lore) where+  indexOp vtable k (SegMap _ space _ kbody) is = do+    Returns ResultMaySimplify se <- maybeNth k $ kernelBodyResult kbody+    guard $ length gtids <= length is+    let idx_table = M.fromList $ zip gtids $ map (ST.Indexed mempty . untyped) is+        idx_table' = foldl' expandIndexedTable idx_table $ kernelBodyStms kbody+    case se of+      Var v -> M.lookup v idx_table'+      _ -> Nothing+    where+      (gtids, _) = unzip $ unSegSpace space+      -- Indexes in excess of what is used to index through the+      -- segment dimensions.+      excess_is = drop (length gtids) is++      expandIndexedTable table stm+        | [v] <- patternNames $ stmPattern stm,+          Just (pe, cs) <-+            runWriterT $ primExpFromExp (asPrimExp table) $ stmExp stm =+          M.insert v (ST.Indexed (stmCerts stm <> cs) pe) table+        | [v] <- patternNames $ stmPattern stm,+          BasicOp (Index arr slice) <- stmExp stm,+          length (sliceDims slice) == length excess_is,+          arr `ST.elem` vtable,+          Just (slice', cs) <- asPrimExpSlice table slice =+          let idx =+                ST.IndexedArray+                  (stmCerts stm <> cs)+                  arr+                  (fixSlice (map (fmap isInt64) slice') excess_is)+           in M.insert v idx table+        | otherwise =+          table++      asPrimExpSlice table =+        runWriterT . mapM (traverse (primExpFromSubExpM (asPrimExp table)))++      asPrimExp table v+        | Just (ST.Indexed cs e) <- M.lookup v table = tell cs >> return e+        | Just (Prim pt) <- ST.lookupType v vtable =+          return $ LeafExp v pt+        | otherwise = lift Nothing+  indexOp _ _ _ _ = Nothing++instance+  (ASTLore lore, ASTConstraints lvl) =>+  IsOp (SegOp lvl lore)+  where+  cheapOp _ = False+  safeOp _ = True++--- Simplification++instance Engine.Simplifiable SplitOrdering where+  simplify SplitContiguous =+    return SplitContiguous+  simplify (SplitStrided stride) =+    SplitStrided <$> Engine.simplify stride++instance Engine.Simplifiable SegSpace where+  simplify (SegSpace phys dims) =+    SegSpace phys <$> mapM (traverse Engine.simplify) dims++instance Engine.Simplifiable KernelResult where+  simplify (Returns manifest what) =+    Returns manifest <$> Engine.simplify what+  simplify (WriteReturns ws a res) =+    WriteReturns <$> Engine.simplify ws <*> Engine.simplify a <*> Engine.simplify res+  simplify (ConcatReturns o w pte what) =+    ConcatReturns+      <$> Engine.simplify o+      <*> Engine.simplify w+      <*> Engine.simplify pte+      <*> Engine.simplify what+  simplify (TileReturns dims what) =+    TileReturns <$> Engine.simplify dims <*> Engine.simplify what++mkWiseKernelBody ::+  (ASTLore lore, CanBeWise (Op lore)) =>+  BodyDec lore ->+  Stms (Wise lore) ->+  [KernelResult] ->+  KernelBody (Wise lore)+mkWiseKernelBody dec bnds res =+  let Body dec' _ _ = mkWiseBody dec bnds res_vs+   in KernelBody dec' bnds res+  where+    res_vs = map kernelResultSubExp res++mkKernelBodyM ::+  MonadBinder m =>+  Stms (Lore m) ->+  [KernelResult] ->+  m (KernelBody (Lore m))+mkKernelBodyM stms kres = do+  Body dec' _ _ <- mkBodyM stms res_ses+  return $ KernelBody dec' stms kres+  where+    res_ses = map kernelResultSubExp kres++simplifyKernelBody ::+  (Engine.SimplifiableLore lore, BodyDec lore ~ ()) =>+  SegSpace ->+  KernelBody lore ->+  Engine.SimpleM lore (KernelBody (Wise lore), Stms (Wise lore))+simplifyKernelBody space (KernelBody _ stms res) = do+  par_blocker <- Engine.asksEngineEnv $ Engine.blockHoistPar . Engine.envHoistBlockers++  ((body_stms, body_res), hoisted) <-+    Engine.localVtable (<> scope_vtable) $+      Engine.localVtable (\vtable -> vtable {ST.simplifyMemory = True}) $+        Engine.blockIf+          ( Engine.hasFree bound_here+              `Engine.orIf` Engine.isOp+              `Engine.orIf` par_blocker+              `Engine.orIf` Engine.isConsumed+          )+          $ Engine.simplifyStms stms $ do+            res' <-+              Engine.localVtable (ST.hideCertified $ namesFromList $ M.keys $ scopeOf stms) $+                mapM Engine.simplify res+            return ((res', UT.usages $ freeIn res'), mempty)++  return (mkWiseKernelBody () body_stms body_res, hoisted)+  where+    scope_vtable = segSpaceSymbolTable space+    bound_here = namesFromList $ M.keys $ scopeOfSegSpace space++segSpaceSymbolTable :: ASTLore lore => SegSpace -> ST.SymbolTable lore+segSpaceSymbolTable (SegSpace flat gtids_and_dims) =+  foldl' f (ST.fromScope $ M.singleton flat $ IndexName Int64) gtids_and_dims+  where+    f vtable (gtid, dim) = ST.insertLoopVar gtid Int64 dim vtable++simplifySegBinOp ::+  Engine.SimplifiableLore lore =>+  SegBinOp lore ->+  Engine.SimpleM lore (SegBinOp (Wise lore), Stms (Wise lore))+simplifySegBinOp (SegBinOp comm lam nes shape) = do+  (lam', hoisted) <-+    Engine.localVtable (\vtable -> vtable {ST.simplifyMemory = True}) $+      Engine.simplifyLambda lam+  shape' <- Engine.simplify shape+  nes' <- mapM Engine.simplify nes+  return (SegBinOp comm lam' nes' shape', hoisted)++-- | Simplify the given 'SegOp'.+simplifySegOp ::+  ( Engine.SimplifiableLore lore,+    BodyDec lore ~ (),+    Engine.Simplifiable lvl+  ) =>+  SegOp lvl lore ->+  Engine.SimpleM lore (SegOp lvl (Wise lore), Stms (Wise lore))+simplifySegOp (SegMap lvl space ts kbody) = do+  (lvl', space', ts') <- Engine.simplify (lvl, space, ts)+  (kbody', body_hoisted) <- simplifyKernelBody space kbody+  return+    ( SegMap lvl' space' ts' kbody',+      body_hoisted+    )+simplifySegOp (SegRed lvl space reds ts kbody) = do+  (lvl', space', ts') <- Engine.simplify (lvl, space, ts)+  (reds', reds_hoisted) <-+    Engine.localVtable (<> scope_vtable) $+      unzip <$> mapM simplifySegBinOp reds+  (kbody', body_hoisted) <- simplifyKernelBody space kbody++  return+    ( SegRed lvl' space' reds' ts' kbody',+      mconcat reds_hoisted <> body_hoisted+    )+  where+    scope = scopeOfSegSpace space+    scope_vtable = ST.fromScope scope+simplifySegOp (SegScan lvl space scans ts kbody) = do+  (lvl', space', ts') <- Engine.simplify (lvl, space, ts)+  (scans', scans_hoisted) <-+    Engine.localVtable (<> scope_vtable) $+      unzip <$> mapM simplifySegBinOp scans+  (kbody', body_hoisted) <- simplifyKernelBody space kbody++  return+    ( SegScan lvl' space' scans' ts' kbody',+      mconcat scans_hoisted <> body_hoisted+    )+  where+    scope = scopeOfSegSpace space+    scope_vtable = ST.fromScope scope+simplifySegOp (SegHist lvl space ops ts kbody) = do+  (lvl', space', ts') <- Engine.simplify (lvl, space, ts)++  (ops', ops_hoisted) <- fmap unzip $+    forM ops $+      \(HistOp w rf arrs nes dims lam) -> do+        w' <- Engine.simplify w+        rf' <- Engine.simplify rf+        arrs' <- Engine.simplify arrs+        nes' <- Engine.simplify nes+        dims' <- Engine.simplify dims+        (lam', op_hoisted) <-+          Engine.localVtable (<> scope_vtable) $+            Engine.localVtable (\vtable -> vtable {ST.simplifyMemory = True}) $+              Engine.simplifyLambda lam+        return+          ( HistOp w' rf' arrs' nes' dims' lam',+            op_hoisted+          )++  (kbody', body_hoisted) <- simplifyKernelBody space kbody++  return+    ( SegHist lvl' space' ops' ts' kbody',+      mconcat ops_hoisted <> body_hoisted+    )+  where+    scope = scopeOfSegSpace space+    scope_vtable = ST.fromScope scope++-- | Does this lore contain 'SegOp's in its t'Op's?  A lore must be an+-- instance of this class for the simplification rules to work.+class HasSegOp lore where+  type SegOpLevel lore+  asSegOp :: Op lore -> Maybe (SegOp (SegOpLevel lore) lore)+  segOp :: SegOp (SegOpLevel lore) lore -> Op lore++-- | Simplification rules for simplifying 'SegOp's.+segOpRules ::+  (HasSegOp lore, BinderOps lore, Bindable lore) =>+  RuleBook lore+segOpRules =+  ruleBook [RuleOp segOpRuleTopDown] [RuleOp segOpRuleBottomUp]++segOpRuleTopDown ::+  (HasSegOp lore, BinderOps lore, Bindable lore) =>+  TopDownRuleOp lore+segOpRuleTopDown vtable pat dec op+  | Just op' <- asSegOp op =+    topDownSegOp vtable pat dec op'+  | otherwise =+    Skip++segOpRuleBottomUp ::+  (HasSegOp lore, BinderOps lore) =>+  BottomUpRuleOp lore+segOpRuleBottomUp vtable pat dec op+  | Just op' <- asSegOp op =+    bottomUpSegOp vtable pat dec op'+  | otherwise =+    Skip++topDownSegOp ::+  (HasSegOp lore, BinderOps lore, Bindable lore) =>+  ST.SymbolTable lore ->+  Pattern lore ->+  StmAux (ExpDec lore) ->+  SegOp (SegOpLevel lore) lore ->+  Rule lore+-- If a SegOp produces something invariant to the SegOp, turn it+-- into a replicate.+topDownSegOp vtable (Pattern [] kpes) dec (SegMap lvl space ts (KernelBody _ kstms kres)) = Simplify $ do+  (ts', kpes', kres') <-+    unzip3 <$> filterM checkForInvarianceResult (zip3 ts kpes kres)++  -- Check if we did anything at all.+  when+    (kres == kres')+    cannotSimplify++  kbody <- mkKernelBodyM kstms kres'+  addStm $+    Let (Pattern [] kpes') dec $+      Op $+        segOp $+          SegMap lvl space ts' kbody+  where+    isInvariant Constant {} = True+    isInvariant (Var v) = isJust $ ST.lookup v vtable++    checkForInvarianceResult (_, pe, Returns rm se)+      | rm == ResultMaySimplify,+        isInvariant se = do+        letBindNames [patElemName pe] $+          BasicOp $ Replicate (Shape $ segSpaceDims space) se+        return False+    checkForInvarianceResult _ =+      return True++-- If a SegRed contains two reduction operations that have the same+-- vector shape, merge them together.  This saves on communication+-- overhead, but can in principle lead to more local memory usage.+topDownSegOp _ (Pattern [] pes) _ (SegRed lvl space ops ts kbody)+  | length ops > 1,+    op_groupings <-+      groupBy sameShape $+        zip ops $+          chunks (map (length . segBinOpNeutral) ops) $+            zip3 red_pes red_ts red_res,+    any ((> 1) . length) op_groupings = Simplify $ do+    let (ops', aux) = unzip $ mapMaybe combineOps op_groupings+        (red_pes', red_ts', red_res') = unzip3 $ concat aux+        pes' = red_pes' ++ map_pes+        ts' = red_ts' ++ map_ts+        kbody' = kbody {kernelBodyResult = red_res' ++ map_res}+    letBind (Pattern [] pes') $ Op $ segOp $ SegRed lvl space ops' ts' kbody'+  where+    (red_pes, map_pes) = splitAt (segBinOpResults ops) pes+    (red_ts, map_ts) = splitAt (segBinOpResults ops) ts+    (red_res, map_res) = splitAt (segBinOpResults ops) $ kernelBodyResult kbody++    sameShape (op1, _) (op2, _) = segBinOpShape op1 == segBinOpShape op2++    combineOps [] = Nothing+    combineOps (x : xs) = Just $ foldl' combine x xs++    combine (op1, op1_aux) (op2, op2_aux) =+      let lam1 = segBinOpLambda op1+          lam2 = segBinOpLambda op2+          (op1_xparams, op1_yparams) =+            splitAt (length (segBinOpNeutral op1)) $ lambdaParams lam1+          (op2_xparams, op2_yparams) =+            splitAt (length (segBinOpNeutral op2)) $ lambdaParams lam2+          lam =+            Lambda+              { lambdaParams =+                  op1_xparams ++ op2_xparams+                    ++ op1_yparams+                    ++ op2_yparams,+                lambdaReturnType = lambdaReturnType lam1 ++ lambdaReturnType lam2,+                lambdaBody =+                  mkBody (bodyStms (lambdaBody lam1) <> bodyStms (lambdaBody lam2)) $+                    bodyResult (lambdaBody lam1) <> bodyResult (lambdaBody lam2)+              }+       in ( SegBinOp+              { segBinOpComm = segBinOpComm op1 <> segBinOpComm op2,+                segBinOpLambda = lam,+                segBinOpNeutral = segBinOpNeutral op1 ++ segBinOpNeutral op2,+                segBinOpShape = segBinOpShape op1 -- Same as shape of op2 due to the grouping.+              },+            op1_aux ++ op2_aux+          )+topDownSegOp _ _ _ _ = Skip++bottomUpSegOp ::+  (HasSegOp lore, BinderOps lore) =>+  (ST.SymbolTable lore, UT.UsageTable) ->+  Pattern lore ->+  StmAux (ExpDec lore) ->+  SegOp (SegOpLevel lore) lore ->+  Rule lore+-- Some SegOp results can be moved outside the SegOp, which can+-- simplify further analysis.+bottomUpSegOp (vtable, used) (Pattern [] kpes) dec (SegMap lvl space kts (KernelBody _ kstms kres)) = Simplify $ do+  -- Iterate through the bindings.  For each, we check whether it is+  -- in kres and can be moved outside.  If so, we remove it from kres+  -- and kpes and make it a binding outside.+  (kpes', kts', kres', kstms') <-+    localScope (scopeOfSegSpace space) $+      foldM distribute (kpes, kts, kres, mempty) kstms++  when+    (kpes' == kpes)+    cannotSimplify++  kbody <-+    localScope (scopeOfSegSpace space) $+      mkKernelBodyM kstms' kres'++  addStm $+    Let (Pattern [] kpes') dec $+      Op $+        segOp $+          SegMap lvl space kts' kbody+  where+    free_in_kstms = foldMap freeIn kstms++    sliceWithGtidsFixed stm+      | Let _ _ (BasicOp (Index arr slice)) <- stm,+        space_slice <- map (DimFix . Var . fst) $ unSegSpace space,+        space_slice `isPrefixOf` slice,+        remaining_slice <- drop (length space_slice) slice,+        all (isJust . flip ST.lookup vtable) $+          namesToList $+            freeIn arr <> freeIn remaining_slice =+        Just (remaining_slice, arr)+      | otherwise =+        Nothing++    distribute (kpes', kts', kres', kstms') stm+      | Let (Pattern [] [pe]) _ _ <- stm,+        Just (remaining_slice, arr) <- sliceWithGtidsFixed stm,+        Just (kpe, kpes'', kts'', kres'') <- isResult kpes' kts' kres' pe = do+        let outer_slice =+              map+                ( \d ->+                    DimSlice+                      (constant (0 :: Int64))+                      d+                      (constant (1 :: Int64))+                )+                $ segSpaceDims space+            index kpe' =+              letBind (Pattern [] [kpe']) $+                BasicOp $+                  Index arr $+                    outer_slice <> remaining_slice+        if patElemName kpe `UT.isConsumed` used+          then do+            precopy <- newVName $ baseString (patElemName kpe) <> "_precopy"+            index kpe {patElemName = precopy}+            letBind (Pattern [] [kpe]) $ BasicOp $ Copy precopy+          else index kpe+        return+          ( kpes'',+            kts'',+            kres'',+            if patElemName pe `nameIn` free_in_kstms+              then kstms' <> oneStm stm+              else kstms'+          )+    distribute (kpes', kts', kres', kstms') stm =+      return (kpes', kts', kres', kstms' <> oneStm stm)++    isResult kpes' kts' kres' pe =+      case partition matches $ zip3 kpes' kts' kres' of+        ([(kpe, _, _)], kpes_and_kres)+          | (kpes'', kts'', kres'') <- unzip3 kpes_and_kres ->+            Just (kpe, kpes'', kts'', kres'')+        _ -> Nothing+      where+        matches (_, _, Returns _ (Var v)) = v == patElemName pe+        matches _ = False+bottomUpSegOp _ _ _ _ = Skip++--- Memory++kernelBodyReturns ::+  (Mem lore, HasScope lore m, Monad m) =>+  KernelBody lore ->+  [ExpReturns] ->+  m [ExpReturns]+kernelBodyReturns = zipWithM correct . kernelBodyResult+  where+    correct (WriteReturns _ arr _) _ = varReturns arr+    correct _ ret = return ret++-- | Like 'segOpType', but for memory representations.+segOpReturns ::+  (Mem lore, Monad m, HasScope lore m) =>+  SegOp lvl lore ->+  m [ExpReturns] segOpReturns k@(SegMap _ _ _ kbody) =   kernelBodyReturns kbody =<< (extReturns <$> opType k) segOpReturns k@(SegRed _ _ _ _ kbody) =
src/Futhark/IR/Seq.hs view
@@ -1,32 +1,33 @@-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeFamilies #-}+ -- | A sequential representation. module Futhark.IR.Seq-       ( -- * The Lore definition-         Seq+  ( -- * The Lore definition+    Seq, -         -- * Simplification-       , simplifyProg+    -- * Simplification+    simplifyProg, -         -- * Module re-exports-       , module Futhark.IR.Prop-       , module Futhark.IR.Traversals-       , module Futhark.IR.Pretty-       , module Futhark.IR.Syntax-       )+    -- * Module re-exports+    module Futhark.IR.Prop,+    module Futhark.IR.Traversals,+    module Futhark.IR.Pretty,+    module Futhark.IR.Syntax,+  ) where -import Futhark.Pass-import Futhark.IR.Syntax-import Futhark.IR.Prop-import Futhark.IR.Traversals-import Futhark.IR.Pretty import Futhark.Binder import Futhark.Construct-import qualified Futhark.TypeCheck as TypeCheck-import qualified Futhark.Optimise.Simplify.Engine as Engine+import Futhark.IR.Pretty+import Futhark.IR.Prop+import Futhark.IR.Syntax+import Futhark.IR.Traversals import qualified Futhark.Optimise.Simplify as Simplify+import qualified Futhark.Optimise.Simplify.Engine as Engine import Futhark.Optimise.Simplify.Rules+import Futhark.Pass+import qualified Futhark.TypeCheck as TypeCheck  -- | The phantom type for the Seq representation. data Seq@@ -40,7 +41,7 @@ instance TypeCheck.CheckableOp Seq where   checkOp = pure -instance TypeCheck.Checkable Seq where+instance TypeCheck.Checkable Seq  instance Bindable Seq where   mkBody = Body ()@@ -48,11 +49,11 @@   mkExpDec _ _ = ()   mkLetNames = simpleMkLetNames -instance BinderOps Seq where+instance BinderOps Seq -instance PrettyLore Seq where+instance PrettyLore Seq -instance BinderOps (Engine.Wise Seq) where+instance BinderOps (Engine.Wise Seq)  simpleSeq :: Simplify.SimpleOps Seq simpleSeq = Simplify.bindableSimpleOps (const $ pure ((), mempty))@@ -60,4 +61,5 @@ -- | Simplify a sequential program. simplifyProg :: Prog Seq -> PassM (Prog Seq) simplifyProg = Simplify.simplifyProg simpleSeq standardRules blockers-  where blockers = Engine.noExtraHoistBlockers+  where+    blockers = Engine.noExtraHoistBlockers
src/Futhark/IR/SeqMem.hs view
@@ -1,43 +1,40 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE TypeFamilies #-}+ module Futhark.IR.SeqMem-  ( SeqMem+  ( SeqMem, -  -- * Simplification-  , simplifyProg-  , simpleSeqMem+    -- * Simplification+    simplifyProg,+    simpleSeqMem,      -- * Module re-exports-  , module Futhark.IR.Mem-  , module Futhark.IR.Kernels.Kernel+    module Futhark.IR.Mem,+    module Futhark.IR.Kernels.Kernel,   )-  where+where  import Futhark.Analysis.PrimExp.Convert-import Futhark.Pass-import Futhark.IR.Syntax-import Futhark.IR.Prop-import Futhark.IR.Traversals-import Futhark.IR.Pretty import Futhark.IR.Kernels.Kernel-import qualified Futhark.TypeCheck as TC import Futhark.IR.Mem import Futhark.IR.Mem.Simplify-import Futhark.Pass.ExplicitAllocations (BinderOps(..), mkLetNamesB', mkLetNamesB'') import qualified Futhark.Optimise.Simplify.Engine as Engine+import Futhark.Pass+import Futhark.Pass.ExplicitAllocations (BinderOps (..), mkLetNamesB', mkLetNamesB'')+import qualified Futhark.TypeCheck as TC  data SeqMem  instance Decorations SeqMem where-  type LetDec     SeqMem = LetDecMem+  type LetDec SeqMem = LetDecMem   type FParamInfo SeqMem = FParamMem   type LParamInfo SeqMem = LParamMem-  type RetType    SeqMem = RetTypeMem+  type RetType SeqMem = RetTypeMem   type BranchType SeqMem = BranchTypeMem-  type Op         SeqMem = MemOp ()+  type Op SeqMem = MemOp ()  instance ASTLore SeqMem where   expTypesFromPattern = return . map snd . snd . bodyReturnsFromPattern@@ -46,7 +43,7 @@   opReturns (Alloc _ space) = return [MemMem space]   opReturns (Inner ()) = pure [] -instance PrettyLore SeqMem where+instance PrettyLore SeqMem  instance TC.CheckableOp SeqMem where   checkOp (Alloc size _) =
src/Futhark/IR/Syntax.hs view
@@ -1,7 +1,13 @@-{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, StandaloneDeriving #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE Strict #-} {-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TypeFamilies #-}+ -- | = Definition of the Futhark core language IR -- -- For actually /constructing/ ASTs, see "Futhark.Construct".@@ -96,97 +102,112 @@ -- instances for it.  See the source of "Futhark.IR.Seq" -- for what is likely the simplest example. module Futhark.IR.Syntax-  (-    module Language.Futhark.Core-  , module Futhark.IR.Decorations-  , module Futhark.IR.Syntax.Core+  ( module Language.Futhark.Core,+    module Futhark.IR.Decorations,+    module Futhark.IR.Syntax.Core, -  -- * Types-  , Uniqueness(..)-  , NoUniqueness(..)-  , Rank(..)-  , ArrayShape(..)-  , Space (..)-  , TypeBase(..)-  , Diet(..)+    -- * Types+    Uniqueness (..),+    NoUniqueness (..),+    Rank (..),+    ArrayShape (..),+    Space (..),+    TypeBase (..),+    Diet (..), -  -- * Attributes-  , Attr(..)-  , Attrs(..)-  , oneAttr-  , inAttrs-  , withoutAttrs+    -- * Attributes+    Attr (..),+    Attrs (..),+    oneAttr,+    inAttrs,+    withoutAttrs, -  -- * Abstract syntax tree-  , Ident (..)-  , SubExp(..)-  , PatElem-  , PatElemT (..)-  , PatternT (..)-  , Pattern-  , StmAux(..)-  , Stm(..)-  , Stms-  , Result-  , BodyT(..)-  , Body-  , BasicOp (..)-  , UnOp (..)-  , BinOp (..)-  , CmpOp (..)-  , ConvOp (..)-  , DimChange (..)-  , ShapeChange-  , ExpT(..)-  , Exp-  , LoopForm (..)-  , IfDec (..)-  , IfSort (..)-  , Safety (..)-  , LambdaT(..)-  , Lambda+    -- * Abstract syntax tree+    Ident (..),+    SubExp (..),+    PatElem,+    PatElemT (..),+    PatternT (..),+    Pattern,+    StmAux (..),+    Stm (..),+    Stms,+    Result,+    BodyT (..),+    Body,+    BasicOp (..),+    UnOp (..),+    BinOp (..),+    CmpOp (..),+    ConvOp (..),+    DimChange (..),+    ShapeChange,+    ExpT (..),+    Exp,+    LoopForm (..),+    IfDec (..),+    IfSort (..),+    Safety (..),+    LambdaT (..),+    Lambda, -  -- * Definitions-  , Param (..)-  , FParam-  , LParam-  , FunDef (..)-  , EntryPoint-  , EntryPointType(..)-  , Prog(..)+    -- * Definitions+    Param (..),+    FParam,+    LParam,+    FunDef (..),+    EntryPoint,+    EntryPointType (..),+    Prog (..), -  -- * Utils-  , oneStm-  , stmsFromList-  , stmsToList-  , stmsHead+    -- * Utils+    oneStm,+    stmsFromList,+    stmsToList,+    stmsHead,   )-  where+where -import qualified Data.Set as S+import Control.Category import Data.Foldable import qualified Data.Sequence as Seq+import qualified Data.Set as S import Data.String+import qualified Data.Text as T import Data.Traversable (fmapDefault, foldMapDefault)--import Language.Futhark.Core import Futhark.IR.Decorations import Futhark.IR.Syntax.Core+import GHC.Generics (Generic)+import Language.Futhark.Core+import Language.SexpGrammar as Sexp+import Language.SexpGrammar.Generic+import Prelude hiding (id, (.))  -- | A single attribute. data Attr   = AttrAtom Name   | AttrComp Name [Attr]-  deriving (Ord, Show, Eq)+  deriving (Ord, Show, Eq, Generic) +instance SexpIso Attr where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "atom") >>> Sexp.el sexpIso)) $+        With+          (. Sexp.list (Sexp.el (Sexp.sym "comp") >>> Sexp.el sexpIso >>> Sexp.el sexpIso))+          End+ instance IsString Attr where   fromString = AttrAtom . fromString  -- | Every statement is associated with a set of attributes, which can -- have various effects throughout the compiler.-newtype Attrs = Attrs { unAttrs :: S.Set Attr }-  deriving (Ord, Show, Eq, Monoid, Semigroup)+newtype Attrs = Attrs {unAttrs :: S.Set Attr}+  deriving (Ord, Show, Eq, Monoid, Semigroup, Generic) +instance SexpIso Attrs where+  sexpIso = with $ \attrs -> sexpIso >>> attrs+ -- | Construct 'Attrs' from a single 'Attr'. oneAttr :: Attr -> Attrs oneAttr = Attrs . S.singleton@@ -203,16 +224,24 @@ type PatElem lore = PatElemT (LetDec lore)  -- | A pattern is conceptually just a list of names and their types.-data PatternT dec =-  Pattern { patternContextElements :: [PatElemT dec]-            -- ^ existential context (sizes and memory blocks)-          , patternValueElements   :: [PatElemT dec]-            -- ^ "real" values-          }-  deriving (Ord, Show, Eq)+data PatternT dec = Pattern+  { -- | existential context (sizes and memory blocks)+    patternContextElements :: [PatElemT dec],+    -- | "real" values+    patternValueElements :: [PatElemT dec]+  }+  deriving (Ord, Show, Eq, Generic) +instance SexpIso dec => SexpIso (PatternT dec) where+  sexpIso = with $ \vname ->+    Sexp.list+      ( Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> vname+ instance Semigroup (PatternT dec) where-  Pattern cs1 vs1 <> Pattern cs2 vs2 = Pattern (cs1++cs2) (vs1++vs2)+  Pattern cs1 vs1 <> Pattern cs2 vs2 = Pattern (cs1 ++ cs2) (vs1 ++ vs2)  instance Monoid (PatternT dec) where   mempty = Pattern [] []@@ -231,32 +260,60 @@ type Pattern lore = PatternT (LetDec lore)  -- | Auxilliary Information associated with a statement.-data StmAux dec = StmAux { stmAuxCerts :: !Certificates-                         , stmAuxAttrs :: Attrs-                         , stmAuxDec :: dec-                         }-                  deriving (Ord, Show, Eq)+data StmAux dec = StmAux+  { stmAuxCerts :: !Certificates,+    stmAuxAttrs :: Attrs,+    stmAuxDec :: dec+  }+  deriving (Ord, Show, Eq, Generic) +instance SexpIso dec => SexpIso (StmAux dec) where+  sexpIso = with $ \vname ->+    Sexp.list+      ( Sexp.el (Sexp.sym "aux")+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> vname+ instance Semigroup dec => Semigroup (StmAux dec) where   StmAux cs1 attrs1 dec1 <> StmAux cs2 attrs2 dec2 =-    StmAux (cs1<>cs2) (attrs1<>attrs2) (dec1<>dec2)+    StmAux (cs1 <> cs2) (attrs1 <> attrs2) (dec1 <> dec2)  -- | A local variable binding.-data Stm lore = Let { stmPattern :: Pattern lore-                      -- ^ Pattern.-                    , stmAux :: StmAux (ExpDec lore)-                      -- ^ Auxiliary information statement.-                    , stmExp :: Exp lore-                      -- ^ Expression.-                    }+data Stm lore = Let+  { -- | Pattern.+    stmPattern :: Pattern lore,+    -- | Auxiliary information statement.+    stmAux :: StmAux (ExpDec lore),+    -- | Expression.+    stmExp :: Exp lore+  }+  deriving (Generic) +instance Decorations lore => SexpIso (Stm lore) where+  sexpIso = with $ \stm ->+    Sexp.list+      ( Sexp.el (Sexp.sym "let")+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> stm+ deriving instance Decorations lore => Ord (Stm lore)+ deriving instance Decorations lore => Show (Stm lore)+ deriving instance Decorations lore => Eq (Stm lore)  -- | A sequence of statements. type Stms lore = Seq.Seq (Stm lore) +instance Decorations lore => SexpIso (Stms lore) where+  sexpIso = iso stmsFromList stmsToList . sexpIso+ -- | A single statement. oneStm :: Stm lore -> Stms lore oneStm = Seq.singleton@@ -271,21 +328,35 @@  -- | The first statement in the sequence, if any. stmsHead :: Stms lore -> Maybe (Stm lore, Stms lore)-stmsHead stms = case Seq.viewl stms of stm Seq.:< stms' -> Just (stm, stms')-                                       Seq.EmptyL       -> Nothing+stmsHead stms = case Seq.viewl stms of+  stm Seq.:< stms' -> Just (stm, stms')+  Seq.EmptyL -> Nothing  -- | The result of a body is a sequence of subexpressions. type Result = [SubExp]  -- | A body consists of a number of bindings, terminating in a result -- (essentially a tuple literal).-data BodyT lore = Body { bodyDec :: BodyDec lore-                       , bodyStms :: Stms lore-                       , bodyResult :: Result-                       }+data BodyT lore = Body+  { bodyDec :: BodyDec lore,+    bodyStms :: Stms lore,+    bodyResult :: Result+  }+  deriving (Generic) +instance Decorations lore => SexpIso (BodyT lore) where+  sexpIso = with $ \stm ->+    Sexp.list+      ( Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> stm+ deriving instance Decorations lore => Ord (BodyT lore)+ deriving instance Decorations lore => Show (BodyT lore)+ deriving instance Decorations lore => Eq (BodyT lore)  -- | Type alias for namespace reasons.@@ -295,14 +366,23 @@ -- disambiguate "real" reshapes, that change the actual shape of the -- array, from type coercions that are just present to make the types -- work out.  The two constructors are considered equal for purposes of 'Eq'.-data DimChange d = DimCoercion d-                   -- ^ The new dimension is guaranteed to be numerically-                   -- equal to the old one.-                 | DimNew d-                   -- ^ The new dimension is not necessarily numerically-                   -- equal to the old one.-                 deriving (Ord, Show)+data DimChange d+  = -- | The new dimension is guaranteed to be numerically+    -- equal to the old one.+    DimCoercion d+  | -- | The new dimension is not necessarily numerically+    -- equal to the old one.+    DimNew d+  deriving (Ord, Show, Generic) +instance SexpIso d => SexpIso (DimChange d) where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "coercion") >>> Sexp.el sexpIso)) $+        With+          (. Sexp.list (Sexp.el (Sexp.sym "new") >>> Sexp.el sexpIso))+          End+ instance Eq d => Eq (DimChange d) where   DimCoercion x == DimNew y = x == y   DimCoercion x == DimCoercion y = x == y@@ -311,15 +391,15 @@  instance Functor DimChange where   fmap f (DimCoercion d) = DimCoercion $ f d-  fmap f (DimNew      d) = DimNew $ f d+  fmap f (DimNew d) = DimNew $ f d  instance Foldable DimChange where   foldMap f (DimCoercion d) = f d-  foldMap f (DimNew      d) = f d+  foldMap f (DimNew d) = f d  instance Traversable DimChange where   traverse f (DimCoercion d) = DimCoercion <$> f d-  traverse f (DimNew      d) = DimNew <$> f d+  traverse f (DimNew d) = DimNew <$> f d  -- | A list of 'DimChange's, indicating the new dimensions of an array. type ShapeChange d = [DimChange d]@@ -327,148 +407,229 @@ -- | A primitive operation that returns something of known size and -- does not itself contain any bindings. data BasicOp-  = SubExp SubExp-    -- ^ A variable or constant.--  | Opaque SubExp-    -- ^ Semantically and operationally just identity, but is+  = -- | A variable or constant.+    SubExp SubExp+  | -- | Semantically and operationally just identity, but is     -- invisible/impenetrable to optimisations (hopefully).  This is     -- just a hack to avoid optimisation (so, to work around compiler     -- limitations).--  | ArrayLit  [SubExp] Type-    -- ^ Array literals, e.g., @[ [1+x, 3], [2, 1+4] ]@.+    Opaque SubExp+  | -- | Array literals, e.g., @[ [1+x, 3], [2, 1+4] ]@.     -- Second arg is the element type of the rows of the array.     -- Scalar operations--  | UnOp UnOp SubExp-    -- ^ Unary operation.--  | BinOp BinOp SubExp SubExp-    -- ^ Binary operation.--  | CmpOp CmpOp SubExp SubExp-    -- ^ Comparison - result type is always boolean.--  | ConvOp ConvOp SubExp-    -- ^ Conversion "casting".--  | Assert SubExp (ErrorMsg SubExp) (SrcLoc, [SrcLoc])-  -- ^ Turn a boolean into a certificate, halting the program with the-  -- given error message if the boolean is false.--  -- Primitive array operations--  | Index VName (Slice SubExp)-  -- ^ The certificates for bounds-checking are part of the 'Stm'.--  | Update VName (Slice SubExp) SubExp-  -- ^ An in-place update of the given array at the given position.-  -- Consumes the array.--  | Concat Int VName [VName] SubExp-  -- ^ @concat@0([1],[2, 3, 4]) = [1, 2, 3, 4]@.--  | Copy VName-  -- ^ Copy the given array.  The result will not alias anything.--  | Manifest [Int] VName-  -- ^ Manifest an array with dimensions represented in the given-  -- order.  The result will not alias anything.--  -- Array construction.-  | Iota SubExp SubExp SubExp IntType-  -- ^ @iota(n, x, s) = [x,x+s,..,x+(n-1)*s]@.-  ---  -- The t'IntType' indicates the type of the array returned and the-  -- offset/stride arguments, but not the length argument.--  | Replicate Shape SubExp-  -- ^ @replicate([3][2],1) = [[1,1], [1,1], [1,1]]@+    ArrayLit [SubExp] Type+  | -- | Unary operation.+    UnOp UnOp SubExp+  | -- | Binary operation.+    BinOp BinOp SubExp SubExp+  | -- | Comparison - result type is always boolean.+    CmpOp CmpOp SubExp SubExp+  | -- | Conversion "casting".+    ConvOp ConvOp SubExp+  | -- | Turn a boolean into a certificate, halting the program with the+    -- given error message if the boolean is false.+    Assert SubExp (ErrorMsg SubExp) (SrcLoc, [SrcLoc])+  | -- Primitive array operations -  | Scratch PrimType [SubExp]-  -- ^ Create array of given type and shape, with undefined elements.+    -- | The certificates for bounds-checking are part of the 'Stm'.+    Index VName (Slice SubExp)+  | -- | An in-place update of the given array at the given position.+    -- Consumes the array.+    Update VName (Slice SubExp) SubExp+  | -- | @concat@0([1],[2, 3, 4]) = [1, 2, 3, 4]@.+    Concat Int VName [VName] SubExp+  | -- | Copy the given array.  The result will not alias anything.+    Copy VName+  | -- | Manifest an array with dimensions represented in the given+    -- order.  The result will not alias anything.+    Manifest [Int] VName+  | -- Array construction. -  -- Array index space transformation.-  | Reshape (ShapeChange SubExp) VName-   -- ^ 1st arg is the new shape, 2nd arg is the input array *)+    -- | @iota(n, x, s) = [x,x+s,..,x+(n-1)*s]@.+    --+    -- The t'IntType' indicates the type of the array returned and the+    -- offset/stride arguments, but not the length argument.+    Iota SubExp SubExp SubExp IntType+  | -- | @replicate([3][2],1) = [[1,1], [1,1], [1,1]]@+    Replicate Shape SubExp+  | -- | Create array of given type and shape, with undefined elements.+    Scratch PrimType [SubExp]+  | -- Array index space transformation. -  | Rearrange [Int] VName-  -- ^ Permute the dimensions of the input array.  The list-  -- of integers is a list of dimensions (0-indexed), which-  -- must be a permutation of @[0,n-1]@, where @n@ is the-  -- number of dimensions in the input array.+    -- | 1st arg is the new shape, 2nd arg is the input array *)+    Reshape (ShapeChange SubExp) VName+  | -- | Permute the dimensions of the input array.  The list+    -- of integers is a list of dimensions (0-indexed), which+    -- must be a permutation of @[0,n-1]@, where @n@ is the+    -- number of dimensions in the input array.+    Rearrange [Int] VName+  | -- | Rotate the dimensions of the input array.  The list of+    -- subexpressions specify how much each dimension is rotated.  The+    -- length of this list must be equal to the rank of the array.+    Rotate [SubExp] VName+  deriving (Eq, Ord, Show, Generic) -  | Rotate [SubExp] VName-  -- ^ Rotate the dimensions of the input array.  The list of-  -- subexpressions specify how much each dimension is rotated.  The-  -- length of this list must be equal to the rank of the array.-  deriving (Eq, Ord, Show)+instance SexpIso BasicOp where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el sexpIso)) $+        With (. Sexp.list (Sexp.el (Sexp.sym "opaque") >>> Sexp.el sexpIso)) $+          With (. Sexp.list (Sexp.el (Sexp.sym "array") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+            With (. Sexp.list (Sexp.el sexpIso >>> Sexp.el sexpIso)) $+              With (. Sexp.list (Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                With (. Sexp.list (Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                  With (. Sexp.list (Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                    With (. Sexp.list (Sexp.el (Sexp.sym "assert") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el assertHelper)) $+                      With (. Sexp.list (Sexp.el (Sexp.sym "index") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                        With (. Sexp.list (Sexp.el (Sexp.sym "update") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                          With (. Sexp.list (Sexp.el (Sexp.sym "concat") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                            With (. Sexp.list (Sexp.el (Sexp.sym "copy") >>> Sexp.el sexpIso)) $+                              With (. Sexp.list (Sexp.el (Sexp.sym "manifest") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                                With (. Sexp.list (Sexp.el (Sexp.sym "iota") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                                  With (. Sexp.list (Sexp.el (Sexp.sym "replicate") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                                    With (. Sexp.list (Sexp.el (Sexp.sym "scratch") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                                      With (. Sexp.list (Sexp.el (Sexp.sym "reshape") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                                        With (. Sexp.list (Sexp.el (Sexp.sym "rearrange") >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+                                          With+                                            (. Sexp.list (Sexp.el (Sexp.sym "rotate") >>> Sexp.el sexpIso >>> Sexp.el sexpIso))+                                            End+    where+      assertHelper =+        with $ \tuple ->+          Sexp.list+            ( Sexp.el (iso (const mempty) (T.pack . show) . Sexp.symbol)+                >>> Sexp.rest (iso (const mempty) (T.pack . show) . Sexp.symbol)+            )+            >>> tuple  -- | The root Futhark expression type.  The v'Op' constructor contains -- a lore-specific operation.  Do-loops, branches and function calls -- are special.  Everything else is a simple t'BasicOp'. data ExpT lore-  = BasicOp BasicOp-    -- ^ A simple (non-recursive) operation.--  | Apply  Name [(SubExp, Diet)] [RetType lore] (Safety, SrcLoc, [SrcLoc])--  | If     SubExp (BodyT lore) (BodyT lore) (IfDec (BranchType lore))--  | DoLoop [(FParam lore, SubExp)] [(FParam lore, SubExp)] (LoopForm lore) (BodyT lore)-    -- ^ @loop {a} = {v} (for i < n|while b) do b@.  The merge+  = -- | A simple (non-recursive) operation.+    BasicOp BasicOp+  | Apply Name [(SubExp, Diet)] [RetType lore] (Safety, SrcLoc, [SrcLoc])+  | If SubExp (BodyT lore) (BodyT lore) (IfDec (BranchType lore))+  | -- | @loop {a} = {v} (for i < n|while b) do b@.  The merge     -- parameters are divided into context and value part.-+    DoLoop [(FParam lore, SubExp)] [(FParam lore, SubExp)] (LoopForm lore) (BodyT lore)   | Op (Op lore)+  deriving (Generic) +instance Decorations lore => SexpIso (ExpT lore) where+  sexpIso =+    match $+      With (. sexpIso) $+        With (. Sexp.list (Sexp.el (Sexp.sym "apply") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el applyHelper)) $+          With (. Sexp.list (Sexp.el (Sexp.sym "if") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+            With (. Sexp.list (Sexp.el (Sexp.sym "loop") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+              With+                (. sexpIso)+                End+    where+      applyHelper =+        with $ \triple ->+          Sexp.list+            ( Sexp.el sexpIso+                >>> Sexp.el (iso (const mempty) (T.pack . show) . Sexp.symbol)+                >>> Sexp.rest (iso (const mempty) (T.pack . show) . Sexp.symbol)+            )+            >>> triple+ deriving instance Decorations lore => Eq (ExpT lore)+ deriving instance Decorations lore => Show (ExpT lore)+ deriving instance Decorations lore => Ord (ExpT lore)  -- | For-loop or while-loop?-data LoopForm lore = ForLoop VName IntType SubExp [(LParam lore,VName)]-                   | WhileLoop VName+data LoopForm lore+  = ForLoop VName IntType SubExp [(LParam lore, VName)]+  | WhileLoop VName+  deriving (Generic) +instance Decorations lore => SexpIso (LoopForm lore) where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "for") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+        With+          (. Sexp.list (Sexp.el (Sexp.sym "while") >>> Sexp.el sexpIso))+          End+ deriving instance Decorations lore => Eq (LoopForm lore)+ deriving instance Decorations lore => Show (LoopForm lore)+ deriving instance Decorations lore => Ord (LoopForm lore)  -- | Data associated with a branch.-data IfDec rt = IfDec { ifReturns :: [rt]-                      , ifSort :: IfSort-                      }-                 deriving (Eq, Show, Ord)+data IfDec rt = IfDec+  { ifReturns :: [rt],+    ifSort :: IfSort+  }+  deriving (Eq, Show, Ord, Generic) +instance SexpIso rt => SexpIso (IfDec rt) where+  sexpIso = with $ \stm ->+    Sexp.list+      ( Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> stm+ -- | What kind of branch is this?  This has no semantic meaning, but -- provides hints to simplifications.-data IfSort = IfNormal-              -- ^ An ordinary branch.-            | IfFallback-              -- ^ A branch where the "true" case is what we are-              -- actually interested in, and the "false" case is only-              -- present as a fallback for when the true case cannot-              -- be safely evaluated.  The compiler is permitted to-              -- optimise away the branch if the true case contains-              -- only safe statements.-            | IfEquiv-              -- ^ Both of these branches are semantically equivalent,-              -- and it is fine to eliminate one if it turns out to-              -- have problems (e.g. contain things we cannot generate-              -- code for).-            deriving (Eq, Show, Ord)+data IfSort+  = -- | An ordinary branch.+    IfNormal+  | -- | A branch where the "true" case is what we are+    -- actually interested in, and the "false" case is only+    -- present as a fallback for when the true case cannot+    -- be safely evaluated.  The compiler is permitted to+    -- optimise away the branch if the true case contains+    -- only safe statements.+    IfFallback+  | -- | Both of these branches are semantically equivalent,+    -- and it is fine to eliminate one if it turns out to+    -- have problems (e.g. contain things we cannot generate+    -- code for).+    IfEquiv+  deriving (Eq, Show, Ord, Generic) +instance SexpIso IfSort where+  sexpIso =+    match $+      With (sym "normal" >>>) $+        With (sym "fallback" >>>) $+          With+            (sym "equiv" >>>)+            End+ -- | A type alias for namespace control. type Exp = ExpT  -- | Anonymous function for use in a SOAC.-data LambdaT lore = Lambda { lambdaParams     :: [LParam lore]-                           , lambdaBody       :: BodyT lore-                           , lambdaReturnType :: [Type]-                           }+data LambdaT lore = Lambda+  { lambdaParams :: [LParam lore],+    lambdaBody :: BodyT lore,+    lambdaReturnType :: [Type]+  }+  deriving (Generic) +instance Decorations lore => SexpIso (LambdaT lore) where+  sexpIso = with $ \lambdat ->+    Sexp.list+      ( Sexp.el (Sexp.sym "lambda")+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> lambdat+ deriving instance Decorations lore => Eq (LambdaT lore)+ deriving instance Decorations lore => Show (LambdaT lore)+ deriving instance Decorations lore => Ord (LambdaT lore)  -- | Type alias for namespacing reasons.@@ -481,18 +642,35 @@ type LParam lore = Param (LParamInfo lore)  -- | Function Declarations-data FunDef lore = FunDef { funDefEntryPoint :: Maybe EntryPoint-                            -- ^ Contains a value if this function is-                            -- an entry point.-                          , funDefAttrs :: Attrs-                          , funDefName :: Name-                          , funDefRetType :: [RetType lore]-                          , funDefParams :: [FParam lore]-                          , funDefBody :: BodyT lore-                          }+data FunDef lore = FunDef+  { -- | Contains a value if this function is+    -- an entry point.+    funDefEntryPoint :: Maybe EntryPoint,+    funDefAttrs :: Attrs,+    funDefName :: Name,+    funDefRetType :: [RetType lore],+    funDefParams :: [FParam lore],+    funDefBody :: BodyT lore+  }+  deriving (Generic) +instance Decorations lore => SexpIso (FunDef lore) where+  sexpIso = with $ \fundef ->+    Sexp.list+      ( Sexp.el (Sexp.sym "fundef")+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> fundef+ deriving instance Decorations lore => Eq (FunDef lore)+ deriving instance Decorations lore => Show (FunDef lore)+ deriving instance Decorations lore => Ord (FunDef lore)  -- | Information about the parameters and return value of an entry@@ -502,26 +680,45 @@  -- | Every entry point argument and return value has an annotation -- indicating how it maps to the original source program type.-data EntryPointType = TypeUnsigned-                      -- ^ Is an unsigned integer or array of unsigned-                      -- integers.-                    | TypeOpaque String Int-                      -- ^ A black box type comprising this many core-                      -- values.  The string is a human-readable-                      -- description with no other semantics.-                    | TypeDirect-                      -- ^ Maps directly.-                    deriving (Eq, Show, Ord)+data EntryPointType+  = -- | Is an unsigned integer or array of unsigned+    -- integers.+    TypeUnsigned+  | -- | A black box type comprising this many core+    -- values.  The string is a human-readable+    -- description with no other semantics.+    TypeOpaque String Int+  | -- | Maps directly.+    TypeDirect+  deriving (Eq, Show, Ord, Generic) +instance SexpIso EntryPointType where+  sexpIso =+    match $+      With (. Sexp.sym "unsigned") $+        With (. Sexp.list (Sexp.el (Sexp.sym "opaque") >>> Sexp.el (iso T.unpack T.pack . sexpIso) >>> Sexp.el sexpIso)) $+          With+            (. Sexp.sym "direct")+            End+ -- | An entire Futhark program. data Prog lore = Prog-  { progConsts :: Stms lore-    -- ^ Top-level constants that are computed at program startup, and+  { -- | Top-level constants that are computed at program startup, and     -- which are in scope inside all functions.--  , progFuns :: [FunDef lore]-    -- ^ The functions comprising the program.  All funtions are also+    progConsts :: Stms lore,+    -- | The functions comprising the program.  All funtions are also     -- available in scope in the definitions of the constants, so be     -- careful not to introduce circular dependencies (not currently     -- checked).-  } deriving (Eq, Ord, Show)+    progFuns :: [FunDef lore]+  }+  deriving (Eq, Ord, Show, Generic)++instance Decorations lore => SexpIso (Prog lore) where+  sexpIso = with $ \prog ->+    Sexp.list+      ( Sexp.el (Sexp.sym "prog")+          >>> Sexp.el sexpIso+          >>> Sexp.rest sexpIso+      )+      >>> prog
src/Futhark/IR/Syntax/Core.hs view
@@ -1,72 +1,83 @@+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Safe #-} {-# LANGUAGE Strict #-}+ -- | The most primitive ("core") aspects of the AST.  Split out of -- "Futhark.IR.Syntax" in order for -- "Futhark.IR.Decorations" to use these definitions.  This -- module is re-exported from "Futhark.IR.Syntax" and -- there should be no reason to include it explicitly. module Futhark.IR.Syntax.Core-       (-           module Language.Futhark.Core-         , module Futhark.IR.Primitive+  ( module Language.Futhark.Core,+    module Futhark.IR.Primitive, -         -- * Types-         , Uniqueness(..)-         , NoUniqueness(..)-         , ShapeBase(..)-         , Shape-         , Ext(..)-         , ExtSize-         , ExtShape-         , Rank(..)-         , ArrayShape(..)-         , Space (..)-         , SpaceId-         , TypeBase(..)-         , Type-         , ExtType-         , DeclType-         , DeclExtType-         , Diet(..)-         , ErrorMsg (..)-         , ErrorMsgPart (..)-         , errorMsgArgTypes+    -- * Types+    Uniqueness (..),+    NoUniqueness (..),+    ShapeBase (..),+    Shape,+    Ext (..),+    ExtSize,+    ExtShape,+    Rank (..),+    ArrayShape (..),+    Space (..),+    SpaceId,+    TypeBase (..),+    Type,+    ExtType,+    DeclType,+    DeclExtType,+    Diet (..),+    ErrorMsg (..),+    ErrorMsgPart (..),+    errorMsgArgTypes, -         -- * Values-         , PrimValue(..)+    -- * Values+    PrimValue (..), -         -- * Abstract syntax tree-         , Ident (..)-         , Certificates(..)-         , SubExp(..)-         , Param (..)-         , DimIndex (..)-         , Slice-         , dimFix-         , sliceIndices-         , sliceDims-         , unitSlice-         , fixSlice-         , sliceSlice-         , PatElemT (..)-         ) where+    -- * Abstract syntax tree+    Ident (..),+    Certificates (..),+    SubExp (..),+    Param (..),+    DimIndex (..),+    Slice,+    dimFix,+    sliceIndices,+    sliceDims,+    unitSlice,+    fixSlice,+    sliceSlice,+    PatElemT (..),+  )+where +import Control.Category import Control.Monad.State+import qualified Data.Map.Strict as M import Data.Maybe import Data.String-import qualified Data.Map.Strict as M+import qualified Data.Text as T import Data.Traversable (fmapDefault, foldMapDefault)--import Language.Futhark.Core import Futhark.IR.Primitive+import GHC.Generics+import Language.Futhark.Core+import Language.SexpGrammar as Sexp+import Language.SexpGrammar.Generic+import Prelude hiding (id, (.))  -- | The size of an array type as a list of its dimension sizes, with -- the type of sizes being parametric.-newtype ShapeBase d = Shape { shapeDims :: [d] }-                    deriving (Eq, Ord, Show)+newtype ShapeBase d = Shape {shapeDims :: [d]}+  deriving (Eq, Ord, Show, Generic) +instance SexpIso d => SexpIso (ShapeBase d) where+  sexpIso = with $ \vname -> sexpIso >>> vname+ instance Functor ShapeBase where   fmap = fmapDefault @@ -87,10 +98,19 @@ type Shape = ShapeBase SubExp  -- | Something that may be existential.-data Ext a = Ext Int-           | Free a-           deriving (Eq, Ord, Show)+data Ext a+  = Ext Int+  | Free a+  deriving (Eq, Ord, Show, Generic) +instance SexpIso a => SexpIso (Ext a) where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "ext") >>> Sexp.el sexpIso)) $+        With+          (. Sexp.list (Sexp.el (Sexp.sym "free") >>> Sexp.el sexpIso))+          End+ instance Functor Ext where   fmap = fmapDefault @@ -111,15 +131,25 @@ -- | The size of an array type as merely the number of dimensions, -- with no further information. newtype Rank = Rank Int-             deriving (Show, Eq, Ord)+  deriving (Show, Eq, Ord, Generic) +instance SexpIso Rank where+  sexpIso = with $ \rank ->+    Sexp.list+      ( Sexp.el (Sexp.sym "rank")+          >>> Sexp.el sexpIso+      )+      >>> rank+ -- | A class encompassing types containing array shape information. class (Monoid a, Eq a, Ord a) => ArrayShape a where   -- | Return the rank of an array with the given size.   shapeRank :: a -> Int+   -- | @stripDims n shape@ strips the outer @n@ dimensions from   -- @shape@.   stripDims :: Int -> a -> a+   -- | Check whether one shape if a subset of another shape.   subShapeOf :: a -> a -> Bool @@ -134,18 +164,21 @@   subShapeOf (Shape ds1) (Shape ds2) =     -- Must agree on Free dimensions, and ds1 may not be existential     -- where ds2 is Free.  Existentials must also be congruent.-    length ds1 == length ds2 &&-    evalState (and <$> zipWithM subDimOf ds1 ds2) M.empty-    where subDimOf (Free se1) (Free se2) = return $ se1 == se2-          subDimOf (Ext _)    (Free _)   = return False-          subDimOf (Free _)   (Ext _)    = return True-          subDimOf (Ext x)    (Ext y)    = do-            extmap <- get-            case M.lookup y extmap of-              Just ywas | ywas == x -> return True-                        | otherwise -> return False-              Nothing -> do put $ M.insert y x extmap-                            return True+    length ds1 == length ds2+      && evalState (and <$> zipWithM subDimOf ds1 ds2) M.empty+    where+      subDimOf (Free se1) (Free se2) = return $ se1 == se2+      subDimOf (Ext _) (Free _) = return False+      subDimOf (Free _) (Ext _) = return True+      subDimOf (Ext x) (Ext y) = do+        extmap <- get+        case M.lookup y extmap of+          Just ywas+            | ywas == x -> return True+            | otherwise -> return False+          Nothing -> do+            put $ M.insert y x extmap+            return True  instance Semigroup Rank where   Rank x <> Rank y = Rank $ x + y@@ -164,28 +197,51 @@ -- used to distinguish between constant, global and shared memory -- spaces.  In GPU-enabled host code, it is used to distinguish -- between host memory ('DefaultSpace') and GPU space.-data Space = DefaultSpace-           | Space SpaceId-           | ScalarSpace [SubExp] PrimType-             -- ^ A special kind of memory that is a statically sized-             -- array of some primitive type.  Used for private memory-             -- on GPUs.-             deriving (Show, Eq, Ord)+data Space+  = DefaultSpace+  | Space SpaceId+  | -- | A special kind of memory that is a statically sized+    -- array of some primitive type.  Used for private memory+    -- on GPUs.+    ScalarSpace [SubExp] PrimType+  deriving (Show, Eq, Ord, Generic) +instance SexpIso Space where+  sexpIso =+    match $+      With (Sexp.sym "default" >>>) $+        With (. Sexp.list (Sexp.el (sym "space") >>> Sexp.el (iso T.unpack T.pack . sexpIso))) $+          With+            (. Sexp.list (Sexp.el (sym "scalar-space") >>> Sexp.el sexpIso >>> Sexp.el sexpIso))+            End+ -- | A string representing a specific non-default memory space. type SpaceId = String  -- | A fancier name for @()@ - encodes no uniqueness information. data NoUniqueness = NoUniqueness-                  deriving (Eq, Ord, Show)+  deriving (Eq, Ord, Show, Generic) +instance SexpIso NoUniqueness where+  sexpIso = with (. sym "no-uniqueness")+ -- | A Futhark type is either an array or an element type.  When -- comparing types for equality with '==', shapes must match.-data TypeBase shape u = Prim PrimType-                      | Array PrimType shape u-                      | Mem Space-                    deriving (Show, Eq, Ord)+data TypeBase shape u+  = Prim PrimType+  | Array PrimType shape u+  | Mem Space+  deriving (Show, Eq, Ord, Generic) +instance (SexpIso shape, SexpIso u) => SexpIso (TypeBase shape u) where+  sexpIso =+    match $+      With (. sexpIso) $+        With (. Sexp.list (Sexp.el (sym "array") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso)) $+          With+            (. Sexp.list (Sexp.el (sym "mem") >>> Sexp.el sexpIso))+            End+ -- | A type with shape information, used for describing the type of -- variables. type Type = TypeBase Shape NoUniqueness@@ -207,21 +263,44 @@ -- example, we might say that a function taking three arguments of -- types @([int], *[int], [int])@ has diet @[Observe, Consume, -- Observe]@.-data Diet = Consume -- ^ Consumes this value.-          | Observe -- ^ Only observes value in this position, does-                    -- not consume.  A result may alias this.-          | ObservePrim -- ^ As 'Observe', but the result will not-                        -- alias, because the parameter does not carry-                        -- aliases.-            deriving (Eq, Ord, Show)+data Diet+  = -- | Consumes this value.+    Consume+  | -- | Only observes value in this position, does+    -- not consume.  A result may alias this.+    Observe+  | -- | As 'Observe', but the result will not+    -- alias, because the parameter does not carry+    -- aliases.+    ObservePrim+  deriving (Eq, Ord, Show, Generic) +instance SexpIso Diet where+  sexpIso =+    match $+      With (Sexp.sym "consume" >>>) $+        With (Sexp.sym "observe" >>>) $+          With+            (Sexp.sym "observe-prim" >>>)+            End+ -- | An identifier consists of its name and the type of the value -- bound to the identifier.-data Ident = Ident { identName :: VName-                   , identType :: Type-                   }-               deriving (Show)+data Ident = Ident+  { identName :: VName,+    identType :: Type+  }+  deriving (Show, Generic) +instance SexpIso Ident where+  sexpIso = with $ \vname ->+    Sexp.list+      ( Sexp.el (Sexp.sym "ident")+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> vname+ instance Eq Ident where   x == y = identName x == identName y @@ -229,9 +308,12 @@   x `compare` y = identName x `compare` identName y  -- | A list of names used for certificates in some expressions.-newtype Certificates = Certificates { unCertificates :: [VName] }-                     deriving (Eq, Ord, Show)+newtype Certificates = Certificates {unCertificates :: [VName]}+  deriving (Eq, Ord, Show, Generic) +instance SexpIso Certificates where+  sexpIso = with $ \certificates -> sexpIso >>> certificates+ instance Semigroup Certificates where   Certificates x <> Certificates y = Certificates (x <> y) @@ -241,18 +323,37 @@ -- | A subexpression is either a scalar constant or a variable.  One -- important property is that evaluation of a subexpression is -- guaranteed to complete in constant time.-data SubExp = Constant PrimValue-            | Var      VName-            deriving (Show, Eq, Ord)+data SubExp+  = Constant PrimValue+  | Var VName+  deriving (Show, Eq, Ord, Generic) +instance SexpIso SubExp where+  sexpIso =+    match $+      With (. sexpIso) $+        With+          (. sexpIso)+          End+ -- | A function or lambda parameter. data Param dec = Param-                 { paramName :: VName-                   -- ^ Name of the parameter.-                 , paramDec :: dec-                   -- ^ Function parameter decoration.-                 } deriving (Ord, Show, Eq)+  { -- | Name of the parameter.+    paramName :: VName,+    -- | Function parameter decoration.+    paramDec :: dec+  }+  deriving (Ord, Show, Eq, Generic) +instance SexpIso dec => SexpIso (Param dec) where+  sexpIso = with $ \vname ->+    Sexp.list+      ( Sexp.el (Sexp.sym "param")+          >>> Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> vname+ instance Foldable Param where   foldMap = foldMapDefault @@ -263,12 +364,22 @@   traverse f (Param name dec) = Param name <$> f dec  -- | How to index a single dimension of an array.-data DimIndex d = DimFix-                  d -- ^ Fix index in this dimension.-                | DimSlice d d d-                  -- ^ @DimSlice start_offset num_elems stride@.-                  deriving (Eq, Ord, Show)+data DimIndex d+  = -- | Fix index in this dimension.+    DimFix+      d+  | -- | @DimSlice start_offset num_elems stride@.+    DimSlice d d d+  deriving (Eq, Ord, Show, Generic) +instance SexpIso d => SexpIso (DimIndex d) where+  sexpIso =+    match $+      With (. sexpIso) $+        With+          (. Sexp.list (Sexp.el (Sexp.sym "slice") >>> Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso))+          End+ instance Functor DimIndex where   fmap f (DimFix i) = DimFix $ f i   fmap f (DimSlice i j s) = DimSlice (f i) (f j) (f s)@@ -299,8 +410,9 @@ -- | The dimensions of the array produced by this slice. sliceDims :: Slice d -> [d] sliceDims = mapMaybe dimSlice-  where dimSlice (DimSlice _ d _) = Just d-        dimSlice DimFix{}         = Nothing+  where+    dimSlice (DimSlice _ d _) = Just d+    dimSlice DimFix {} = Nothing  -- | A slice with a stride of one. unitSlice :: Num d => d -> d -> DimIndex d@@ -309,33 +421,42 @@ -- | Fix the 'DimSlice's of a slice.  The number of indexes must equal -- the length of 'sliceDims' for the slice. fixSlice :: Num d => Slice d -> [d] -> [d]-fixSlice (DimFix j:mis') is' =+fixSlice (DimFix j : mis') is' =   j : fixSlice mis' is'-fixSlice (DimSlice orig_k _ orig_s:mis') (i:is') =-  (orig_k+i*orig_s) : fixSlice mis' is'+fixSlice (DimSlice orig_k _ orig_s : mis') (i : is') =+  (orig_k + i * orig_s) : fixSlice mis' is' fixSlice _ _ = []  -- | Further slice the 'DimSlice's of a slice.  The number of slices -- must equal the length of 'sliceDims' for the slice. sliceSlice :: Num d => Slice d -> Slice d -> Slice d-sliceSlice (DimFix j:js') is' =+sliceSlice (DimFix j : js') is' =   DimFix j : sliceSlice js' is'-sliceSlice (DimSlice j _ s:js') (DimFix i:is') =-  DimFix (j + (i*s)) : sliceSlice js' is'-sliceSlice (DimSlice j _ s0:js') (DimSlice i n s1:is') =-  DimSlice (j+(s0*i)) n (s0*s1) : sliceSlice js' is'+sliceSlice (DimSlice j _ s : js') (DimFix i : is') =+  DimFix (j + (i * s)) : sliceSlice js' is'+sliceSlice (DimSlice j _ s0 : js') (DimSlice i n s1 : is') =+  DimSlice (j + (s0 * i)) n (s0 * s1) : sliceSlice js' is' sliceSlice _ _ = []  -- | An element of a pattern - consisting of a name and an addditional -- parametric decoration.  This decoration is what is expected to -- contain the type of the resulting variable.-data PatElemT dec = PatElem { patElemName :: VName-                               -- ^ The name being bound.-                             , patElemDec :: dec-                               -- ^ Pattern element decoration.-                             }-                   deriving (Ord, Show, Eq)+data PatElemT dec = PatElem+  { -- | The name being bound.+    patElemName :: VName,+    -- | Pattern element decoration.+    patElemDec :: dec+  }+  deriving (Ord, Show, Eq, Generic) +instance SexpIso dec => SexpIso (PatElemT dec) where+  sexpIso = with $ \pe ->+    Sexp.list+      ( Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> pe+ instance Functor PatElemT where   fmap = fmapDefault @@ -349,16 +470,33 @@ -- | An error message is a list of error parts, which are concatenated -- to form the final message. newtype ErrorMsg a = ErrorMsg [ErrorMsgPart a]-  deriving (Eq, Ord, Show)+  deriving (Eq, Ord, Show, Generic) +instance SexpIso a => SexpIso (ErrorMsg a) where+  sexpIso = with $ \errormsg -> sexpIso >>> errormsg+ instance IsString (ErrorMsg a) where   fromString = ErrorMsg . pure . fromString  -- | A part of an error message.-data ErrorMsgPart a = ErrorString String -- ^ A literal string.-                    | ErrorInt32 a -- ^ A run-time integer value.-                    deriving (Eq, Ord, Show)+data ErrorMsgPart a+  = -- | A literal string.+    ErrorString String+  | -- | A run-time integer value.+    ErrorInt32 a+  | -- | A bigger run-time integer value.+    ErrorInt64 a+  deriving (Eq, Ord, Show, Generic) +instance SexpIso a => SexpIso (ErrorMsgPart a) where+  sexpIso =+    match $+      With (. Sexp.list (Sexp.el (Sexp.sym "error-string") . Sexp.el (iso T.unpack T.pack . sexpIso))) $+        With (. Sexp.list (Sexp.el (Sexp.sym "error-int32") . Sexp.el sexpIso)) $+          With+            (. Sexp.list (Sexp.el (Sexp.sym "error-int64") . Sexp.el sexpIso))+            End+ instance IsString (ErrorMsgPart a) where   fromString = ErrorString @@ -374,18 +512,23 @@ instance Functor ErrorMsgPart where   fmap _ (ErrorString s) = ErrorString s   fmap f (ErrorInt32 a) = ErrorInt32 $ f a+  fmap f (ErrorInt64 a) = ErrorInt64 $ f a  instance Foldable ErrorMsgPart where-  foldMap _ ErrorString{} = mempty+  foldMap _ ErrorString {} = mempty   foldMap f (ErrorInt32 a) = f a+  foldMap f (ErrorInt64 a) = f a  instance Traversable ErrorMsgPart where   traverse _ (ErrorString s) = pure $ ErrorString s   traverse f (ErrorInt32 a) = ErrorInt32 <$> f a+  traverse f (ErrorInt64 a) = ErrorInt64 <$> f a  -- | How many non-constant parts does the error message have, and what -- is their type? errorMsgArgTypes :: ErrorMsg a -> [PrimType] errorMsgArgTypes (ErrorMsg parts) = mapMaybe onPart parts-  where onPart ErrorString{} = Nothing-        onPart ErrorInt32{} = Just $ IntType Int32+  where+    onPart ErrorString {} = Nothing+    onPart ErrorInt32 {} = Just $ IntType Int32+    onPart ErrorInt64 {} = Just $ IntType Int64
src/Futhark/IR/Traversals.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE Safe #-}+ -- | -- -- Functions for generic traversals across Futhark syntax trees.  The@@ -23,68 +24,72 @@ -- The "Futhark.Transform.Rename" module is a simple example of how to -- use this facility. module Futhark.IR.Traversals-  (-  -- * Mapping-    Mapper(..)-  , identityMapper-  , mapExpM-  , mapExp+  ( -- * Mapping+    Mapper (..),+    identityMapper,+    mapExpM,+    mapExp, -  -- * Walking-  , Walker(..)-  , identityWalker-  , walkExpM+    -- * Walking+    Walker (..),+    identityWalker,+    walkExpM,   )-  where+where  import Control.Monad import Control.Monad.Identity-import qualified Data.Traversable import Data.Foldable (traverse_)--import Futhark.IR.Syntax+import qualified Data.Traversable import Futhark.IR.Prop.Scope import Futhark.IR.Prop.Types (mapOnType)+import Futhark.IR.Syntax  -- | Express a monad mapping operation on a syntax node.  Each element -- of this structure expresses the operation to be performed on a -- given child.-data Mapper flore tlore m = Mapper {-    mapOnSubExp :: SubExp -> m SubExp-  , mapOnBody :: Scope tlore -> Body flore -> m (Body tlore)-    -- ^ Most bodies are enclosed in a scope, which is passed along+data Mapper flore tlore m = Mapper+  { mapOnSubExp :: SubExp -> m SubExp,+    -- | Most bodies are enclosed in a scope, which is passed along     -- for convenience.-  , mapOnVName :: VName -> m VName-  , mapOnRetType :: RetType flore -> m (RetType tlore)-  , mapOnBranchType :: BranchType flore -> m (BranchType tlore)-  , mapOnFParam :: FParam flore -> m (FParam tlore)-  , mapOnLParam :: LParam flore -> m (LParam tlore)-  , mapOnOp :: Op flore -> m (Op tlore)+    mapOnBody :: Scope tlore -> Body flore -> m (Body tlore),+    mapOnVName :: VName -> m VName,+    mapOnRetType :: RetType flore -> m (RetType tlore),+    mapOnBranchType :: BranchType flore -> m (BranchType tlore),+    mapOnFParam :: FParam flore -> m (FParam tlore),+    mapOnLParam :: LParam flore -> m (LParam tlore),+    mapOnOp :: Op flore -> m (Op tlore)   }  -- | A mapper that simply returns the tree verbatim. identityMapper :: Monad m => Mapper lore lore m-identityMapper = Mapper {-                   mapOnSubExp = return-                 , mapOnBody = const return-                 , mapOnVName = return-                 , mapOnRetType = return-                 , mapOnBranchType = return-                 , mapOnFParam = return-                 , mapOnLParam = return-                 , mapOnOp = return-                 }+identityMapper =+  Mapper+    { mapOnSubExp = return,+      mapOnBody = const return,+      mapOnVName = return,+      mapOnRetType = return,+      mapOnBranchType = return,+      mapOnFParam = return,+      mapOnLParam = return,+      mapOnOp = return+    }  -- | Map a monadic action across the immediate children of an -- expression.  Importantly, the mapping does not descend recursively -- into subexpressions.  The mapping is done left-to-right.-mapExpM :: (Applicative m, Monad m) =>-           Mapper flore tlore m -> Exp flore -> m (Exp tlore)+mapExpM ::+  (Applicative m, Monad m) =>+  Mapper flore tlore m ->+  Exp flore ->+  m (Exp tlore) mapExpM tv (BasicOp (SubExp se)) =   BasicOp <$> (SubExp <$> mapOnSubExp tv se) mapExpM tv (BasicOp (ArrayLit els rowt)) =-  BasicOp <$> (ArrayLit <$> mapM (mapOnSubExp tv) els <*>-              mapOnType (mapOnSubExp tv) rowt)+  BasicOp+    <$> ( ArrayLit <$> mapM (mapOnSubExp tv) els+            <*> mapOnType (mapOnSubExp tv) rowt+        ) mapExpM tv (BasicOp (BinOp bop x y)) =   BasicOp <$> (BinOp bop <$> mapOnSubExp tv x <*> mapOnSubExp tv y) mapExpM tv (BasicOp (CmpOp op x y)) =@@ -94,18 +99,23 @@ mapExpM tv (BasicOp (UnOp op x)) =   BasicOp <$> (UnOp op <$> mapOnSubExp tv x) mapExpM tv (If c texp fexp (IfDec ts s)) =-  If <$> mapOnSubExp tv c <*> mapOnBody tv mempty texp <*> mapOnBody tv mempty fexp <*>-        (IfDec <$> mapM (mapOnBranchType tv) ts <*> pure s)+  If <$> mapOnSubExp tv c <*> mapOnBody tv mempty texp <*> mapOnBody tv mempty fexp+    <*> (IfDec <$> mapM (mapOnBranchType tv) ts <*> pure s) mapExpM tv (Apply fname args ret loc) = do   args' <- forM args $ \(arg, d) ->-             (,) <$> mapOnSubExp tv arg <*> pure d+    (,) <$> mapOnSubExp tv arg <*> pure d   Apply fname args' <$> mapM (mapOnRetType tv) ret <*> pure loc mapExpM tv (BasicOp (Index arr slice)) =-  BasicOp <$> (Index <$> mapOnVName tv arr <*>-               mapM (traverse (mapOnSubExp tv)) slice)+  BasicOp+    <$> ( Index <$> mapOnVName tv arr+            <*> mapM (traverse (mapOnSubExp tv)) slice+        ) mapExpM tv (BasicOp (Update arr slice se)) =-  BasicOp <$> (Update <$> mapOnVName tv arr <*>-               mapM (traverse (mapOnSubExp tv)) slice <*> mapOnSubExp tv se)+  BasicOp+    <$> ( Update <$> mapOnVName tv arr+            <*> mapM (traverse (mapOnSubExp tv)) slice+            <*> mapOnSubExp tv se+        ) mapExpM tv (BasicOp (Iota n x s et)) =   BasicOp <$> (Iota <$> mapOnSubExp tv n <*> mapOnSubExp tv x <*> mapOnSubExp tv s <*> pure et) mapExpM tv (BasicOp (Replicate shape vexp)) =@@ -113,17 +123,22 @@ mapExpM tv (BasicOp (Scratch t shape)) =   BasicOp <$> (Scratch t <$> mapM (mapOnSubExp tv) shape) mapExpM tv (BasicOp (Reshape shape arrexp)) =-  BasicOp <$> (Reshape <$>-               mapM (Data.Traversable.traverse (mapOnSubExp tv)) shape <*>-               mapOnVName tv arrexp)+  BasicOp+    <$> ( Reshape+            <$> mapM (Data.Traversable.traverse (mapOnSubExp tv)) shape+            <*> mapOnVName tv arrexp+        ) mapExpM tv (BasicOp (Rearrange perm e)) =   BasicOp <$> (Rearrange perm <$> mapOnVName tv e) mapExpM tv (BasicOp (Rotate es e)) =   BasicOp <$> (Rotate <$> mapM (mapOnSubExp tv) es <*> mapOnVName tv e) mapExpM tv (BasicOp (Concat i x ys size)) =-  BasicOp <$> (Concat i <$>-               mapOnVName tv x <*> mapM (mapOnVName tv) ys <*>-               mapOnSubExp tv size)+  BasicOp+    <$> ( Concat i+            <$> mapOnVName tv x+            <*> mapM (mapOnVName tv) ys+            <*> mapOnSubExp tv size+        ) mapExpM tv (BasicOp (Copy e)) =   BasicOp <$> (Copy <$> mapOnVName tv e) mapExpM tv (BasicOp (Manifest perm e)) =@@ -136,25 +151,31 @@   ctxparams' <- mapM (mapOnFParam tv) ctxparams   valparams' <- mapM (mapOnFParam tv) valparams   form' <- mapOnLoopForm tv form-  let scope = scopeOf form' <> scopeOfFParams (ctxparams'++valparams')-  DoLoop <$>-    (zip ctxparams' <$> mapM (mapOnSubExp tv) ctxinits) <*>-    (zip valparams' <$> mapM (mapOnSubExp tv) valinits) <*>-    pure form' <*> mapOnBody tv scope loopbody-  where (ctxparams,ctxinits) = unzip ctxmerge-        (valparams,valinits) = unzip valmerge+  let scope = scopeOf form' <> scopeOfFParams (ctxparams' ++ valparams')+  DoLoop+    <$> (zip ctxparams' <$> mapM (mapOnSubExp tv) ctxinits)+    <*> (zip valparams' <$> mapM (mapOnSubExp tv) valinits)+    <*> pure form'+    <*> mapOnBody tv scope loopbody+  where+    (ctxparams, ctxinits) = unzip ctxmerge+    (valparams, valinits) = unzip valmerge mapExpM tv (Op op) =   Op <$> mapOnOp tv op  mapOnShape :: Monad m => Mapper flore tlore m -> Shape -> m Shape mapOnShape tv (Shape ds) = Shape <$> mapM (mapOnSubExp tv) ds -mapOnLoopForm :: Monad m =>-                 Mapper flore tlore m -> LoopForm flore -> m (LoopForm tlore)+mapOnLoopForm ::+  Monad m =>+  Mapper flore tlore m ->+  LoopForm flore ->+  m (LoopForm tlore) mapOnLoopForm tv (ForLoop i it bound loop_vars) =-  ForLoop <$> mapOnVName tv i <*> pure it <*> mapOnSubExp tv bound <*>-  (zip <$> mapM (mapOnLParam tv) loop_lparams <*> mapM (mapOnVName tv) loop_arrs)-  where (loop_lparams,loop_arrs) = unzip loop_vars+  ForLoop <$> mapOnVName tv i <*> pure it <*> mapOnSubExp tv bound+    <*> (zip <$> mapM (mapOnLParam tv) loop_lparams <*> mapM (mapOnVName tv) loop_arrs)+  where+    (loop_lparams, loop_arrs) = unzip loop_vars mapOnLoopForm tv (WhileLoop cond) =   WhileLoop <$> mapOnVName tv cond @@ -165,43 +186,46 @@ -- | Express a monad expression on a syntax node.  Each element of -- this structure expresses the action to be performed on a given -- child.-data Walker lore m = Walker {-    walkOnSubExp :: SubExp -> m ()-  , walkOnBody :: Scope lore -> Body lore -> m ()-  , walkOnVName :: VName -> m ()-  , walkOnRetType :: RetType lore -> m ()-  , walkOnBranchType :: BranchType lore -> m ()-  , walkOnFParam :: FParam lore -> m ()-  , walkOnLParam :: LParam lore -> m ()-  , walkOnOp :: Op lore -> m ()+data Walker lore m = Walker+  { walkOnSubExp :: SubExp -> m (),+    walkOnBody :: Scope lore -> Body lore -> m (),+    walkOnVName :: VName -> m (),+    walkOnRetType :: RetType lore -> m (),+    walkOnBranchType :: BranchType lore -> m (),+    walkOnFParam :: FParam lore -> m (),+    walkOnLParam :: LParam lore -> m (),+    walkOnOp :: Op lore -> m ()   }  -- | A no-op traversal. identityWalker :: Monad m => Walker lore m-identityWalker = Walker {-                   walkOnSubExp = const $ return ()-                 , walkOnBody = const $ const $ return ()-                 , walkOnVName = const $ return ()-                 , walkOnRetType = const $ return ()-                 , walkOnBranchType = const $ return ()-                 , walkOnFParam = const $ return ()-                 , walkOnLParam = const $ return ()-                 , walkOnOp = const $ return ()-                 }+identityWalker =+  Walker+    { walkOnSubExp = const $ return (),+      walkOnBody = const $ const $ return (),+      walkOnVName = const $ return (),+      walkOnRetType = const $ return (),+      walkOnBranchType = const $ return (),+      walkOnFParam = const $ return (),+      walkOnLParam = const $ return (),+      walkOnOp = const $ return ()+    }  walkOnShape :: Monad m => Walker lore m -> Shape -> m () walkOnShape tv (Shape ds) = mapM_ (walkOnSubExp tv) ds  walkOnType :: Monad m => Walker lore m -> Type -> m ()-walkOnType _ Prim{} = return ()-walkOnType _ Mem{} = return ()+walkOnType _ Prim {} = return ()+walkOnType _ Mem {} = return () walkOnType tv (Array _ shape _) = walkOnShape tv shape  walkOnLoopForm :: Monad m => Walker lore m -> LoopForm lore -> m () walkOnLoopForm tv (ForLoop i _ bound loop_vars) =-  walkOnVName tv i >> walkOnSubExp tv bound >>-  mapM_ (walkOnLParam tv) loop_lparams >> mapM_ (walkOnVName tv) loop_arrs-  where (loop_lparams,loop_arrs) = unzip loop_vars+  walkOnVName tv i >> walkOnSubExp tv bound+    >> mapM_ (walkOnLParam tv) loop_lparams+    >> mapM_ (walkOnVName tv) loop_arrs+  where+    (loop_lparams, loop_arrs) = unzip loop_vars walkOnLoopForm tv (WhileLoop cond) =   walkOnVName tv cond @@ -229,9 +253,9 @@ walkExpM tv (BasicOp (Index arr slice)) =   walkOnVName tv arr >> mapM_ (traverse_ (walkOnSubExp tv)) slice walkExpM tv (BasicOp (Update arr slice se)) =-  walkOnVName tv arr >>-  mapM_ (traverse_ (walkOnSubExp tv)) slice >>-  walkOnSubExp tv se+  walkOnVName tv arr+    >> mapM_ (traverse_ (walkOnSubExp tv)) slice+    >> walkOnSubExp tv se walkExpM tv (BasicOp (Iota n x s _)) =   walkOnSubExp tv n >> walkOnSubExp tv x >> walkOnSubExp tv s walkExpM tv (BasicOp (Replicate shape vexp)) =@@ -260,9 +284,10 @@   walkOnLoopForm tv form   mapM_ (walkOnSubExp tv) ctxinits   mapM_ (walkOnSubExp tv) valinits-  let scope = scopeOfFParams (ctxparams++valparams) <> scopeOf form+  let scope = scopeOfFParams (ctxparams ++ valparams) <> scopeOf form   walkOnBody tv scope loopbody-  where (ctxparams,ctxinits) = unzip ctxmerge-        (valparams,valinits) = unzip valmerge+  where+    (ctxparams, ctxinits) = unzip ctxmerge+    (valparams, valinits) = unzip valmerge walkExpM tv (Op op) =   walkOnOp tv op
src/Futhark/Internalise.hs view
@@ -1,1900 +1,2181 @@-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE Strict #-}-{-# LANGUAGE Safe #-}--- |------ This module implements a transformation from source to core--- Futhark.----module Futhark.Internalise (internaliseProg) where--import Control.Monad.State-import Control.Monad.Reader-import Data.Bitraversable-import qualified Data.Map.Strict as M-import qualified Data.Set as S-import Data.List (find, intercalate, intersperse, nub, transpose)-import qualified Data.List.NonEmpty as NE--import Language.Futhark as E hiding (TypeArg)-import Language.Futhark.Semantic (Imports)-import Futhark.IR.SOACS as I hiding (stmPattern)-import Futhark.Transform.Rename as I-import Futhark.MonadFreshNames-import Futhark.Tools-import Futhark.Util (splitAt3)--import Futhark.Internalise.Monad as I-import Futhark.Internalise.AccurateSizes-import Futhark.Internalise.TypesValues-import Futhark.Internalise.Bindings-import Futhark.Internalise.Lambdas-import Futhark.Internalise.Defunctorise as Defunctorise-import Futhark.Internalise.Defunctionalise as Defunctionalise-import Futhark.Internalise.Monomorphise as Monomorphise---- | Convert a program in source Futhark to a program in the Futhark--- core language.-internaliseProg :: MonadFreshNames m =>-                   Bool -> Imports -> m (I.Prog SOACS)-internaliseProg always_safe prog = do-  prog_decs <- Defunctorise.transformProg prog-  prog_decs' <- Monomorphise.transformProg prog_decs-  prog_decs'' <- Defunctionalise.transformProg prog_decs'-  (consts, funs) <--    runInternaliseM always_safe (internaliseValBinds prog_decs'')-  I.renameProg $ I.Prog consts funs--internaliseAttr :: E.AttrInfo -> Attr-internaliseAttr (E.AttrAtom v) = I.AttrAtom v-internaliseAttr (E.AttrComp f attrs) = I.AttrComp f $ map internaliseAttr attrs--internaliseAttrs :: [E.AttrInfo] -> Attrs-internaliseAttrs = mconcat . map (oneAttr . internaliseAttr)--internaliseValBinds :: [E.ValBind] -> InternaliseM ()-internaliseValBinds = mapM_ internaliseValBind--internaliseFunName :: VName -> [E.Pattern] -> InternaliseM Name-internaliseFunName ofname [] = return $ nameFromString $ pretty ofname ++ "f"-internaliseFunName ofname _  = do-  info <- lookupFunction' ofname-  -- In some rare cases involving local functions, the same function-  -- name may be re-used in multiple places.  We check whether the-  -- function name has already been used, and generate a new one if-  -- so.-  case info of-    Just _ -> nameFromString . pretty <$> newNameFromString (baseString ofname)-    Nothing -> return $ nameFromString $ pretty ofname--internaliseValBind :: E.ValBind -> InternaliseM ()-internaliseValBind fb@(E.ValBind entry fname retdecl (Info (rettype, _)) tparams params body _ attrs loc) = do-  localConstsScope $ bindingParams tparams params $ \shapeparams params' -> do-    let shapenames = map I.paramName shapeparams-        normal_params = shapenames ++ map I.paramName (concat params')-        normal_param_names = namesFromList normal_params--    fname' <- internaliseFunName fname params--    msg <- case retdecl of-             Just dt -> errorMsg .-                        ("Function return value does not match shape of type ":) <$>-                        typeExpForError dt-             Nothing -> return $ errorMsg ["Function return value does not match shape of declared return type."]--    ((rettype', body_res), body_stms) <- collectStms $ do-      body_res <- internaliseExp "res" body-      rettype_bad <- internaliseReturnType rettype-      let rettype' = zeroExts rettype_bad-      return (rettype', body_res)-    body' <- ensureResultExtShape msg loc (map I.fromDecl rettype') $-             mkBody body_stms body_res--    constants <- allConsts-    let free_in_fun = freeIn body'-                      `namesSubtract` normal_param_names-                      `namesSubtract` constants--    used_free_params <- forM (namesToList free_in_fun) $ \v -> do-      v_t <- lookupType v-      return $ Param v $ toDecl v_t Nonunique--    let free_shape_params = map (`Param` I.Prim int32) $-                            concatMap (I.shapeVars . I.arrayShape . I.paramType) used_free_params-        free_params = nub $ free_shape_params ++ used_free_params-        all_params = free_params ++ shapeparams ++ concat params'--    let fd = I.FunDef Nothing (internaliseAttrs attrs) fname'-             rettype' all_params body'--    if null params'-      then bindConstant fname fd-      else bindFunction fname fd-           (fname',-            map I.paramName free_params,-            shapenames,-            map declTypeOf $ concat params',-            all_params,-            applyRetType rettype' all_params)--  case entry of Just (Info entry') -> generateEntryPoint entry' fb-                Nothing -> return ()--  where-    -- | Recompute existential sizes to start from zero.-    -- Necessary because some convoluted constructions will start-    -- them from somewhere else.-    zeroExts ts = generaliseExtTypes ts ts--allDimsFreshInType :: MonadFreshNames m => E.PatternType -> m E.PatternType-allDimsFreshInType = bitraverse onDim pure-  where onDim (E.NamedDim v) =-          E.NamedDim . E.qualName <$> newVName (baseString $ E.qualLeaf v)-        onDim _ =-          E.NamedDim . E.qualName <$> newVName "size"---- | Replace all named dimensions with a fresh name, and remove all--- constant dimensions.  The point is to remove the constraints, but--- keep the names around.  We use this for constructing the entry--- point parameters.-allDimsFreshInPat :: MonadFreshNames m => E.Pattern -> m E.Pattern-allDimsFreshInPat (PatternAscription p _ _) =-  allDimsFreshInPat p-allDimsFreshInPat (PatternParens p _) =-  allDimsFreshInPat p-allDimsFreshInPat (Id v (Info t) loc) =-  Id v <$> (Info <$> allDimsFreshInType t) <*> pure loc-allDimsFreshInPat (TuplePattern ps loc) =-  TuplePattern <$> mapM allDimsFreshInPat ps <*> pure loc-allDimsFreshInPat (RecordPattern ps loc) =-  RecordPattern <$> mapM (traverse allDimsFreshInPat) ps <*> pure loc-allDimsFreshInPat (Wildcard (Info t) loc) =-  Wildcard <$> (Info <$> allDimsFreshInType t) <*> pure loc-allDimsFreshInPat (PatternLit e (Info t) loc) =-  PatternLit e <$> (Info <$> allDimsFreshInType t) <*> pure loc-allDimsFreshInPat (PatternConstr c (Info t) pats loc) =-  PatternConstr c <$> (Info <$> allDimsFreshInType t) <*>-  mapM allDimsFreshInPat pats <*> pure loc--generateEntryPoint :: E.EntryPoint -> E.ValBind -> InternaliseM ()-generateEntryPoint (E.EntryPoint e_paramts e_rettype) vb = localConstsScope $ do-  let (E.ValBind _ ofname _ (Info (rettype, _)) _ params _ _ attrs loc) = vb-  -- We replace all shape annotations, so there should be no constant-  -- parameters here.-  params_fresh <- mapM allDimsFreshInPat params-  let tparams = map (`E.TypeParamDim` mempty) $ S.toList $-                mconcat $ map E.patternDimNames params_fresh-  bindingParams tparams params_fresh $ \shapeparams params' -> do-    entry_rettype <- internaliseEntryReturnType $ anySizes rettype-    let entry' = entryPoint (zip e_paramts params') (e_rettype, entry_rettype)-        args = map (I.Var . I.paramName) $ concat params'--    entry_body <- insertStmsM $ do-      -- Special case the (rare) situation where the entry point is-      -- not a function.-      maybe_const <- lookupConst ofname-      vals <- case maybe_const of-                Just ses ->-                  return ses-                Nothing ->-                  fst <$> funcall "entry_result" (E.qualName ofname) args loc-      ctx <- extractShapeContext (concat entry_rettype) <$>-             mapM (fmap I.arrayDims . subExpType) vals-      resultBodyM (ctx ++ vals)--    addFunDef $-      I.FunDef (Just entry') (internaliseAttrs attrs)-      (baseName ofname)-      (concat entry_rettype)-      (shapeparams ++ concat params') entry_body--entryPoint :: [(E.EntryType, [I.FParam])]-           -> (E.EntryType,-               [[I.TypeBase ExtShape Uniqueness]])-           -> I.EntryPoint-entryPoint params (eret, crets) =-  (concatMap (entryPointType . preParam) params,-   case (isTupleRecord $ entryType eret,-         entryAscribed eret) of-     (Just ts, Just (E.TETuple e_ts _)) ->-       concatMap entryPointType $-       zip (zipWith E.EntryType ts (map Just e_ts)) crets-     (Just ts, Nothing) ->-       concatMap entryPointType $-       zip (map (`E.EntryType` Nothing) ts) crets-     _ ->-       entryPointType (eret, concat crets))-  where preParam (e_t, ps) = (e_t, staticShapes $ map I.paramDeclType ps)--        entryPointType (t, ts)-          | E.Scalar (E.Prim E.Unsigned{}) <- E.entryType t =-              [I.TypeUnsigned]-          | E.Array _ _ (E.Prim E.Unsigned{}) _ <- E.entryType t =-              [I.TypeUnsigned]-          | E.Scalar E.Prim{} <- E.entryType t =-              [I.TypeDirect]-          | E.Array _ _ E.Prim{} _ <- E.entryType t =-              [I.TypeDirect]-          | otherwise =-              [I.TypeOpaque desc $ length ts]-          where desc = maybe (pretty t') typeExpOpaqueName $ E.entryAscribed t-                t' = noSizes (E.entryType t) `E.setUniqueness` Nonunique--        -- | We remove dimension arguments such that we hopefully end-        -- up with a simpler type name for the entry point.  The-        -- intent is that if an entry point uses a type 'nasty [w] [h]',-        -- then we should turn that into an opaque type just called-        -- 'nasty'.  Also, we try to give arrays of opaques a nicer name.-        typeExpOpaqueName (TEApply te TypeArgExpDim{} _) =-          typeExpOpaqueName te-        typeExpOpaqueName (TEArray te _ _) =-          let (d, te') = withoutDims te-          in "arr_" ++ typeExpOpaqueName te' ++-             "_" ++ show (1 + d) ++ "d"-        typeExpOpaqueName te = pretty te--        withoutDims (TEArray te _ _) =-          let (d, te') = withoutDims te-          in (d+1, te')-        withoutDims te = (0::Int, te)--internaliseIdent :: E.Ident -> InternaliseM I.VName-internaliseIdent (E.Ident name (Info tp) loc) =-  case tp of-    E.Scalar E.Prim{} -> return name-    _ -> error $ "Futhark.Internalise.internaliseIdent: asked to internalise non-prim-typed ident '"-         ++ pretty name ++ " of type " ++ pretty tp ++-         " at " ++ locStr loc ++ "."--internaliseBody :: E.Exp -> InternaliseM Body-internaliseBody e = insertStmsM $ resultBody <$> internaliseExp "res" e--bodyFromStms :: InternaliseM (Result, a)-             -> InternaliseM (Body, a)-bodyFromStms m = do-  ((res, a), stms) <- collectStms m-  (,a) <$> mkBodyM stms res--internaliseExp :: String -> E.Exp -> InternaliseM [I.SubExp]--internaliseExp desc (E.Parens e _) =-  internaliseExp desc e--internaliseExp desc (E.QualParens _ e _) =-  internaliseExp desc e--internaliseExp desc (E.StringLit vs _) =-  fmap pure $ letSubExp desc $-  I.BasicOp $ I.ArrayLit (map constant vs) $ I.Prim int8--internaliseExp _ (E.Var (E.QualName _ name) (Info t) loc) = do-  subst <- lookupSubst name-  case subst of-    Just substs -> return substs-    Nothing     -> do-      -- If this identifier is the name of a constant, we have to turn it-      -- into a call to the corresponding function.-      is_const <- lookupConst name-      case is_const of-        Just ses -> return ses-        Nothing -> (:[]) . I.Var <$> internaliseIdent (E.Ident name (Info t) loc)--internaliseExp desc (E.Index e idxs (Info ret, Info retext) loc) = do-  vs <- internaliseExpToVars "indexed" e-  dims <- case vs of []  -> return [] -- Will this happen?-                     v:_ -> I.arrayDims <$> lookupType v-  (idxs', cs) <- internaliseSlice loc dims idxs-  let index v = do v_t <- lookupType v-                   return $ I.BasicOp $ I.Index v $ fullSlice v_t idxs'-  ses <- certifying cs $ letSubExps desc =<< mapM index vs-  bindExtSizes (E.toStruct ret) retext ses-  return ses---- XXX: we map empty records and tuples to bools, because otherwise--- arrays of unit will lose their sizes.-internaliseExp _ (E.TupLit [] _) =-  return [constant True]-internaliseExp _ (E.RecordLit [] _) =-  return [constant True]--internaliseExp desc (E.TupLit es _) = concat <$> mapM (internaliseExp desc) es--internaliseExp desc (E.RecordLit orig_fields _) =-  concatMap snd . sortFields . M.unions <$> mapM internaliseField orig_fields-  where internaliseField (E.RecordFieldExplicit name e _) =-          M.singleton name <$> internaliseExp desc e-        internaliseField (E.RecordFieldImplicit name t loc) =-          internaliseField $ E.RecordFieldExplicit (baseName name)-          (E.Var (E.qualName name) t loc) loc--internaliseExp desc (E.ArrayLit es (Info arr_t) loc)-  -- If this is a multidimensional array literal of primitives, we-  -- treat it specially by flattening it out followed by a reshape.-  -- This cuts down on the amount of statements that are produced, and-  -- thus allows us to efficiently handle huge array literals - a-  -- corner case, but an important one.-  | Just ((eshape,e'):es') <- mapM isArrayLiteral es,-    not $ null eshape,-    all ((eshape==) . fst) es',-    Just basetype <- E.peelArray (length eshape) arr_t = do-      let flat_lit = E.ArrayLit (e' ++ concatMap snd es') (Info basetype) loc-          new_shape = length es:eshape-      flat_arrs <- internaliseExpToVars "flat_literal" flat_lit-      forM flat_arrs $ \flat_arr -> do-        flat_arr_t <- lookupType flat_arr-        let new_shape' = reshapeOuter (map (DimNew . constant) new_shape)-                         1 $ I.arrayShape flat_arr_t-        letSubExp desc $ I.BasicOp $ I.Reshape new_shape' flat_arr--  | otherwise = do-      es' <- mapM (internaliseExp "arr_elem") es-      arr_t_ext <- internaliseReturnType (E.toStruct arr_t)--      rowtypes <--        case mapM (fmap rowType . hasStaticShape . I.fromDecl) arr_t_ext of-          Just ts -> pure ts-          Nothing ->-            -- XXX: the monomorphiser may create single-element array-            -- literals with an unknown row type.  In those cases we-            -- need to look at the types of the actual elements.-            -- Fixing this in the monomorphiser is a lot more tricky-            -- than just working around it here.-            case es' of-              [] -> error $ "internaliseExp ArrayLit: existential type: " ++ pretty arr_t-              e':_ -> mapM subExpType e'--      let arraylit ks rt = do-            ks' <- mapM (ensureShape-                         "shape of element differs from shape of first element"-                         loc rt "elem_reshaped") ks-            return $ I.BasicOp $ I.ArrayLit ks' rt--      letSubExps desc =<<-        if null es'-        then mapM (arraylit []) rowtypes-        else zipWithM arraylit (transpose es') rowtypes--  where isArrayLiteral :: E.Exp -> Maybe ([Int],[E.Exp])-        isArrayLiteral (E.ArrayLit inner_es _ _) = do-          (eshape,e):inner_es' <- mapM isArrayLiteral inner_es-          guard $ all ((eshape==) . fst) inner_es'-          return (length inner_es:eshape, e ++ concatMap snd inner_es')-        isArrayLiteral e =-          Just ([], [e])--internaliseExp desc (E.Range start maybe_second end (Info ret, Info retext) loc) = do-  start' <- internaliseExp1 "range_start" start-  end' <- internaliseExp1 "range_end" $ case end of-    DownToExclusive e -> e-    ToInclusive e -> e-    UpToExclusive e -> e-  maybe_second' <--    traverse (internaliseExp1 "range_second") maybe_second--  -- Construct an error message in case the range is invalid.-  let conv = case E.typeOf start of-               E.Scalar (E.Prim (E.Unsigned _)) -> asIntZ Int32-               _ -> asIntS Int32-  start'_i32 <- conv start'-  end'_i32 <- conv end'-  maybe_second'_i32 <- traverse conv maybe_second'-  let errmsg =-        errorMsg $-        ["Range "] ++-        [ErrorInt32 start'_i32] ++-        (case maybe_second'_i32 of-           Nothing -> []-           Just second_i32 -> ["..", ErrorInt32 second_i32]) ++-        (case end of-           DownToExclusive{} -> ["..>"]-           ToInclusive{} -> ["..."]-           UpToExclusive{} -> ["..<"]) ++-        [ErrorInt32 end'_i32, " is invalid."]--  (it, le_op, lt_op) <--    case E.typeOf start of-      E.Scalar (E.Prim (E.Signed it)) -> return (it, CmpSle it, CmpSlt it)-      E.Scalar (E.Prim (E.Unsigned it)) -> return (it, CmpUle it, CmpUlt it)-      start_t -> error $ "Start value in range has type " ++ pretty start_t--  let one = intConst it 1-      negone = intConst it (-1)-      default_step = case end of DownToExclusive{} -> negone-                                 ToInclusive{} -> one-                                 UpToExclusive{} -> one--  (step, step_zero) <- case maybe_second' of-    Just second' -> do-      subtracted_step <- letSubExp "subtracted_step" $-        I.BasicOp $ I.BinOp (I.Sub it I.OverflowWrap) second' start'-      step_zero <- letSubExp "step_zero" $ I.BasicOp $ I.CmpOp (I.CmpEq $ IntType it) start' second'-      return (subtracted_step, step_zero)-    Nothing ->-      return (default_step, constant False)--  step_sign <- letSubExp "s_sign" $ BasicOp $ I.UnOp (I.SSignum it) step-  step_sign_i32 <- asIntS Int32 step_sign--  bounds_invalid_downwards <- letSubExp "bounds_invalid_downwards" $-                              I.BasicOp $ I.CmpOp le_op start' end'-  bounds_invalid_upwards <- letSubExp "bounds_invalid_upwards" $-                            I.BasicOp $ I.CmpOp lt_op end' start'--  (distance, step_wrong_dir, bounds_invalid) <- case end of-    DownToExclusive{} -> do-      step_wrong_dir <- letSubExp "step_wrong_dir" $-                        I.BasicOp $ I.CmpOp (I.CmpEq $ IntType it) step_sign one-      distance <- letSubExp "distance" $-                  I.BasicOp $ I.BinOp (Sub it I.OverflowWrap) start' end'-      distance_i32 <- asIntZ Int32 distance-      return (distance_i32, step_wrong_dir, bounds_invalid_downwards)-    UpToExclusive{} -> do-      step_wrong_dir <- letSubExp "step_wrong_dir" $-                        I.BasicOp $ I.CmpOp (I.CmpEq $ IntType it) step_sign negone-      distance <- letSubExp "distance" $ I.BasicOp $ I.BinOp (Sub it I.OverflowWrap) end' start'-      distance_i32 <- asIntZ Int32 distance-      return (distance_i32, step_wrong_dir, bounds_invalid_upwards)-    ToInclusive{} -> do-      downwards <- letSubExp "downwards" $-                   I.BasicOp $ I.CmpOp (I.CmpEq $ IntType it) step_sign negone-      distance_downwards_exclusive <--        letSubExp "distance_downwards_exclusive" $-        I.BasicOp $ I.BinOp (Sub it I.OverflowWrap) start' end'-      distance_upwards_exclusive <--        letSubExp "distance_upwards_exclusive" $-        I.BasicOp $ I.BinOp (Sub it I.OverflowWrap) end' start'--      bounds_invalid <- letSubExp "bounds_invalid" $-                        I.If downwards-                        (resultBody [bounds_invalid_downwards])-                        (resultBody [bounds_invalid_upwards]) $-                        ifCommon [I.Prim I.Bool]-      distance_exclusive <- letSubExp "distance_exclusive" $-                            I.If downwards-                            (resultBody [distance_downwards_exclusive])-                            (resultBody [distance_upwards_exclusive]) $-                            ifCommon [I.Prim $ IntType it]-      distance_exclusive_i32 <- asIntZ Int32 distance_exclusive-      distance <- letSubExp "distance" $-                  I.BasicOp $ I.BinOp (Add Int32 I.OverflowWrap)-                  distance_exclusive_i32 (intConst Int32 1)-      return (distance, constant False, bounds_invalid)--  step_invalid <- letSubExp "step_invalid" $-                  I.BasicOp $ I.BinOp I.LogOr step_wrong_dir step_zero--  invalid <- letSubExp "range_invalid" $-             I.BasicOp $ I.BinOp I.LogOr step_invalid bounds_invalid-  valid <- letSubExp "valid" $ I.BasicOp $ I.UnOp I.Not invalid-  cs <- assert "range_valid_c" valid errmsg loc--  step_i32 <- asIntS Int32 step-  pos_step <- letSubExp "pos_step" $-              I.BasicOp $ I.BinOp (Mul Int32 I.OverflowWrap) step_i32 step_sign_i32--  num_elems <- certifying cs $-               letSubExp "num_elems" $-               I.BasicOp $ I.BinOp (SDivUp Int32 I.Unsafe) distance pos_step--  se <- letSubExp desc (I.BasicOp $ I.Iota num_elems start' step it)-  bindExtSizes (E.toStruct ret) retext [se]-  return [se]--internaliseExp desc (E.Ascript e _ _) =-  internaliseExp desc e--internaliseExp desc (E.Coerce e (TypeDecl dt (Info et)) (Info ret, Info retext) loc) = do-  ses <- internaliseExp desc e-  ts <- internaliseReturnType et-  dt' <- typeExpForError dt-  bindExtSizes (E.toStruct ret) retext ses-  forM (zip ses ts) $ \(e',t') -> do-    dims <- arrayDims <$> subExpType e'-    let parts = ["Value of (core language) shape ("] ++-                intersperse ", " (map ErrorInt32 dims) ++-                [") cannot match shape of type `"] ++ dt' ++ ["`."]-    ensureExtShape (errorMsg parts) loc (I.fromDecl t') desc e'--internaliseExp desc (E.Negate e _) = do-  e' <- internaliseExp1 "negate_arg" e-  et <- subExpType e'-  case et of I.Prim (I.IntType t) ->-               letTupExp' desc $ I.BasicOp $ I.BinOp (I.Sub t I.OverflowWrap) (I.intConst t 0) e'-             I.Prim (I.FloatType t) ->-               letTupExp' desc $ I.BasicOp $ I.BinOp (I.FSub t) (I.floatConst t 0) e'-             _ -> error "Futhark.Internalise.internaliseExp: non-numeric type in Negate"--internaliseExp desc e@E.Apply{} = do-  (qfname, args, ret, retext) <- findFuncall e-  -- Argument evaluation is outermost-in so that any existential sizes-  -- created by function applications can be brought into scope.-  let fname = nameFromString $ pretty $ baseName $ qualLeaf qfname-      loc = srclocOf e-      arg_desc = nameToString fname ++ "_arg"--  -- Some functions are magical (overloaded) and we handle that here.-  ses <--    case () of-      -- Overloaded functions never take array arguments (except-      -- equality, but those cannot be existential), so we can safely-      -- ignore the existential dimensions.-      () | Just internalise <- isOverloadedFunction qfname (map fst args) loc ->-             internalise desc--         | Just (rettype, _) <- M.lookup fname I.builtInFunctions -> do-             let tag ses = [ (se, I.Observe) | se <- ses ]-             args' <- reverse <$> mapM (internaliseArg arg_desc) (reverse args)-             let args'' = concatMap tag args'-             letTupExp' desc $ I.Apply fname args'' [I.Prim rettype]-               (Safe, loc, [])--         | otherwise -> do-             args' <- concat . reverse <$> mapM (internaliseArg arg_desc) (reverse args)-             fst <$> funcall desc qfname args' loc--  bindExtSizes ret retext ses-  return ses--internaliseExp desc (E.LetPat pat e body (Info ret, Info retext) _) = do-  ses <- internalisePat desc pat e body (internaliseExp desc)-  bindExtSizes (E.toStruct ret) retext ses-  return ses--internaliseExp desc (E.LetFun ofname (tparams, params, retdecl, Info rettype, body) letbody _ loc) = do-  internaliseValBind $-    E.ValBind Nothing ofname retdecl (Info (rettype, [])) tparams params body Nothing mempty loc-  internaliseExp desc letbody--internaliseExp desc (E.DoLoop sparams mergepat mergeexp form loopbody (Info (ret, retext)) loc) = do-  ses <- internaliseExp "loop_init" mergeexp-  ((loopbody', (form', shapepat, mergepat', mergeinit')), initstms) <--    collectStms $ handleForm ses form--  addStms initstms-  mergeinit_ts' <- mapM subExpType mergeinit'--  ctxinit <- argShapes (map I.paramName shapepat) mergepat' mergeinit_ts'--  let ctxmerge = zip shapepat ctxinit-      valmerge = zip mergepat' mergeinit'-      dropCond = case form of E.While{} -> drop 1-                              _         -> id--  -- Ensure that the result of the loop matches the shapes of the-  -- merge parameters.  XXX: Ideally they should already match (by-  -- the source language type rules), but some of our-  -- transformations (esp. defunctionalisation) strips out some size-  -- information.  For a type-correct source program, these reshapes-  -- should simplify away.-  let merge = ctxmerge ++ valmerge-      merge_ts = map (I.paramType . fst) merge-  loopbody'' <- localScope (scopeOfFParams $ map fst merge) $-                inScopeOf form' $ insertStmsM $-    resultBodyM-    =<< ensureArgShapes-        "shape of loop result does not match shapes in loop parameter"-        loc (map (I.paramName . fst) ctxmerge) merge_ts-    =<< bodyBind loopbody'--  attrs <- asks envAttrs-  loop_res <- map I.Var . dropCond <$> attributing attrs-              (letTupExp desc (I.DoLoop ctxmerge valmerge form' loopbody''))-  bindExtSizes (E.toStruct ret) retext loop_res-  return loop_res--  where-    sparams' = map (`TypeParamDim` mempty) sparams--    forLoop mergepat' shapepat mergeinit form' =-      bodyFromStms $ inScopeOf form' $ do-        ses <- internaliseExp "loopres" loopbody-        sets <- mapM subExpType ses-        shapeargs <- argShapes (map I.paramName shapepat) mergepat' sets-        return (shapeargs ++ ses,-                (form',-                 shapepat,-                 mergepat',-                 mergeinit))--    handleForm mergeinit (E.ForIn x arr) = do-      arr' <- internaliseExpToVars "for_in_arr" arr-      arr_ts <- mapM lookupType arr'-      let w = arraysSize 0 arr_ts--      i <- newVName "i"--      bindingLoopParams sparams' mergepat $-        \shapepat mergepat' ->-        bindingLambdaParams [x] (map rowType arr_ts) $ \x_params -> do-          let loopvars = zip x_params arr'-          forLoop mergepat' shapepat mergeinit $-            I.ForLoop i Int32 w loopvars--    handleForm mergeinit (E.For i num_iterations) = do-      num_iterations' <- internaliseExp1 "upper_bound" num_iterations-      i' <- internaliseIdent i-      num_iterations_t <- I.subExpType num_iterations'-      it <- case num_iterations_t of-              I.Prim (IntType it) -> return it-              _                   -> error "internaliseExp DoLoop: invalid type"--      bindingLoopParams sparams' mergepat $-        \shapepat mergepat' ->-          forLoop mergepat' shapepat mergeinit $-          I.ForLoop i' it num_iterations' []--    handleForm mergeinit (E.While cond) =-      bindingLoopParams sparams' mergepat $ \shapepat mergepat' -> do-        mergeinit_ts <- mapM subExpType mergeinit-        -- We need to insert 'cond' twice - once for the initial-        -- condition (do we enter the loop at all?), and once with the-        -- result values of the loop (do we continue into the next-        -- iteration?).  This is safe, as the type rules for the-        -- external language guarantees that 'cond' does not consume-        -- anything.-        shapeinit <- argShapes (map I.paramName shapepat) mergepat' mergeinit_ts--        (loop_initial_cond, init_loop_cond_bnds) <- collectStms $ do-          forM_ (zip shapepat shapeinit) $ \(p, se) ->-            letBindNames [paramName p] $ BasicOp $ SubExp se-          forM_ (zip mergepat' mergeinit) $ \(p, se) ->-            unless (se == I.Var (paramName p)) $-            letBindNames [paramName p] $ BasicOp $-            case se of I.Var v | not $ primType $ paramType p ->-                                   Reshape (map DimCoercion $ arrayDims $ paramType p) v-                       _ -> SubExp se-          internaliseExp1 "loop_cond" cond--        addStms init_loop_cond_bnds--        bodyFromStms $ do-          ses <- internaliseExp "loopres" loopbody-          sets <- mapM subExpType ses-          loop_while <- newParam "loop_while" $ I.Prim I.Bool-          shapeargs <- argShapes (map I.paramName shapepat) mergepat' sets--          -- Careful not to clobber anything.-          loop_end_cond_body <- renameBody <=< insertStmsM $ do-            forM_ (zip shapepat shapeargs) $ \(p, se) ->-              unless (se == I.Var (paramName p)) $-              letBindNames [paramName p] $ BasicOp $ SubExp se-            forM_ (zip mergepat' ses) $ \(p, se) ->-              unless (se == I.Var (paramName p)) $-              letBindNames [paramName p] $ BasicOp $-              case se of I.Var v | not $ primType $ paramType p ->-                                     Reshape (map DimCoercion $ arrayDims $ paramType p) v-                         _ -> SubExp se-            resultBody <$> internaliseExp "loop_cond" cond-          loop_end_cond <- bodyBind loop_end_cond_body--          return (shapeargs++loop_end_cond++ses,-                  (I.WhileLoop $ I.paramName loop_while,-                   shapepat,-                   loop_while : mergepat',-                   loop_initial_cond : mergeinit))--internaliseExp desc (E.LetWith name src idxs ve body t loc) = do-  let pat = E.Id (E.identName name) (E.identType name) loc-      src_t = E.fromStruct <$> E.identType src-      e = E.Update (E.Var (E.qualName $ E.identName src) src_t loc) idxs ve loc-  internaliseExp desc $ E.LetPat pat e body (t, Info []) loc--internaliseExp desc (E.Update src slice ve loc) = do-  ves <- internaliseExp "lw_val" ve-  srcs <- internaliseExpToVars "src" src-  dims <- case srcs of-            [] -> return [] -- Will this happen?-            v:_ -> I.arrayDims <$> lookupType v-  (idxs', cs) <- internaliseSlice loc dims slice--  let comb sname ve' = do-        sname_t <- lookupType sname-        let full_slice = fullSlice sname_t idxs'-            rowtype = sname_t `setArrayDims` sliceDims full_slice-        ve'' <- ensureShape "shape of value does not match shape of source array"-                loc rowtype "lw_val_correct_shape" ve'-        letInPlace desc sname full_slice $ BasicOp $ SubExp ve''-  certifying cs $ map I.Var <$> zipWithM comb srcs ves--internaliseExp desc (E.RecordUpdate src fields ve _ _) = do-  src' <- internaliseExp desc src-  ve' <- internaliseExp desc ve-  replace (E.typeOf src `setAliases` ()) fields ve' src'-  where replace (E.Scalar (E.Record m)) (f:fs) ve' src'-          | Just t <- M.lookup f m = do-          i <- fmap sum $ mapM (internalisedTypeSize . snd) $-               takeWhile ((/=f) . fst) $ sortFields m-          k <- internalisedTypeSize t-          let (bef, to_update, aft) = splitAt3 i k src'-          src'' <- replace t fs ve' to_update-          return $ bef ++ src'' ++ aft-        replace _ _ ve' _ = return ve'--internaliseExp desc (E.Attr attr e _) =-  local f $ internaliseExp desc e-  where attrs = oneAttr $ internaliseAttr attr-        f env | "unsafe" `inAttrs` attrs,-                not $ envSafe env =-                  env { envDoBoundsChecks = False }-              | otherwise =-                  env { envAttrs = envAttrs env <> attrs }--internaliseExp desc (E.Assert e1 e2 (Info check) loc) = do-  e1' <- internaliseExp1 "assert_cond" e1-  c <- assert "assert_c" e1' (errorMsg [ErrorString $ "Assertion is false: " <> check]) loc-  -- Make sure there are some bindings to certify.-  certifying c $ mapM rebind =<< internaliseExp desc e2-  where rebind v = do-          v' <- newVName "assert_res"-          letBindNames [v'] $ I.BasicOp $ I.SubExp v-          return $ I.Var v'--internaliseExp _ (E.Constr c es (Info (E.Scalar (E.Sum fs))) _) = do-  (ts, constr_map) <- internaliseSumType $ M.map (map E.toStruct) fs-  es' <- concat <$> mapM (internaliseExp "payload") es--  let noExt _ = return $ intConst Int32 0-  ts' <- instantiateShapes noExt $ map fromDecl ts--  case M.lookup c constr_map of-    Just (i, js) ->-      (intConst Int8 (toInteger i):) <$> clauses 0 ts' (zip js es')-    Nothing ->-      error "internaliseExp Constr: missing constructor"--  where clauses j (t:ts) js_to_es-          | Just e <- j `lookup` js_to_es =-              (e:) <$> clauses (j+1) ts js_to_es-          | otherwise = do-              blank <- letSubExp "zero" =<< eBlank t-              (blank:) <$> clauses (j+1) ts js_to_es-        clauses _ [] _ =-          return []--internaliseExp _ (E.Constr _ _ (Info t) loc) =-  error $ "internaliseExp: constructor with type " ++ pretty t ++ " at " ++ locStr loc--internaliseExp desc (E.Match e cs (Info ret, Info retext) _) = do-  ses <- internaliseExp (desc ++ "_scrutinee") e-  res <--    case NE.uncons cs of-    (CasePat pCase eCase _, Nothing) -> do-      (_, pertinent) <- generateCond pCase ses-      internalisePat' pCase pertinent eCase (internaliseExp desc)-    (c, Just cs') -> do-      let CasePat pLast eLast _ = NE.last cs'-      bFalse <- do-        (_, pertinent) <- generateCond pLast ses-        eLast' <- internalisePat' pLast pertinent eLast internaliseBody-        foldM (\bf c' -> eBody $ return $ generateCaseIf ses c' bf) eLast' $-          reverse $ NE.init cs'-      letTupExp' desc =<< generateCaseIf ses c bFalse-  bindExtSizes (E.toStruct ret) retext res-  return res---- The "interesting" cases are over, now it's mostly boilerplate.--internaliseExp _ (E.Literal v _) =-  return [I.Constant $ internalisePrimValue v]--internaliseExp _ (E.IntLit v (Info t) _) =-  case t of-    E.Scalar (E.Prim (E.Signed it)) ->-      return [I.Constant $ I.IntValue $ intValue it v]-    E.Scalar (E.Prim (E.Unsigned it)) ->-      return [I.Constant $ I.IntValue $ intValue it v]-    E.Scalar (E.Prim (E.FloatType ft)) ->-      return [I.Constant $ I.FloatValue $ floatValue ft v]-    _ -> error $ "internaliseExp: nonsensical type for integer literal: " ++ pretty t--internaliseExp _ (E.FloatLit v (Info t) _) =-  case t of-    E.Scalar (E.Prim (E.FloatType ft)) ->-      return [I.Constant $ I.FloatValue $ floatValue ft v]-    _ -> error $ "internaliseExp: nonsensical type for float literal: " ++ pretty t--internaliseExp desc (E.If ce te fe (Info ret, Info retext) _) = do-  ses <- letTupExp' desc =<<-         eIf (BasicOp . SubExp <$> internaliseExp1 "cond" ce)-         (internaliseBody te) (internaliseBody fe)-  bindExtSizes (E.toStruct ret) retext ses-  return ses---- Builtin operators are handled specially because they are--- overloaded.-internaliseExp desc (E.BinOp (op, _) _ (xe,_) (ye,_) _ _ loc)-  | Just internalise <- isOverloadedFunction op [xe, ye] loc =-      internalise desc---- User-defined operators are just the same as a function call.-internaliseExp desc (E.BinOp (op, oploc) (Info t)-                     (xarg, Info (xt,xext)) (yarg, Info (yt,yext))-                     _ (Info retext) loc) =-  internaliseExp desc $-  E.Apply (E.Apply (E.Var op (Info t) oploc) xarg (Info (E.diet xt, xext))-           (Info $ foldFunType [E.fromStruct yt] t, Info []) loc)-          yarg (Info (E.diet yt, yext)) (Info t, Info retext) loc--internaliseExp desc (E.Project k e (Info rt) _) = do-  n <- internalisedTypeSize $ rt `setAliases` ()-  i' <- fmap sum $ mapM internalisedTypeSize $-        case E.typeOf e `setAliases` () of-               E.Scalar (Record fs) ->-                 map snd $ takeWhile ((/=k) . fst) $ sortFields fs-               t -> [t]-  take n . drop i' <$> internaliseExp desc e--internaliseExp _ e@E.Lambda{} =-  error $ "internaliseExp: Unexpected lambda at " ++ locStr (srclocOf e)--internaliseExp _ e@E.OpSection{} =-  error $ "internaliseExp: Unexpected operator section at " ++ locStr (srclocOf e)--internaliseExp _ e@E.OpSectionLeft{} =-  error $ "internaliseExp: Unexpected left operator section at " ++ locStr (srclocOf e)--internaliseExp _ e@E.OpSectionRight{} =-  error $ "internaliseExp: Unexpected right operator section at " ++ locStr (srclocOf e)--internaliseExp _ e@E.ProjectSection{} =-  error $ "internaliseExp: Unexpected projection section at " ++ locStr (srclocOf e)--internaliseExp _ e@E.IndexSection{} =-  error $ "internaliseExp: Unexpected index section at " ++ locStr (srclocOf e)--internaliseArg :: String -> (E.Exp, Maybe VName) -> InternaliseM [SubExp]-internaliseArg desc (arg, argdim) = do-  arg' <- internaliseExp desc arg-  case (arg', argdim) of-    ([se], Just d) -> letBindNames [d] $ BasicOp $ SubExp se-    _ -> return ()-  return arg'--generateCond :: E.Pattern -> [I.SubExp] -> InternaliseM (I.SubExp, [I.SubExp])-generateCond orig_p orig_ses = do-  (cmps, pertinent, _) <- compares orig_p orig_ses-  cmp <- letSubExp "matches" =<< eAll cmps-  return (cmp, pertinent)-  where-    -- Literals are always primitive values.-    compares (E.PatternLit e _ _) (se:ses) = do-      e' <- internaliseExp1 "constant" e-      t' <- elemType <$> subExpType se-      cmp <- letSubExp "match_lit" $ I.BasicOp $ I.CmpOp (I.CmpEq t') e' se-      return ([cmp], [se], ses)--    compares (E.PatternConstr c (Info (E.Scalar (E.Sum fs))) pats _) (se:ses) = do-      (payload_ts, m) <- internaliseSumType $ M.map (map toStruct) fs-      case M.lookup c m of-        Just (i, payload_is) -> do-          let i' = intConst Int8 $ toInteger i-          let (payload_ses, ses') = splitAt (length payload_ts) ses-          cmp <- letSubExp "match_constr" $ I.BasicOp $ I.CmpOp (I.CmpEq int8) i' se-          (cmps, pertinent, _) <- comparesMany pats $ map (payload_ses!!) payload_is-          return (cmp : cmps, pertinent, ses')-        Nothing ->-          error "generateCond: missing constructor"--    compares (E.PatternConstr _ (Info t) _ _) _ =-      error $ "generateCond: PatternConstr has nonsensical type: " ++ pretty t--    compares (E.Id _ t loc) ses =-      compares (E.Wildcard t loc) ses--    compares (E.Wildcard (Info t) _) ses = do-      n <- internalisedTypeSize $ E.toStruct t-      let (id_ses, rest_ses) = splitAt n ses-      return ([], id_ses, rest_ses)--    compares (E.PatternParens pat _) ses =-      compares pat ses--    compares (E.TuplePattern pats _) ses =-      comparesMany pats ses--    compares (E.RecordPattern fs _) ses =-      comparesMany (map snd $ E.sortFields $ M.fromList fs) ses--    compares (E.PatternAscription pat _ _) ses =-      compares pat ses--    compares pat [] =-      error $ "generateCond: No values left for pattern " ++ pretty pat--    comparesMany [] ses = return ([], [], ses)-    comparesMany (pat:pats) ses = do-      (cmps1, pertinent1, ses') <- compares pat ses-      (cmps2, pertinent2, ses'') <- comparesMany pats ses'-      return (cmps1 <> cmps2,-              pertinent1 <> pertinent2,-              ses'')--generateCaseIf :: [I.SubExp] -> Case -> I.Body -> InternaliseM I.Exp-generateCaseIf ses (CasePat p eCase _) bFail = do-  (cond, pertinent) <- generateCond p ses-  eCase' <- internalisePat' p pertinent eCase internaliseBody-  eIf (eSubExp cond) (return eCase') (return bFail)--internalisePat :: String -> E.Pattern -> E.Exp-               -> E.Exp -> (E.Exp -> InternaliseM a)-               -> InternaliseM a-internalisePat desc p e body m = do-  ses <- internaliseExp desc' e-  internalisePat' p ses body m-  where desc' = case S.toList $ E.patternIdents p of-                  [v] -> baseString $ E.identName v-                  _ -> desc--internalisePat' :: E.Pattern -> [I.SubExp]-                -> E.Exp -> (E.Exp -> InternaliseM a)-                -> InternaliseM a-internalisePat' p ses body m = do-  ses_ts <- mapM subExpType ses-  stmPattern p ses_ts $ \pat_names -> do-    forM_ (zip pat_names ses) $ \(v,se) ->-      letBindNames [v] $ I.BasicOp $ I.SubExp se-    m body--internaliseSlice :: SrcLoc-                 -> [SubExp]-                 -> [E.DimIndex]-                 -> InternaliseM ([I.DimIndex SubExp], Certificates)-internaliseSlice loc dims idxs = do- (idxs', oks, parts) <- unzip3 <$> zipWithM internaliseDimIndex dims idxs- ok <- letSubExp "index_ok" =<< eAll oks- let msg = errorMsg $ ["Index ["] ++ intercalate [", "] parts ++-           ["] out of bounds for array of shape ["] ++-           intersperse "][" (map ErrorInt32 $ take (length idxs) dims) ++ ["]."]- c <- assert "index_certs" ok msg loc- return (idxs', c)--internaliseDimIndex :: SubExp -> E.DimIndex-                    -> InternaliseM (I.DimIndex SubExp, SubExp, [ErrorMsgPart SubExp])-internaliseDimIndex w (E.DimFix i) = do-  (i', _) <- internaliseDimExp "i" i-  let lowerBound = I.BasicOp $-                   I.CmpOp (I.CmpSle I.Int32) (I.constant (0 :: I.Int32)) i'-      upperBound = I.BasicOp $-                   I.CmpOp (I.CmpSlt I.Int32) i' w-  ok <- letSubExp "bounds_check" =<< eBinOp I.LogAnd (pure lowerBound) (pure upperBound)-  return (I.DimFix i', ok, [ErrorInt32 i'])---- Special-case an important common case that otherwise leads to horrible code.-internaliseDimIndex w (E.DimSlice Nothing Nothing-                       (Just (E.Negate (E.IntLit 1 _ _) _))) = do-  w_minus_1 <- letSubExp "w_minus_1" $-               BasicOp $ I.BinOp (Sub Int32 I.OverflowWrap) w one-  return (I.DimSlice w_minus_1 w $ intConst Int32 (-1),-          constant True,-          mempty)-  where one = constant (1::Int32)--internaliseDimIndex w (E.DimSlice i j s) = do-  s' <- maybe (return one) (fmap fst . internaliseDimExp "s") s-  s_sign <- letSubExp "s_sign" $ BasicOp $ I.UnOp (I.SSignum Int32) s'-  backwards <- letSubExp "backwards" $ I.BasicOp $ I.CmpOp (I.CmpEq int32) s_sign negone-  w_minus_1 <- letSubExp "w_minus_1" $ BasicOp $ I.BinOp (Sub Int32 I.OverflowWrap) w one-  let i_def = letSubExp "i_def" $ I.If backwards-              (resultBody [w_minus_1])-              (resultBody [zero]) $ ifCommon [I.Prim int32]-      j_def = letSubExp "j_def" $ I.If backwards-              (resultBody [negone])-              (resultBody [w]) $ ifCommon [I.Prim int32]-  i' <- maybe i_def (fmap fst . internaliseDimExp "i") i-  j' <- maybe j_def (fmap fst . internaliseDimExp "j") j-  j_m_i <- letSubExp "j_m_i" $ BasicOp $ I.BinOp (Sub Int32 I.OverflowWrap) j' i'-  -- Something like a division-rounding-up, but accomodating negative-  -- operands.-  let divRounding x y =-        eBinOp (SQuot Int32 Unsafe)-        (eBinOp (Add Int32 I.OverflowWrap) x-         (eBinOp (Sub Int32 I.OverflowWrap) y (eSignum $ toExp s'))) y-  n <- letSubExp "n" =<< divRounding (toExp j_m_i) (toExp s')--  -- Bounds checks depend on whether we are slicing forwards or-  -- backwards.  If forwards, we must check '0 <= i && i <= j'.  If-  -- backwards, '-1 <= j && j <= i'.  In both cases, we check '0 <=-  -- i+n*s && i+(n-1)*s < w'.  We only check if the slice is nonempty.-  empty_slice <- letSubExp "empty_slice" $ I.BasicOp $ I.CmpOp (CmpEq int32) n zero--  m <- letSubExp "m" $ I.BasicOp $ I.BinOp (Sub Int32 I.OverflowWrap) n one-  m_t_s <- letSubExp "m_t_s" $ I.BasicOp $ I.BinOp (Mul Int32 I.OverflowWrap) m s'-  i_p_m_t_s <- letSubExp "i_p_m_t_s" $ I.BasicOp $ I.BinOp (Add Int32 I.OverflowWrap) i' m_t_s-  zero_leq_i_p_m_t_s <- letSubExp "zero_leq_i_p_m_t_s" $-                        I.BasicOp $ I.CmpOp (I.CmpSle Int32) zero i_p_m_t_s-  i_p_m_t_s_leq_w <- letSubExp "i_p_m_t_s_leq_w" $-                     I.BasicOp $ I.CmpOp (I.CmpSle Int32) i_p_m_t_s w-  i_p_m_t_s_lth_w <- letSubExp "i_p_m_t_s_leq_w" $-                     I.BasicOp $ I.CmpOp (I.CmpSlt Int32) i_p_m_t_s w--  zero_lte_i <- letSubExp "zero_lte_i" $ I.BasicOp $ I.CmpOp (I.CmpSle Int32) zero i'-  i_lte_j <- letSubExp "i_lte_j" $ I.BasicOp $ I.CmpOp (I.CmpSle Int32) i' j'-  forwards_ok <- letSubExp "forwards_ok" =<<-                 eAll [zero_lte_i, zero_lte_i, i_lte_j, zero_leq_i_p_m_t_s, i_p_m_t_s_lth_w]--  negone_lte_j <- letSubExp "negone_lte_j" $ I.BasicOp $ I.CmpOp (I.CmpSle Int32) negone j'-  j_lte_i <- letSubExp "j_lte_i" $ I.BasicOp $ I.CmpOp (I.CmpSle Int32) j' i'-  backwards_ok <- letSubExp "backwards_ok" =<<-                  eAll-                  [negone_lte_j, negone_lte_j, j_lte_i, zero_leq_i_p_m_t_s, i_p_m_t_s_leq_w]--  slice_ok <- letSubExp "slice_ok" $ I.If backwards-              (resultBody [backwards_ok])-              (resultBody [forwards_ok]) $-              ifCommon [I.Prim I.Bool]-  ok_or_empty <- letSubExp "ok_or_empty" $-                 I.BasicOp $ I.BinOp I.LogOr empty_slice slice_ok--  let parts = case (i, j, s) of-                (_, _, Just{}) ->-                  [maybe "" (const $ ErrorInt32 i') i, ":",-                   maybe "" (const $ ErrorInt32 j') j, ":",-                   ErrorInt32 s']-                (_, Just{}, _) ->-                  [maybe "" (const $ ErrorInt32 i') i, ":",-                   ErrorInt32 j'] ++-                   maybe mempty (const [":", ErrorInt32 s']) s-                (_, Nothing, Nothing) ->-                  [ErrorInt32 i', ":"]-  return (I.DimSlice i' n s', ok_or_empty, parts)-  where zero = constant (0::Int32)-        negone = constant (-1::Int32)-        one = constant (1::Int32)--internaliseScanOrReduce :: String -> String-                        -> (SubExp -> I.Lambda -> [SubExp] -> [VName] -> InternaliseM (SOAC SOACS))-                        -> (E.Exp, E.Exp, E.Exp, SrcLoc)-                        -> InternaliseM [SubExp]-internaliseScanOrReduce desc what f (lam, ne, arr, loc) = do-  arrs <- internaliseExpToVars (what++"_arr") arr-  nes <- internaliseExp (what++"_ne") ne-  nes' <- forM (zip nes arrs) $ \(ne', arr') -> do-    rowtype <- I.stripArray 1 <$> lookupType arr'-    ensureShape-      "Row shape of input array does not match shape of neutral element"-      loc rowtype (what++"_ne_right_shape") ne'-  nests <- mapM I.subExpType nes'-  arrts <- mapM lookupType arrs-  lam' <- internaliseFoldLambda internaliseLambda lam nests arrts-  w <- arraysSize 0 <$> mapM lookupType arrs-  letTupExp' desc . I.Op =<< f w lam' nes' arrs--internaliseHist :: String-                -> E.Exp -> E.Exp -> E.Exp -> E.Exp -> E.Exp -> E.Exp -> SrcLoc-                -> InternaliseM [SubExp]-internaliseHist desc rf hist op ne buckets img loc = do-  rf' <- internaliseExp1 "hist_rf" rf-  ne' <- internaliseExp "hist_ne" ne-  hist' <- internaliseExpToVars "hist_hist" hist-  buckets' <- letExp "hist_buckets" . BasicOp . SubExp =<<-              internaliseExp1 "hist_buckets" buckets-  img' <- internaliseExpToVars "hist_img" img--  -- reshape neutral element to have same size as the destination array-  ne_shp <- forM (zip ne' hist') $ \(n, h) -> do-    rowtype <- I.stripArray 1 <$> lookupType h-    ensureShape-      "Row shape of destination array does not match shape of neutral element"-      loc rowtype "hist_ne_right_shape" n-  ne_ts <- mapM I.subExpType ne_shp-  his_ts <- mapM lookupType hist'-  op' <- internaliseFoldLambda internaliseLambda op ne_ts his_ts--  -- reshape return type of bucket function to have same size as neutral element-  -- (modulo the index)-  bucket_param <- newParam "bucket_p" $ I.Prim int32-  img_params <- mapM (newParam "img_p" . rowType) =<< mapM lookupType img'-  let params = bucket_param : img_params-      rettype = I.Prim int32 : ne_ts-      body = mkBody mempty $ map (I.Var . paramName) params-  body' <- localScope (scopeOfLParams params) $-           ensureResultShape-           "Row shape of value array does not match row shape of hist target"-           (srclocOf img) rettype body--  -- get sizes of histogram and image arrays-  w_hist <- arraysSize 0 <$> mapM lookupType hist'-  w_img <- arraysSize 0 <$> mapM lookupType img'--  -- Generate an assertion and reshapes to ensure that buckets' and-  -- img' are the same size.-  b_shape <- I.arrayShape <$> lookupType buckets'-  let b_w = shapeSize 0 b_shape-  cmp <- letSubExp "bucket_cmp" $ I.BasicOp $ I.CmpOp (I.CmpEq I.int32) b_w w_img-  c <- assert "bucket_cert" cmp-       "length of index and value array does not match" loc-  buckets'' <- certifying c $ letExp (baseString buckets') $-    I.BasicOp $ I.Reshape (reshapeOuter [DimCoercion w_img] 1 b_shape) buckets'--  letTupExp' desc $ I.Op $-    I.Hist w_img [HistOp w_hist rf' hist' ne_shp op'] (I.Lambda params body' rettype) $ buckets'' : img'--internaliseStreamMap :: String -> StreamOrd -> E.Exp -> E.Exp-                     -> InternaliseM [SubExp]-internaliseStreamMap desc o lam arr = do-  arrs <- internaliseExpToVars "stream_input" arr-  lam' <- internaliseStreamMapLambda internaliseLambda lam $ map I.Var arrs-  w <- arraysSize 0 <$> mapM lookupType arrs-  let form = I.Parallel o Commutative (I.Lambda [] (mkBody mempty []) []) []-  letTupExp' desc $ I.Op $ I.Stream w form lam' arrs--internaliseStreamRed :: String -> StreamOrd -> Commutativity-                     -> E.Exp -> E.Exp -> E.Exp-                     -> InternaliseM [SubExp]-internaliseStreamRed desc o comm lam0 lam arr = do-  arrs <- internaliseExpToVars "stream_input" arr-  rowts <- mapM (fmap I.rowType . lookupType) arrs-  (lam_params, lam_body) <--    internaliseStreamLambda internaliseLambda lam rowts-  let (chunk_param, _, lam_val_params) =-        partitionChunkedFoldParameters 0 lam_params--  -- Synthesize neutral elements by applying the fold function-  -- to an empty chunk.-  letBindNames [I.paramName chunk_param] $-    I.BasicOp $ I.SubExp $ constant (0::Int32)-  forM_ lam_val_params $ \p ->-    letBindNames [I.paramName p] $-    I.BasicOp $ I.Scratch (I.elemType $ I.paramType p) $-    I.arrayDims $ I.paramType p-  nes <- bodyBind =<< renameBody lam_body--  nes_ts <- mapM I.subExpType nes-  outsz  <- arraysSize 0 <$> mapM lookupType arrs-  let acc_arr_tps = [ I.arrayOf t (I.Shape [outsz]) NoUniqueness | t <- nes_ts ]-  lam0' <- internaliseFoldLambda internaliseLambda lam0 nes_ts acc_arr_tps--  let lam0_acc_params = take (length nes) $ I.lambdaParams lam0'-  lam_acc_params <- forM lam0_acc_params $ \p -> do-    name <- newVName $ baseString $ I.paramName p-    return p { I.paramName = name }--  -- Make sure the chunk size parameter comes first.-  let lam_params' = chunk_param : lam_acc_params ++ lam_val_params--  body_with_lam0 <--    ensureResultShape "shape of result does not match shape of initial value"-    (srclocOf lam0) nes_ts <=< insertStmsM $ localScope (scopeOfLParams lam_params') $ do-      lam_res <- bodyBind lam_body-      lam_res' <- ensureArgShapes-                  "shape of chunk function result does not match shape of initial value"-                  (srclocOf lam) [] (map I.typeOf $ I.lambdaParams lam0') lam_res-      new_lam_res <- eLambda lam0' $ map eSubExp $-                     map (I.Var . paramName) lam_acc_params ++ lam_res'-      return $ resultBody new_lam_res--  let form = I.Parallel o comm lam0' nes-      lam' = I.Lambda { lambdaParams = lam_params'-                      , lambdaBody = body_with_lam0-                      , lambdaReturnType = nes_ts }-  w <- arraysSize 0 <$> mapM lookupType arrs-  letTupExp' desc $ I.Op $ I.Stream w form lam' arrs--internaliseExp1 :: String -> E.Exp -> InternaliseM I.SubExp-internaliseExp1 desc e = do-  vs <- internaliseExp desc e-  case vs of [se] -> return se-             _ -> error "Internalise.internaliseExp1: was passed not just a single subexpression"---- | Promote to dimension type as appropriate for the original type.--- Also return original type.-internaliseDimExp :: String -> E.Exp -> InternaliseM (I.SubExp, IntType)-internaliseDimExp s e = do-  e' <- internaliseExp1 s e-  case E.typeOf e of-    E.Scalar (E.Prim (Signed it))   -> (,it) <$> asIntS Int32 e'-    E.Scalar (E.Prim (Unsigned it)) -> (,it) <$> asIntZ Int32 e'-    _                               -> error "internaliseDimExp: bad type"--internaliseExpToVars :: String -> E.Exp -> InternaliseM [I.VName]-internaliseExpToVars desc e =-  mapM asIdent =<< internaliseExp desc e-  where asIdent (I.Var v) = return v-        asIdent se        = letExp desc $ I.BasicOp $ I.SubExp se--internaliseOperation :: String-                     -> E.Exp-                     -> (I.VName -> InternaliseM I.BasicOp)-                     -> InternaliseM [I.SubExp]-internaliseOperation s e op = do-  vs <- internaliseExpToVars s e-  letSubExps s =<< mapM (fmap I.BasicOp . op) vs--certifyingNonzero :: SrcLoc -> IntType -> SubExp-                  -> InternaliseM a-                  -> InternaliseM a-certifyingNonzero loc t x m = do-  zero <- letSubExp "zero" $ I.BasicOp $-          CmpOp (CmpEq (IntType t)) x (intConst t 0)-  nonzero <- letSubExp "nonzero" $ I.BasicOp $ UnOp Not zero-  c <- assert "nonzero_cert" nonzero "division by zero" loc-  certifying c m--certifyingNonnegative :: SrcLoc -> IntType -> SubExp-                      -> InternaliseM a-                      -> InternaliseM a-certifyingNonnegative loc t x m = do-  nonnegative <- letSubExp "nonnegative" $ I.BasicOp $-                 CmpOp (CmpSle t) (intConst t 0) x-  c <- assert "nonzero_cert" nonnegative "negative exponent" loc-  certifying c m--internaliseBinOp :: SrcLoc -> String-                 -> E.BinOp-                 -> I.SubExp -> I.SubExp-                 -> E.PrimType-                 -> E.PrimType-                 -> InternaliseM [I.SubExp]-internaliseBinOp _ desc E.Plus x y (E.Signed t) _ =-  simpleBinOp desc (I.Add t I.OverflowWrap) x y-internaliseBinOp _ desc E.Plus x y (E.Unsigned t) _ =-  simpleBinOp desc (I.Add t I.OverflowWrap) x y-internaliseBinOp _ desc E.Plus x y (E.FloatType t) _ =-  simpleBinOp desc (I.FAdd t) x y-internaliseBinOp _ desc E.Minus x y (E.Signed t) _ =-  simpleBinOp desc (I.Sub t I.OverflowWrap) x y-internaliseBinOp _ desc E.Minus x y (E.Unsigned t) _ =-  simpleBinOp desc (I.Sub t I.OverflowWrap) x y-internaliseBinOp _ desc E.Minus x y (E.FloatType t) _ =-  simpleBinOp desc (I.FSub t) x y-internaliseBinOp _ desc E.Times x y (E.Signed t) _ =-  simpleBinOp desc (I.Mul t I.OverflowWrap) x y-internaliseBinOp _ desc E.Times x y (E.Unsigned t) _ =-  simpleBinOp desc (I.Mul t I.OverflowWrap) x y-internaliseBinOp _ desc E.Times x y (E.FloatType t) _ =-  simpleBinOp desc (I.FMul t) x y-internaliseBinOp loc desc E.Divide x y (E.Signed t) _ =-  certifyingNonzero loc t y $-  simpleBinOp desc (I.SDiv t I.Unsafe) x y-internaliseBinOp loc desc E.Divide x y (E.Unsigned t) _ =-  certifyingNonzero loc t y $-  simpleBinOp desc (I.UDiv t I.Unsafe) x y-internaliseBinOp _ desc E.Divide x y (E.FloatType t) _ =-  simpleBinOp desc (I.FDiv t) x y-internaliseBinOp _ desc E.Pow x y (E.FloatType t) _ =-  simpleBinOp desc (I.FPow t) x y-internaliseBinOp loc desc E.Pow x y (E.Signed t) _ =-  certifyingNonnegative loc t y $-  simpleBinOp desc (I.Pow t) x y-internaliseBinOp _ desc E.Pow x y (E.Unsigned t) _ =-  simpleBinOp desc (I.Pow t) x y-internaliseBinOp loc desc E.Mod x y (E.Signed t) _ =-  certifyingNonzero loc t y $-  simpleBinOp desc (I.SMod t I.Unsafe) x y-internaliseBinOp loc desc E.Mod x y (E.Unsigned t) _ =-  certifyingNonzero loc t y $-  simpleBinOp desc (I.UMod t I.Unsafe) x y-internaliseBinOp _ desc E.Mod x y (E.FloatType t) _ =-  simpleBinOp desc (I.FMod t) x y-internaliseBinOp loc desc E.Quot x y (E.Signed t) _ =-  certifyingNonzero loc t y $-  simpleBinOp desc (I.SQuot t I.Unsafe) x y-internaliseBinOp loc desc E.Quot x y (E.Unsigned t) _ =-  certifyingNonzero loc t y $-  simpleBinOp desc (I.UDiv t I.Unsafe) x y-internaliseBinOp loc desc E.Rem x y (E.Signed t) _ =-  certifyingNonzero loc t y $-  simpleBinOp desc (I.SRem t I.Unsafe) x y-internaliseBinOp loc desc E.Rem x y (E.Unsigned t) _ =-  certifyingNonzero loc t y $-  simpleBinOp desc (I.UMod t I.Unsafe) x y-internaliseBinOp _ desc E.ShiftR x y (E.Signed t) _ =-  simpleBinOp desc (I.AShr t) x y-internaliseBinOp _ desc E.ShiftR x y (E.Unsigned t) _ =-  simpleBinOp desc (I.LShr t) x y-internaliseBinOp _ desc E.ShiftL x y (E.Signed t) _ =-  simpleBinOp desc (I.Shl t) x y-internaliseBinOp _ desc E.ShiftL x y (E.Unsigned t) _ =-  simpleBinOp desc (I.Shl t) x y-internaliseBinOp _ desc E.Band x y (E.Signed t) _ =-  simpleBinOp desc (I.And t) x y-internaliseBinOp _ desc E.Band x y (E.Unsigned t) _ =-  simpleBinOp desc (I.And t) x y-internaliseBinOp _ desc E.Xor x y (E.Signed t) _ =-  simpleBinOp desc (I.Xor t) x y-internaliseBinOp _ desc E.Xor x y (E.Unsigned t) _ =-  simpleBinOp desc (I.Xor t) x y-internaliseBinOp _ desc E.Bor x y (E.Signed t) _ =-  simpleBinOp desc (I.Or t) x y-internaliseBinOp _ desc E.Bor x y (E.Unsigned t) _ =-  simpleBinOp desc (I.Or t) x y--internaliseBinOp _ desc E.Equal x y t _ =-  simpleCmpOp desc (I.CmpEq $ internalisePrimType t) x y-internaliseBinOp _ desc E.NotEqual x y t _ = do-  eq <- letSubExp (desc++"true") $ I.BasicOp $ I.CmpOp (I.CmpEq $ internalisePrimType t) x y-  fmap pure $ letSubExp desc $ I.BasicOp $ I.UnOp I.Not eq-internaliseBinOp _ desc E.Less x y (E.Signed t) _ =-  simpleCmpOp desc (I.CmpSlt t) x y-internaliseBinOp _ desc E.Less x y (E.Unsigned t) _ =-  simpleCmpOp desc (I.CmpUlt t) x y-internaliseBinOp _ desc E.Leq x y (E.Signed t) _ =-  simpleCmpOp desc (I.CmpSle t) x y-internaliseBinOp _ desc E.Leq x y (E.Unsigned t) _ =-  simpleCmpOp desc (I.CmpUle t) x y-internaliseBinOp _ desc E.Greater x y (E.Signed t) _ =-  simpleCmpOp desc (I.CmpSlt t) y x -- Note the swapped x and y-internaliseBinOp _ desc E.Greater x y (E.Unsigned t) _ =-  simpleCmpOp desc (I.CmpUlt t) y x -- Note the swapped x and y-internaliseBinOp _ desc E.Geq x y (E.Signed t) _ =-  simpleCmpOp desc (I.CmpSle t) y x -- Note the swapped x and y-internaliseBinOp _ desc E.Geq x y (E.Unsigned t) _ =-  simpleCmpOp desc (I.CmpUle t) y x -- Note the swapped x and y-internaliseBinOp _ desc E.Less x y (E.FloatType t) _ =-  simpleCmpOp desc (I.FCmpLt t) x y-internaliseBinOp _ desc E.Leq x y (E.FloatType t) _ =-  simpleCmpOp desc (I.FCmpLe t) x y-internaliseBinOp _ desc E.Greater x y (E.FloatType t) _ =-  simpleCmpOp desc (I.FCmpLt t) y x -- Note the swapped x and y-internaliseBinOp _ desc E.Geq x y (E.FloatType t) _ =-  simpleCmpOp desc (I.FCmpLe t) y x -- Note the swapped x and y---- Relational operators for booleans.-internaliseBinOp _ desc E.Less x y E.Bool _ =-  simpleCmpOp desc I.CmpLlt x y-internaliseBinOp _ desc E.Leq x y E.Bool _ =-  simpleCmpOp desc I.CmpLle x y-internaliseBinOp _ desc E.Greater x y E.Bool _ =-  simpleCmpOp desc I.CmpLlt y x -- Note the swapped x and y-internaliseBinOp _ desc E.Geq x y E.Bool _ =-  simpleCmpOp desc I.CmpLle y x -- Note the swapped x and y--internaliseBinOp _ _ op _ _ t1 t2 =-  error $ "Invalid binary operator " ++ pretty op ++-  " with operand types " ++ pretty t1 ++ ", " ++ pretty t2--simpleBinOp :: String-            -> I.BinOp-            -> I.SubExp -> I.SubExp-            -> InternaliseM [I.SubExp]-simpleBinOp desc bop x y =-  letTupExp' desc $ I.BasicOp $ I.BinOp bop x y--simpleCmpOp :: String-            -> I.CmpOp-            -> I.SubExp -> I.SubExp-            -> InternaliseM [I.SubExp]-simpleCmpOp desc op x y =-  letTupExp' desc $ I.BasicOp $ I.CmpOp op x y--findFuncall :: E.Exp -> InternaliseM (E.QualName VName,-                                      [(E.Exp, Maybe VName)],-                                      E.StructType,-                                      [VName])-findFuncall (E.Var fname (Info t) _) =-  return (fname, [], E.toStruct t, [])-findFuncall (E.Apply f arg (Info (_, argext)) (Info ret, Info retext) _) = do-  (fname, args, _, _) <- findFuncall f-  return (fname, args ++ [(arg, argext)], E.toStruct ret, retext)-findFuncall e =-  error $ "Invalid function expression in application: " ++ pretty e--internaliseLambda :: InternaliseLambda--internaliseLambda (E.Parens e _) rowtypes =-  internaliseLambda e rowtypes--internaliseLambda (E.Lambda params body _ (Info (_, rettype)) _) rowtypes =-  bindingLambdaParams params rowtypes $ \params' -> do-    body' <- internaliseBody body-    rettype' <- internaliseLambdaReturnType rettype-    return (params', body', rettype')--internaliseLambda e _ = error $ "internaliseLambda: unexpected expression:\n" ++ pretty e---- | Some operators and functions are overloaded or otherwise special--- - we detect and treat them here.-isOverloadedFunction :: E.QualName VName -> [E.Exp] -> SrcLoc-                     -> Maybe (String -> InternaliseM [SubExp])-isOverloadedFunction qname args loc = do-  guard $ baseTag (qualLeaf qname) <= maxIntrinsicTag-  let handlers = [handleSign,-                  handleIntrinsicOps,-                  handleOps,-                  handleSOACs,-                  handleRest]-  msum [h args $ baseString $ qualLeaf qname | h <- handlers]-  where-    handleSign [x] "sign_i8"  = Just $ toSigned I.Int8 x-    handleSign [x] "sign_i16" = Just $ toSigned I.Int16 x-    handleSign [x] "sign_i32" = Just $ toSigned I.Int32 x-    handleSign [x] "sign_i64" = Just $ toSigned I.Int64 x--    handleSign [x] "unsign_i8"  = Just $ toUnsigned I.Int8 x-    handleSign [x] "unsign_i16" = Just $ toUnsigned I.Int16 x-    handleSign [x] "unsign_i32" = Just $ toUnsigned I.Int32 x-    handleSign [x] "unsign_i64" = Just $ toUnsigned I.Int64 x--    handleSign _ _ = Nothing--    handleIntrinsicOps [x] s-      | Just unop <- find ((==s) . pretty) allUnOps = Just $ \desc -> do-          x' <- internaliseExp1 "x" x-          fmap pure $ letSubExp desc $ I.BasicOp $ I.UnOp unop x'-    handleIntrinsicOps [TupLit [x,y] _] s-      | Just bop <- find ((==s) . pretty) allBinOps = Just $ \desc -> do-          x' <- internaliseExp1 "x" x-          y' <- internaliseExp1 "y" y-          fmap pure $ letSubExp desc $ I.BasicOp $ I.BinOp bop x' y'-      | Just cmp <- find ((==s) . pretty) allCmpOps = Just $ \desc -> do-          x' <- internaliseExp1 "x" x-          y' <- internaliseExp1 "y" y-          fmap pure $ letSubExp desc $ I.BasicOp $ I.CmpOp cmp x' y'-    handleIntrinsicOps [x] s-      | Just conv <- find ((==s) . pretty) allConvOps = Just $ \desc -> do-          x' <- internaliseExp1 "x" x-          fmap pure $ letSubExp desc $ I.BasicOp $ I.ConvOp conv x'-    handleIntrinsicOps _ _ = Nothing--    -- Short-circuiting operators are magical.-    handleOps [x,y] "&&" = Just $ \desc ->-      internaliseExp desc $-      E.If x y (E.Literal (E.BoolValue False) mempty) (Info $ E.Scalar $ E.Prim E.Bool, Info []) mempty-    handleOps [x,y] "||" = Just $ \desc ->-        internaliseExp desc $-        E.If x (E.Literal (E.BoolValue True) mempty) y (Info $ E.Scalar $ E.Prim E.Bool, Info []) mempty--    -- Handle equality and inequality specially, to treat the case of-    -- arrays.-    handleOps [xe,ye] op-      | Just cmp_f <- isEqlOp op = Just $ \desc -> do-          xe' <- internaliseExp "x" xe-          ye' <- internaliseExp "y" ye-          rs <- zipWithM (doComparison desc) xe' ye'-          cmp_f desc =<< letSubExp "eq" =<< eAll rs-        where isEqlOp "!=" = Just $ \desc eq ->-                letTupExp' desc $ I.BasicOp $ I.UnOp I.Not eq-              isEqlOp "==" = Just $ \_ eq ->-                return [eq]-              isEqlOp _ = Nothing--              doComparison desc x y = do-                x_t <- I.subExpType x-                y_t <- I.subExpType y-                case x_t of-                  I.Prim t -> letSubExp desc $ I.BasicOp $ I.CmpOp (I.CmpEq t) x y-                  _ -> do-                    let x_dims = I.arrayDims x_t-                        y_dims = I.arrayDims y_t-                    dims_match <- forM (zip x_dims y_dims) $ \(x_dim, y_dim) ->-                      letSubExp "dim_eq" $ I.BasicOp $ I.CmpOp (I.CmpEq int32) x_dim y_dim-                    shapes_match <- letSubExp "shapes_match" =<< eAll dims_match-                    compare_elems_body <- runBodyBinder $ do-                      -- Flatten both x and y.-                      x_num_elems <- letSubExp "x_num_elems" =<<-                                     foldBinOp (I.Mul Int32 I.OverflowUndef) (constant (1::Int32)) x_dims-                      x' <- letExp "x" $ I.BasicOp $ I.SubExp x-                      y' <- letExp "x" $ I.BasicOp $ I.SubExp y-                      x_flat <- letExp "x_flat" $ I.BasicOp $ I.Reshape [I.DimNew x_num_elems] x'-                      y_flat <- letExp "y_flat" $ I.BasicOp $ I.Reshape [I.DimNew x_num_elems] y'--                      -- Compare the elements.-                      cmp_lam <- cmpOpLambda $ I.CmpEq (elemType x_t)-                      cmps <- letExp "cmps" $ I.Op $-                              I.Screma x_num_elems (I.mapSOAC cmp_lam) [x_flat, y_flat]--                      -- Check that all were equal.-                      and_lam <- binOpLambda I.LogAnd I.Bool-                      reduce <- I.reduceSOAC [Reduce Commutative and_lam [constant True]]-                      all_equal <- letSubExp "all_equal" $ I.Op $ I.Screma x_num_elems reduce [cmps]-                      return $ resultBody [all_equal]--                    letSubExp "arrays_equal" $-                      I.If shapes_match compare_elems_body (resultBody [constant False]) $-                      ifCommon [I.Prim I.Bool]--    handleOps [x,y] name-      | Just bop <- find ((name==) . pretty) [minBound..maxBound::E.BinOp] =-      Just $ \desc -> do-        x' <- internaliseExp1 "x" x-        y' <- internaliseExp1 "y" y-        case (E.typeOf x, E.typeOf y) of-          (E.Scalar (E.Prim t1), E.Scalar (E.Prim t2)) ->-            internaliseBinOp loc desc bop x' y' t1 t2-          _ -> error "Futhark.Internalise.internaliseExp: non-primitive type in BinOp."--    handleOps _ _ = Nothing--    handleSOACs [TupLit [lam, arr] _] "map" = Just $ \desc -> do-      arr' <- internaliseExpToVars "map_arr" arr-      lam' <- internaliseMapLambda internaliseLambda lam $ map I.Var arr'-      w <- arraysSize 0 <$> mapM lookupType arr'-      letTupExp' desc $ I.Op $-        I.Screma w (I.mapSOAC lam') arr'--    handleSOACs [TupLit [k, lam, arr] _] "partition" = do-      k' <- fromIntegral <$> isInt32 k-      Just $ \_desc -> do-        arrs <- internaliseExpToVars "partition_input" arr-        lam' <- internalisePartitionLambda internaliseLambda k' lam $ map I.Var arrs-        uncurry (++) <$> partitionWithSOACS k' lam' arrs-        where isInt32 (Literal (SignedValue (Int32Value k')) _) = Just k'-              isInt32 (IntLit k' (Info (E.Scalar (E.Prim (Signed Int32)))) _) = Just $ fromInteger k'-              isInt32 _ = Nothing--    handleSOACs [TupLit [lam, ne, arr] _] "reduce" = Just $ \desc ->-      internaliseScanOrReduce desc "reduce" reduce (lam, ne, arr, loc)-      where reduce w red_lam nes arrs =-              I.Screma w <$>-              I.reduceSOAC [Reduce Noncommutative red_lam nes] <*> pure arrs--    handleSOACs [TupLit [lam, ne, arr] _] "reduce_comm" = Just $ \desc ->-      internaliseScanOrReduce desc "reduce" reduce (lam, ne, arr, loc)-      where reduce w red_lam nes arrs =-              I.Screma w <$>-              I.reduceSOAC [Reduce Commutative red_lam nes] <*> pure arrs--    handleSOACs [TupLit [lam, ne, arr] _] "scan" = Just $ \desc ->-      internaliseScanOrReduce desc "scan" reduce (lam, ne, arr, loc)-      where reduce w scan_lam nes arrs =-              I.Screma w <$> I.scanSOAC [Scan scan_lam nes] <*> pure arrs--    handleSOACs [TupLit [op, f, arr] _] "reduce_stream" = Just $ \desc ->-      internaliseStreamRed desc InOrder Noncommutative op f arr--    handleSOACs [TupLit [op, f, arr] _] "reduce_stream_per" = Just $ \desc ->-      internaliseStreamRed desc Disorder Commutative op f arr--    handleSOACs [TupLit [f, arr] _] "map_stream" = Just $ \desc ->-      internaliseStreamMap desc InOrder f arr--    handleSOACs [TupLit [f, arr] _] "map_stream_per" = Just $ \desc ->-      internaliseStreamMap desc Disorder f arr--    handleSOACs [TupLit [rf, dest, op, ne, buckets, img] _] "hist" = Just $ \desc ->-      internaliseHist desc rf dest op ne buckets img loc--    handleSOACs _ _ = Nothing--    handleRest [x] "!" = Just $ complementF x--    handleRest [x] "opaque" = Just $ \desc ->-      mapM (letSubExp desc . BasicOp . Opaque) =<< internaliseExp "opaque_arg" x--    handleRest [E.TupLit [a, si, v] _] "scatter" = Just $ scatterF a si v--    handleRest [E.TupLit [n, m, arr] _] "unflatten" = Just $ \desc -> do-      arrs <- internaliseExpToVars "unflatten_arr" arr-      n' <- internaliseExp1 "n" n-      m' <- internaliseExp1 "m" m-      -- The unflattened dimension needs to have the same number of elements-      -- as the original dimension.-      old_dim <- I.arraysSize 0 <$> mapM lookupType arrs-      dim_ok <- letSubExp "dim_ok" =<<-                eCmpOp (I.CmpEq I.int32)-                (eBinOp (I.Mul Int32 I.OverflowUndef) (eSubExp n') (eSubExp m'))-                (eSubExp old_dim)-      dim_ok_cert <- assert "dim_ok_cert" dim_ok-                     "new shape has different number of elements than old shape" loc-      certifying dim_ok_cert $ forM arrs $ \arr' -> do-        arr_t <- lookupType arr'-        letSubExp desc $ I.BasicOp $-          I.Reshape (reshapeOuter [DimNew n', DimNew m'] 1 $ I.arrayShape arr_t) arr'--    handleRest [arr] "flatten" = Just $ \desc -> do-      arrs <- internaliseExpToVars "flatten_arr" arr-      forM arrs $ \arr' -> do-        arr_t <- lookupType arr'-        let n = arraySize 0 arr_t-            m = arraySize 1 arr_t-        k <- letSubExp "flat_dim" $ I.BasicOp $ I.BinOp (Mul Int32 I.OverflowUndef) n m-        letSubExp desc $ I.BasicOp $-          I.Reshape (reshapeOuter [DimNew k] 2 $ I.arrayShape arr_t) arr'--    handleRest [TupLit [x, y] _] "concat" = Just $ \desc -> do-      xs <- internaliseExpToVars "concat_x" x-      ys <- internaliseExpToVars "concat_y" y-      outer_size <- arraysSize 0 <$> mapM lookupType xs-      let sumdims xsize ysize = letSubExp "conc_tmp" $ I.BasicOp $-                                I.BinOp (I.Add I.Int32 I.OverflowUndef) xsize ysize-      ressize <- foldM sumdims outer_size =<<-                 mapM (fmap (arraysSize 0) . mapM lookupType) [ys]--      let conc xarr yarr =-            I.BasicOp $ I.Concat 0 xarr [yarr] ressize-      letSubExps desc $ zipWith conc xs ys--    handleRest [TupLit [offset, e] _] "rotate" = Just $ \desc -> do-      offset' <- internaliseExp1 "rotation_offset" offset-      internaliseOperation desc e $ \v -> do-        r <- I.arrayRank <$> lookupType v-        let zero = intConst Int32 0-            offsets = offset' : replicate (r-1) zero-        return $ I.Rotate offsets v--    handleRest [e] "transpose" = Just $ \desc ->-      internaliseOperation desc e $ \v -> do-        r <- I.arrayRank <$> lookupType v-        return $ I.Rearrange ([1,0] ++ [2..r-1]) v--    handleRest [TupLit [x, y] _] "zip" = Just $ \desc ->-      (++) <$> internaliseExp (desc ++ "_zip_x") x-           <*> internaliseExp (desc ++ "_zip_y") y--    handleRest [x] "unzip" = Just $ flip internaliseExp x--    handleRest [x] "trace" = Just $ flip internaliseExp x-    handleRest [x] "break" = Just $ flip internaliseExp x--    handleRest _ _ = Nothing--    toSigned int_to e desc = do-      e' <- internaliseExp1 "trunc_arg" e-      case E.typeOf e of-        E.Scalar (E.Prim E.Bool) ->-          letTupExp' desc $ I.If e' (resultBody [intConst int_to 1])-                                    (resultBody [intConst int_to 0]) $-                                    ifCommon [I.Prim $ I.IntType int_to]-        E.Scalar (E.Prim (E.Signed int_from)) ->-          letTupExp' desc $ I.BasicOp $ I.ConvOp (I.SExt int_from int_to) e'-        E.Scalar (E.Prim (E.Unsigned int_from)) ->-          letTupExp' desc $ I.BasicOp $ I.ConvOp (I.ZExt int_from int_to) e'-        E.Scalar (E.Prim (E.FloatType float_from)) ->-          letTupExp' desc $ I.BasicOp $ I.ConvOp (I.FPToSI float_from int_to) e'-        _ -> error "Futhark.Internalise: non-numeric type in ToSigned"--    toUnsigned int_to e desc = do-      e' <- internaliseExp1 "trunc_arg" e-      case E.typeOf e of-        E.Scalar (E.Prim E.Bool) ->-          letTupExp' desc $ I.If e' (resultBody [intConst int_to 1])-                                    (resultBody [intConst int_to 0]) $-                                    ifCommon [I.Prim $ I.IntType int_to]-        E.Scalar (E.Prim (E.Signed int_from)) ->-          letTupExp' desc $ I.BasicOp $ I.ConvOp (I.ZExt int_from int_to) e'-        E.Scalar (E.Prim (E.Unsigned int_from)) ->-          letTupExp' desc $ I.BasicOp $ I.ConvOp (I.ZExt int_from int_to) e'-        E.Scalar (E.Prim (E.FloatType float_from)) ->-          letTupExp' desc $ I.BasicOp $ I.ConvOp (I.FPToUI float_from int_to) e'-        _ -> error "Futhark.Internalise.internaliseExp: non-numeric type in ToUnsigned"--    complementF e desc = do-      e' <- internaliseExp1 "complement_arg" e-      et <- subExpType e'-      case et of I.Prim (I.IntType t) ->-                   letTupExp' desc $ I.BasicOp $ I.UnOp (I.Complement t) e'-                 I.Prim I.Bool ->-                   letTupExp' desc $ I.BasicOp $ I.UnOp I.Not e'-                 _ ->-                   error "Futhark.Internalise.internaliseExp: non-int/bool type in Complement"--    scatterF a si v desc = do-      si' <- letExp "write_si" . BasicOp . SubExp =<< internaliseExp1 "write_arg_i" si-      svs <- internaliseExpToVars "write_arg_v" v-      sas <- internaliseExpToVars "write_arg_a" a--      si_shape <- I.arrayShape <$> lookupType si'-      let si_w = shapeSize 0 si_shape-      sv_ts <- mapM lookupType svs--      svs' <- forM (zip svs sv_ts) $ \(sv,sv_t) -> do-        let sv_shape = I.arrayShape sv_t-            sv_w = arraySize 0 sv_t--        -- Generate an assertion and reshapes to ensure that sv and si' are the same-        -- size.-        cmp <- letSubExp "write_cmp" $ I.BasicOp $-          I.CmpOp (I.CmpEq I.int32) si_w sv_w-        c <- assert "write_cert" cmp-             "length of index and value array does not match" loc-        certifying c $ letExp (baseString sv ++ "_write_sv") $-          I.BasicOp $ I.Reshape (reshapeOuter [DimCoercion si_w] 1 sv_shape) sv--      indexType <- rowType <$> lookupType si'-      indexName <- newVName "write_index"-      valueNames <- replicateM (length sv_ts) $ newVName "write_value"--      sa_ts <- mapM lookupType sas-      let bodyTypes = replicate (length sv_ts) indexType ++ map rowType sa_ts-          paramTypes = indexType : map rowType sv_ts-          bodyNames = indexName : valueNames-          bodyParams = zipWith I.Param bodyNames paramTypes--      -- This body is pretty boring right now, as every input is exactly the output.-      -- But it can get funky later on if fused with something else.-      body <- localScope (scopeOfLParams bodyParams) $ insertStmsM $ do-        let outs = replicate (length valueNames) indexName ++ valueNames-        results <- forM outs $ \name ->-          letSubExp "write_res" $ I.BasicOp $ I.SubExp $ I.Var name-        ensureResultShape "scatter value has wrong size" loc-          bodyTypes $ resultBody results--      let lam = I.Lambda { I.lambdaParams = bodyParams-                         , I.lambdaReturnType = bodyTypes-                         , I.lambdaBody = body-                         }-          sivs = si' : svs'--      let sa_ws = map (arraySize 0) sa_ts-      letTupExp' desc $ I.Op $ I.Scatter si_w lam sivs $ zip3 sa_ws (repeat 1) sas--funcall :: String -> QualName VName -> [SubExp] -> SrcLoc-        -> InternaliseM ([SubExp], [I.ExtType])-funcall desc (QualName _ fname) args loc = do-  (fname', closure, shapes, value_paramts, fun_params, rettype_fun) <--    lookupFunction fname-  argts <- mapM subExpType args--  shapeargs <- argShapes shapes fun_params argts-  let diets = replicate (length closure + length shapeargs) I.ObservePrim ++-              map I.diet value_paramts-  args' <- ensureArgShapes "function arguments of wrong shape"-           loc (map I.paramName fun_params)-           (map I.paramType fun_params) (map I.Var closure ++ shapeargs ++ args)-  argts' <- mapM subExpType args'-  case rettype_fun $ zip args' argts' of-    Nothing -> error $ "Cannot apply " ++ pretty fname ++ " to arguments\n " ++-               pretty args' ++ "\nof types\n " ++-               pretty argts' ++-               "\nFunction has parameters\n " ++ pretty fun_params-    Just ts -> do-      safety <- askSafety-      attrs <- asks envAttrs-      ses <- attributing attrs $ letTupExp' desc $-             I.Apply fname' (zip args' diets) ts (safety, loc, mempty)-      return (ses, map I.fromDecl ts)---- Bind existential names defined by an expression, based on the--- concrete values that expression evaluated to.  This most--- importantly should be done after function calls, but also--- everything else that can produce existentials in the source--- language.-bindExtSizes :: E.StructType -> [VName] -> [SubExp] -> InternaliseM ()-bindExtSizes ret retext ses = do-  ts <- internaliseType ret-  ses_ts <- mapM subExpType ses--  let combine t1 t2 =-        mconcat $ zipWith combine' (arrayExtDims t1) (arrayDims t2)-      combine' (I.Free (I.Var v)) se-        | v `elem` retext = M.singleton v se-      combine' _ _ = mempty--  forM_ (M.toList $ mconcat $ zipWith combine ts ses_ts) $ \(v, se) ->-    letBindNames [v] $ BasicOp $ SubExp se--askSafety :: InternaliseM Safety-askSafety = do check <- asks envDoBoundsChecks-               return $ if check then I.Safe else I.Unsafe---- Implement partitioning using maps, scans and writes.-partitionWithSOACS :: Int -> I.Lambda -> [I.VName] -> InternaliseM ([I.SubExp], [I.SubExp])-partitionWithSOACS k lam arrs = do-  arr_ts <- mapM lookupType arrs-  let w = arraysSize 0 arr_ts-  classes_and_increments <- letTupExp "increments" $ I.Op $ I.Screma w (mapSOAC lam) arrs-  (classes, increments) <- case classes_and_increments of-                             classes : increments -> return (classes, take k increments)-                             _                    -> error "partitionWithSOACS"--  add_lam_x_params <--    replicateM k $ I.Param <$> newVName "x" <*> pure (I.Prim int32)-  add_lam_y_params <--    replicateM k $ I.Param <$> newVName "y" <*> pure (I.Prim int32)-  add_lam_body <- runBodyBinder $-                  localScope (scopeOfLParams $ add_lam_x_params++add_lam_y_params) $-    fmap resultBody $ forM (zip add_lam_x_params add_lam_y_params) $ \(x,y) ->-      letSubExp "z" $ I.BasicOp $ I.BinOp (I.Add Int32 I.OverflowUndef)-      (I.Var $ I.paramName x) (I.Var $ I.paramName y)-  let add_lam = I.Lambda { I.lambdaBody = add_lam_body-                         , I.lambdaParams = add_lam_x_params ++ add_lam_y_params-                         , I.lambdaReturnType = replicate k $ I.Prim int32-                         }-      nes = replicate (length increments) $ constant (0::Int32)--  scan <- I.scanSOAC [I.Scan add_lam nes]-  all_offsets <- letTupExp "offsets" $ I.Op $ I.Screma w scan increments--  -- We have the offsets for each of the partitions, but we also need-  -- the total sizes, which are the last elements in the offests.  We-  -- just have to be careful in case the array is empty.-  last_index <- letSubExp "last_index" $ I.BasicOp $ I.BinOp (I.Sub Int32 OverflowUndef) w $ constant (1::Int32)-  nonempty_body <- runBodyBinder $ fmap resultBody $ forM all_offsets $ \offset_array ->-    letSubExp "last_offset" $ I.BasicOp $ I.Index offset_array [I.DimFix last_index]-  let empty_body = resultBody $ replicate k $ constant (0::Int32)-  is_empty <- letSubExp "is_empty" $ I.BasicOp $ I.CmpOp (CmpEq int32) w $ constant (0::Int32)-  sizes <- letTupExp "partition_size" $-           I.If is_empty empty_body nonempty_body $-           ifCommon $ replicate k $ I.Prim int32--  -- The total size of all partitions must necessarily be equal to the-  -- size of the input array.--  -- Create scratch arrays for the result.-  blanks <- forM arr_ts $ \arr_t ->-    letExp "partition_dest" $ I.BasicOp $-    Scratch (elemType arr_t) (w : drop 1 (I.arrayDims arr_t))--  -- Now write into the result.-  write_lam <- do-    c_param <- I.Param <$> newVName "c" <*> pure (I.Prim int32)-    offset_params <- replicateM k $ I.Param <$> newVName "offset" <*> pure (I.Prim int32)-    value_params <- forM arr_ts $ \arr_t ->-      I.Param <$> newVName "v" <*> pure (I.rowType arr_t)-    (offset, offset_stms) <- collectStms $ mkOffsetLambdaBody (map I.Var sizes)-                             (I.Var $ I.paramName c_param) 0 offset_params-    return I.Lambda { I.lambdaParams = c_param : offset_params ++ value_params-                    , I.lambdaReturnType = replicate (length arr_ts) (I.Prim int32) ++-                                           map I.rowType arr_ts-                    , I.lambdaBody = mkBody offset_stms $-                                     replicate (length arr_ts) offset ++-                                     map (I.Var . I.paramName) value_params-                    }-  results <- letTupExp "partition_res" $ I.Op $ I.Scatter w-             write_lam (classes : all_offsets ++ arrs) $-             zip3 (repeat w) (repeat 1) blanks-  sizes' <- letSubExp "partition_sizes" $ I.BasicOp $-            I.ArrayLit (map I.Var sizes) $ I.Prim int32-  return (map I.Var results, [sizes'])-  where-    mkOffsetLambdaBody :: [SubExp]-                       -> SubExp-                       -> Int-                       -> [I.LParam]-                       -> InternaliseM SubExp-    mkOffsetLambdaBody _ _ _ [] =-      return $ constant (-1::Int32)-    mkOffsetLambdaBody sizes c i (p:ps) = do-      is_this_one <- letSubExp "is_this_one" $ I.BasicOp $ I.CmpOp (CmpEq int32) c (constant i)-      next_one <- mkOffsetLambdaBody sizes c (i+1) ps-      this_one <- letSubExp "this_offset" =<<-                  foldBinOp (Add Int32 OverflowUndef) (constant (-1::Int32))-                  (I.Var (I.paramName p) : take i sizes)-      letSubExp "total_res" $ I.If is_this_one-        (resultBody [this_one]) (resultBody [next_one]) $ ifCommon [I.Prim int32]--typeExpForError :: E.TypeExp VName -> InternaliseM [ErrorMsgPart SubExp]-typeExpForError (E.TEVar qn _) =-  return [ErrorString $ pretty qn]-typeExpForError (E.TEUnique te _) =-  ("*":) <$> typeExpForError te-typeExpForError (E.TEArray te d _) = do-  d' <- dimExpForError d-  te' <- typeExpForError te-  return $ ["[", d', "]"] ++ te'-typeExpForError (E.TETuple tes _) = do-  tes' <- mapM typeExpForError tes-  return $ ["("] ++ intercalate [", "] tes' ++ [")"]-typeExpForError (E.TERecord fields _) = do-  fields' <- mapM onField fields-  return $ ["{"] ++ intercalate [", "] fields' ++ ["}"]-  where onField (k, te) =-          (ErrorString (pretty k ++ ": "):) <$> typeExpForError te-typeExpForError (E.TEArrow _ t1 t2 _) = do-  t1' <- typeExpForError t1-  t2' <- typeExpForError t2-  return $ t1' ++ [" -> "] ++ t2'-typeExpForError (E.TEApply t arg _) = do-  t' <- typeExpForError t-  arg' <- case arg of TypeArgExpType argt -> typeExpForError argt-                      TypeArgExpDim d _   -> pure <$> dimExpForError d-  return $ t' ++ [" "] ++ arg'-typeExpForError (E.TESum cs _) = do-  cs' <- mapM (onClause . snd) cs-  return $ intercalate [" | "] cs'-  where onClause c = do-          c' <- mapM typeExpForError c-          return $ intercalate [" "] c'--dimExpForError :: E.DimExp VName -> InternaliseM (ErrorMsgPart SubExp)-dimExpForError (DimExpNamed d _) = do-  substs <- lookupSubst $ E.qualLeaf d-  d' <- case substs of-          Just [v] -> return v-          _        -> return $ I.Var $ E.qualLeaf d-  return $ ErrorInt32 d'-dimExpForError (DimExpConst d _) =-  return $ ErrorString $ pretty d-dimExpForError DimExpAny = return ""---- A smart constructor that compacts neighbouring literals for easier--- reading in the IR.-errorMsg :: [ErrorMsgPart a] -> ErrorMsg a-errorMsg = ErrorMsg . compact-  where compact [] = []-        compact (ErrorString x : ErrorString y : parts) =-          compact (ErrorString (x++y) : parts)-        compact (x:y) = x : compact y+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE Strict #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}++-- |+--+-- This module implements a transformation from source to core+-- Futhark.+module Futhark.Internalise (internaliseProg) where++import Control.Monad.Reader+import Data.Bitraversable+import Data.List (find, intercalate, intersperse, nub, transpose)+import qualified Data.List.NonEmpty as NE+import qualified Data.Map.Strict as M+import qualified Data.Set as S+import Futhark.IR.SOACS as I hiding (stmPattern)+import Futhark.Internalise.AccurateSizes+import Futhark.Internalise.Bindings+import Futhark.Internalise.Defunctionalise as Defunctionalise+import Futhark.Internalise.Defunctorise as Defunctorise+import Futhark.Internalise.Lambdas+import Futhark.Internalise.Monad as I+import Futhark.Internalise.Monomorphise as Monomorphise+import Futhark.Internalise.TypesValues+import Futhark.Transform.Rename as I+import Futhark.Util (splitAt3)+import Language.Futhark as E hiding (TypeArg)+import Language.Futhark.Semantic (Imports)++-- | Convert a program in source Futhark to a program in the Futhark+-- core language.+internaliseProg ::+  MonadFreshNames m =>+  Bool ->+  Imports ->+  m (I.Prog SOACS)+internaliseProg always_safe prog = do+  prog_decs <- Defunctorise.transformProg prog+  prog_decs' <- Monomorphise.transformProg prog_decs+  prog_decs'' <- Defunctionalise.transformProg prog_decs'+  (consts, funs) <-+    runInternaliseM always_safe (internaliseValBinds prog_decs'')+  I.renameProg $ I.Prog consts funs++internaliseAttr :: E.AttrInfo -> Attr+internaliseAttr (E.AttrAtom v) = I.AttrAtom v+internaliseAttr (E.AttrComp f attrs) = I.AttrComp f $ map internaliseAttr attrs++internaliseAttrs :: [E.AttrInfo] -> Attrs+internaliseAttrs = mconcat . map (oneAttr . internaliseAttr)++internaliseValBinds :: [E.ValBind] -> InternaliseM ()+internaliseValBinds = mapM_ internaliseValBind++internaliseFunName :: VName -> [E.Pattern] -> InternaliseM Name+internaliseFunName ofname [] = return $ nameFromString $ pretty ofname ++ "f"+internaliseFunName ofname _ = do+  info <- lookupFunction' ofname+  -- In some rare cases involving local functions, the same function+  -- name may be re-used in multiple places.  We check whether the+  -- function name has already been used, and generate a new one if+  -- so.+  case info of+    Just _ -> nameFromString . pretty <$> newNameFromString (baseString ofname)+    Nothing -> return $ nameFromString $ pretty ofname++internaliseValBind :: E.ValBind -> InternaliseM ()+internaliseValBind fb@(E.ValBind entry fname retdecl (Info (rettype, _)) tparams params body _ attrs loc) = do+  localConstsScope $+    bindingParams tparams params $ \shapeparams params' -> do+      let shapenames = map I.paramName shapeparams+          normal_params = shapenames ++ map I.paramName (concat params')+          normal_param_names = namesFromList normal_params++      fname' <- internaliseFunName fname params++      msg <- case retdecl of+        Just dt ->+          errorMsg+            . ("Function return value does not match shape of type " :)+            <$> typeExpForError dt+        Nothing -> return $ errorMsg ["Function return value does not match shape of declared return type."]++      ((rettype', body_res), body_stms) <- collectStms $ do+        body_res <- internaliseExp "res" body+        rettype_bad <- internaliseReturnType rettype+        let rettype' = zeroExts rettype_bad+        return (rettype', body_res)+      body' <-+        ensureResultExtShape msg loc (map I.fromDecl rettype') $+          mkBody body_stms body_res++      constants <- allConsts+      let free_in_fun =+            freeIn body'+              `namesSubtract` normal_param_names+              `namesSubtract` constants++      used_free_params <- forM (namesToList free_in_fun) $ \v -> do+        v_t <- lookupType v+        return $ Param v $ toDecl v_t Nonunique++      let free_shape_params =+            map (`Param` I.Prim int64) $+              concatMap (I.shapeVars . I.arrayShape . I.paramType) used_free_params+          free_params = nub $ free_shape_params ++ used_free_params+          all_params = free_params ++ shapeparams ++ concat params'++      let fd =+            I.FunDef+              Nothing+              (internaliseAttrs attrs)+              fname'+              rettype'+              all_params+              body'++      if null params'+        then bindConstant fname fd+        else+          bindFunction+            fname+            fd+            ( fname',+              map I.paramName free_params,+              shapenames,+              map declTypeOf $ concat params',+              all_params,+              applyRetType rettype' all_params+            )++  case entry of+    Just (Info entry') -> generateEntryPoint entry' fb+    Nothing -> return ()+  where+    zeroExts ts = generaliseExtTypes ts ts++allDimsFreshInType :: MonadFreshNames m => E.PatternType -> m E.PatternType+allDimsFreshInType = bitraverse onDim pure+  where+    onDim (E.NamedDim v) =+      E.NamedDim . E.qualName <$> newVName (baseString $ E.qualLeaf v)+    onDim _ =+      E.NamedDim . E.qualName <$> newVName "size"++-- | Replace all named dimensions with a fresh name, and remove all+-- constant dimensions.  The point is to remove the constraints, but+-- keep the names around.  We use this for constructing the entry+-- point parameters.+allDimsFreshInPat :: MonadFreshNames m => E.Pattern -> m E.Pattern+allDimsFreshInPat (PatternAscription p _ _) =+  allDimsFreshInPat p+allDimsFreshInPat (PatternParens p _) =+  allDimsFreshInPat p+allDimsFreshInPat (Id v (Info t) loc) =+  Id v <$> (Info <$> allDimsFreshInType t) <*> pure loc+allDimsFreshInPat (TuplePattern ps loc) =+  TuplePattern <$> mapM allDimsFreshInPat ps <*> pure loc+allDimsFreshInPat (RecordPattern ps loc) =+  RecordPattern <$> mapM (traverse allDimsFreshInPat) ps <*> pure loc+allDimsFreshInPat (Wildcard (Info t) loc) =+  Wildcard <$> (Info <$> allDimsFreshInType t) <*> pure loc+allDimsFreshInPat (PatternLit e (Info t) loc) =+  PatternLit e <$> (Info <$> allDimsFreshInType t) <*> pure loc+allDimsFreshInPat (PatternConstr c (Info t) pats loc) =+  PatternConstr c <$> (Info <$> allDimsFreshInType t)+    <*> mapM allDimsFreshInPat pats+    <*> pure loc++generateEntryPoint :: E.EntryPoint -> E.ValBind -> InternaliseM ()+generateEntryPoint (E.EntryPoint e_paramts e_rettype) vb = localConstsScope $ do+  let (E.ValBind _ ofname _ (Info (rettype, _)) _ params _ _ attrs loc) = vb+  -- We replace all shape annotations, so there should be no constant+  -- parameters here.+  params_fresh <- mapM allDimsFreshInPat params+  let tparams =+        map (`E.TypeParamDim` mempty) $+          S.toList $+            mconcat $ map E.patternDimNames params_fresh+  bindingParams tparams params_fresh $ \shapeparams params' -> do+    entry_rettype <- internaliseEntryReturnType $ anySizes rettype+    let entry' = entryPoint (zip e_paramts params') (e_rettype, entry_rettype)+        args = map (I.Var . I.paramName) $ concat params'++    entry_body <- insertStmsM $ do+      -- Special case the (rare) situation where the entry point is+      -- not a function.+      maybe_const <- lookupConst ofname+      vals <- case maybe_const of+        Just ses ->+          return ses+        Nothing ->+          fst <$> funcall "entry_result" (E.qualName ofname) args loc+      ctx <-+        extractShapeContext (concat entry_rettype)+          <$> mapM (fmap I.arrayDims . subExpType) vals+      resultBodyM (ctx ++ vals)++    addFunDef $+      I.FunDef+        (Just entry')+        (internaliseAttrs attrs)+        (baseName ofname)+        (concat entry_rettype)+        (shapeparams ++ concat params')+        entry_body++entryPoint ::+  [(E.EntryType, [I.FParam])] ->+  ( E.EntryType,+    [[I.TypeBase ExtShape Uniqueness]]+  ) ->+  I.EntryPoint+entryPoint params (eret, crets) =+  ( concatMap (entryPointType . preParam) params,+    case ( isTupleRecord $ entryType eret,+           entryAscribed eret+         ) of+      (Just ts, Just (E.TETuple e_ts _)) ->+        concatMap entryPointType $+          zip (zipWith E.EntryType ts (map Just e_ts)) crets+      (Just ts, Nothing) ->+        concatMap entryPointType $+          zip (map (`E.EntryType` Nothing) ts) crets+      _ ->+        entryPointType (eret, concat crets)+  )+  where+    preParam (e_t, ps) = (e_t, staticShapes $ map I.paramDeclType ps)++    entryPointType (t, ts)+      | E.Scalar (E.Prim E.Unsigned {}) <- E.entryType t =+        [I.TypeUnsigned]+      | E.Array _ _ (E.Prim E.Unsigned {}) _ <- E.entryType t =+        [I.TypeUnsigned]+      | E.Scalar E.Prim {} <- E.entryType t =+        [I.TypeDirect]+      | E.Array _ _ E.Prim {} _ <- E.entryType t =+        [I.TypeDirect]+      | otherwise =+        [I.TypeOpaque desc $ length ts]+      where+        desc = maybe (pretty t') typeExpOpaqueName $ E.entryAscribed t+        t' = noSizes (E.entryType t) `E.setUniqueness` Nonunique+    typeExpOpaqueName (TEApply te TypeArgExpDim {} _) =+      typeExpOpaqueName te+    typeExpOpaqueName (TEArray te _ _) =+      let (d, te') = withoutDims te+       in "arr_" ++ typeExpOpaqueName te'+            ++ "_"+            ++ show (1 + d)+            ++ "d"+    typeExpOpaqueName te = pretty te++    withoutDims (TEArray te _ _) =+      let (d, te') = withoutDims te+       in (d + 1, te')+    withoutDims te = (0 :: Int, te)++internaliseIdent :: E.Ident -> InternaliseM I.VName+internaliseIdent (E.Ident name (Info tp) loc) =+  case tp of+    E.Scalar E.Prim {} -> return name+    _ ->+      error $+        "Futhark.Internalise.internaliseIdent: asked to internalise non-prim-typed ident '"+          ++ pretty name+          ++ " of type "+          ++ pretty tp+          ++ " at "+          ++ locStr loc+          ++ "."++internaliseBody :: E.Exp -> InternaliseM Body+internaliseBody e = insertStmsM $ resultBody <$> internaliseExp "res" e++bodyFromStms ::+  InternaliseM (Result, a) ->+  InternaliseM (Body, a)+bodyFromStms m = do+  ((res, a), stms) <- collectStms m+  (,a) <$> mkBodyM stms res++internaliseExp :: String -> E.Exp -> InternaliseM [I.SubExp]+internaliseExp desc (E.Parens e _) =+  internaliseExp desc e+internaliseExp desc (E.QualParens _ e _) =+  internaliseExp desc e+internaliseExp desc (E.StringLit vs _) =+  fmap pure $+    letSubExp desc $+      I.BasicOp $ I.ArrayLit (map constant vs) $ I.Prim int8+internaliseExp _ (E.Var (E.QualName _ name) (Info t) loc) = do+  subst <- lookupSubst name+  case subst of+    Just substs -> return substs+    Nothing -> do+      -- If this identifier is the name of a constant, we have to turn it+      -- into a call to the corresponding function.+      is_const <- lookupConst name+      case is_const of+        Just ses -> return ses+        Nothing -> (: []) . I.Var <$> internaliseIdent (E.Ident name (Info t) loc)+internaliseExp desc (E.Index e idxs (Info ret, Info retext) loc) = do+  vs <- internaliseExpToVars "indexed" e+  dims <- case vs of+    [] -> return [] -- Will this happen?+    v : _ -> I.arrayDims <$> lookupType v+  (idxs', cs) <- internaliseSlice loc dims idxs+  let index v = do+        v_t <- lookupType v+        return $ I.BasicOp $ I.Index v $ fullSlice v_t idxs'+  ses <- certifying cs $ letSubExps desc =<< mapM index vs+  bindExtSizes (E.toStruct ret) retext ses+  return ses++-- XXX: we map empty records and tuples to bools, because otherwise+-- arrays of unit will lose their sizes.+internaliseExp _ (E.TupLit [] _) =+  return [constant True]+internaliseExp _ (E.RecordLit [] _) =+  return [constant True]+internaliseExp desc (E.TupLit es _) = concat <$> mapM (internaliseExp desc) es+internaliseExp desc (E.RecordLit orig_fields _) =+  concatMap snd . sortFields . M.unions <$> mapM internaliseField orig_fields+  where+    internaliseField (E.RecordFieldExplicit name e _) =+      M.singleton name <$> internaliseExp desc e+    internaliseField (E.RecordFieldImplicit name t loc) =+      internaliseField $+        E.RecordFieldExplicit+          (baseName name)+          (E.Var (E.qualName name) t loc)+          loc+internaliseExp desc (E.ArrayLit es (Info arr_t) loc)+  -- If this is a multidimensional array literal of primitives, we+  -- treat it specially by flattening it out followed by a reshape.+  -- This cuts down on the amount of statements that are produced, and+  -- thus allows us to efficiently handle huge array literals - a+  -- corner case, but an important one.+  | Just ((eshape, e') : es') <- mapM isArrayLiteral es,+    not $ null eshape,+    all ((eshape ==) . fst) es',+    Just basetype <- E.peelArray (length eshape) arr_t = do+    let flat_lit = E.ArrayLit (e' ++ concatMap snd es') (Info basetype) loc+        new_shape = length es : eshape+    flat_arrs <- internaliseExpToVars "flat_literal" flat_lit+    forM flat_arrs $ \flat_arr -> do+      flat_arr_t <- lookupType flat_arr+      let new_shape' =+            reshapeOuter+              (map (DimNew . intConst Int64 . toInteger) new_shape)+              1+              $ I.arrayShape flat_arr_t+      letSubExp desc $ I.BasicOp $ I.Reshape new_shape' flat_arr+  | otherwise = do+    es' <- mapM (internaliseExp "arr_elem") es+    arr_t_ext <- internaliseReturnType (E.toStruct arr_t)++    rowtypes <-+      case mapM (fmap rowType . hasStaticShape . I.fromDecl) arr_t_ext of+        Just ts -> pure ts+        Nothing ->+          -- XXX: the monomorphiser may create single-element array+          -- literals with an unknown row type.  In those cases we+          -- need to look at the types of the actual elements.+          -- Fixing this in the monomorphiser is a lot more tricky+          -- than just working around it here.+          case es' of+            [] -> error $ "internaliseExp ArrayLit: existential type: " ++ pretty arr_t+            e' : _ -> mapM subExpType e'++    let arraylit ks rt = do+          ks' <-+            mapM+              ( ensureShape+                  "shape of element differs from shape of first element"+                  loc+                  rt+                  "elem_reshaped"+              )+              ks+          return $ I.BasicOp $ I.ArrayLit ks' rt++    letSubExps desc+      =<< if null es'+        then mapM (arraylit []) rowtypes+        else zipWithM arraylit (transpose es') rowtypes+  where+    isArrayLiteral :: E.Exp -> Maybe ([Int], [E.Exp])+    isArrayLiteral (E.ArrayLit inner_es _ _) = do+      (eshape, e) : inner_es' <- mapM isArrayLiteral inner_es+      guard $ all ((eshape ==) . fst) inner_es'+      return (length inner_es : eshape, e ++ concatMap snd inner_es')+    isArrayLiteral e =+      Just ([], [e])+internaliseExp desc (E.Range start maybe_second end (Info ret, Info retext) loc) = do+  start' <- internaliseExp1 "range_start" start+  end' <- internaliseExp1 "range_end" $ case end of+    DownToExclusive e -> e+    ToInclusive e -> e+    UpToExclusive e -> e+  maybe_second' <-+    traverse (internaliseExp1 "range_second") maybe_second++  -- Construct an error message in case the range is invalid.+  let conv = case E.typeOf start of+        E.Scalar (E.Prim (E.Unsigned _)) -> asIntZ Int64+        _ -> asIntS Int64+  start'_i64 <- conv start'+  end'_i64 <- conv end'+  maybe_second'_i64 <- traverse conv maybe_second'+  let errmsg =+        errorMsg $+          ["Range "]+            ++ [ErrorInt64 start'_i64]+            ++ ( case maybe_second'_i64 of+                   Nothing -> []+                   Just second_i64 -> ["..", ErrorInt64 second_i64]+               )+            ++ ( case end of+                   DownToExclusive {} -> ["..>"]+                   ToInclusive {} -> ["..."]+                   UpToExclusive {} -> ["..<"]+               )+            ++ [ErrorInt64 end'_i64, " is invalid."]++  (it, le_op, lt_op) <-+    case E.typeOf start of+      E.Scalar (E.Prim (E.Signed it)) -> return (it, CmpSle it, CmpSlt it)+      E.Scalar (E.Prim (E.Unsigned it)) -> return (it, CmpUle it, CmpUlt it)+      start_t -> error $ "Start value in range has type " ++ pretty start_t++  let one = intConst it 1+      negone = intConst it (-1)+      default_step = case end of+        DownToExclusive {} -> negone+        ToInclusive {} -> one+        UpToExclusive {} -> one++  (step, step_zero) <- case maybe_second' of+    Just second' -> do+      subtracted_step <-+        letSubExp "subtracted_step" $+          I.BasicOp $ I.BinOp (I.Sub it I.OverflowWrap) second' start'+      step_zero <- letSubExp "step_zero" $ I.BasicOp $ I.CmpOp (I.CmpEq $ IntType it) start' second'+      return (subtracted_step, step_zero)+    Nothing ->+      return (default_step, constant False)++  step_sign <- letSubExp "s_sign" $ BasicOp $ I.UnOp (I.SSignum it) step+  step_sign_i64 <- asIntS Int64 step_sign++  bounds_invalid_downwards <-+    letSubExp "bounds_invalid_downwards" $+      I.BasicOp $ I.CmpOp le_op start' end'+  bounds_invalid_upwards <-+    letSubExp "bounds_invalid_upwards" $+      I.BasicOp $ I.CmpOp lt_op end' start'++  (distance, step_wrong_dir, bounds_invalid) <- case end of+    DownToExclusive {} -> do+      step_wrong_dir <-+        letSubExp "step_wrong_dir" $+          I.BasicOp $ I.CmpOp (I.CmpEq $ IntType it) step_sign one+      distance <-+        letSubExp "distance" $+          I.BasicOp $ I.BinOp (Sub it I.OverflowWrap) start' end'+      distance_i64 <- asIntS Int64 distance+      return (distance_i64, step_wrong_dir, bounds_invalid_downwards)+    UpToExclusive {} -> do+      step_wrong_dir <-+        letSubExp "step_wrong_dir" $+          I.BasicOp $ I.CmpOp (I.CmpEq $ IntType it) step_sign negone+      distance <- letSubExp "distance" $ I.BasicOp $ I.BinOp (Sub it I.OverflowWrap) end' start'+      distance_i64 <- asIntS Int64 distance+      return (distance_i64, step_wrong_dir, bounds_invalid_upwards)+    ToInclusive {} -> do+      downwards <-+        letSubExp "downwards" $+          I.BasicOp $ I.CmpOp (I.CmpEq $ IntType it) step_sign negone+      distance_downwards_exclusive <-+        letSubExp "distance_downwards_exclusive" $+          I.BasicOp $ I.BinOp (Sub it I.OverflowWrap) start' end'+      distance_upwards_exclusive <-+        letSubExp "distance_upwards_exclusive" $+          I.BasicOp $ I.BinOp (Sub it I.OverflowWrap) end' start'++      bounds_invalid <-+        letSubExp "bounds_invalid" $+          I.If+            downwards+            (resultBody [bounds_invalid_downwards])+            (resultBody [bounds_invalid_upwards])+            $ ifCommon [I.Prim I.Bool]+      distance_exclusive <-+        letSubExp "distance_exclusive" $+          I.If+            downwards+            (resultBody [distance_downwards_exclusive])+            (resultBody [distance_upwards_exclusive])+            $ ifCommon [I.Prim $ IntType it]+      distance_exclusive_i64 <- asIntS Int64 distance_exclusive+      distance <-+        letSubExp "distance" $+          I.BasicOp $+            I.BinOp+              (Add Int64 I.OverflowWrap)+              distance_exclusive_i64+              (intConst Int64 1)+      return (distance, constant False, bounds_invalid)++  step_invalid <-+    letSubExp "step_invalid" $+      I.BasicOp $ I.BinOp I.LogOr step_wrong_dir step_zero++  invalid <-+    letSubExp "range_invalid" $+      I.BasicOp $ I.BinOp I.LogOr step_invalid bounds_invalid+  valid <- letSubExp "valid" $ I.BasicOp $ I.UnOp I.Not invalid+  cs <- assert "range_valid_c" valid errmsg loc++  step_i64 <- asIntS Int64 step+  pos_step <-+    letSubExp "pos_step" $+      I.BasicOp $ I.BinOp (Mul Int64 I.OverflowWrap) step_i64 step_sign_i64++  num_elems <-+    certifying cs $+      letSubExp "num_elems" $+        I.BasicOp $ I.BinOp (SDivUp Int64 I.Unsafe) distance pos_step++  se <- letSubExp desc (I.BasicOp $ I.Iota num_elems start' step it)+  bindExtSizes (E.toStruct ret) retext [se]+  return [se]+internaliseExp desc (E.Ascript e _ _) =+  internaliseExp desc e+internaliseExp desc (E.Coerce e (TypeDecl dt (Info et)) (Info ret, Info retext) loc) = do+  ses <- internaliseExp desc e+  ts <- internaliseReturnType et+  dt' <- typeExpForError dt+  bindExtSizes (E.toStruct ret) retext ses+  forM (zip ses ts) $ \(e', t') -> do+    dims <- arrayDims <$> subExpType e'+    let parts =+          ["Value of (core language) shape ("]+            ++ intersperse ", " (map ErrorInt64 dims)+            ++ [") cannot match shape of type `"]+            ++ dt'+            ++ ["`."]+    ensureExtShape (errorMsg parts) loc (I.fromDecl t') desc e'+internaliseExp desc (E.Negate e _) = do+  e' <- internaliseExp1 "negate_arg" e+  et <- subExpType e'+  case et of+    I.Prim (I.IntType t) ->+      letTupExp' desc $ I.BasicOp $ I.BinOp (I.Sub t I.OverflowWrap) (I.intConst t 0) e'+    I.Prim (I.FloatType t) ->+      letTupExp' desc $ I.BasicOp $ I.BinOp (I.FSub t) (I.floatConst t 0) e'+    _ -> error "Futhark.Internalise.internaliseExp: non-numeric type in Negate"+internaliseExp desc e@E.Apply {} = do+  (qfname, args, ret, retext) <- findFuncall e+  -- Argument evaluation is outermost-in so that any existential sizes+  -- created by function applications can be brought into scope.+  let fname = nameFromString $ pretty $ baseName $ qualLeaf qfname+      loc = srclocOf e+      arg_desc = nameToString fname ++ "_arg"++  -- Some functions are magical (overloaded) and we handle that here.+  ses <-+    case () of+      -- Overloaded functions never take array arguments (except+      -- equality, but those cannot be existential), so we can safely+      -- ignore the existential dimensions.+      ()+        | Just internalise <- isOverloadedFunction qfname (map fst args) loc ->+          internalise desc+        | Just (rettype, _) <- M.lookup fname I.builtInFunctions -> do+          let tag ses = [(se, I.Observe) | se <- ses]+          args' <- reverse <$> mapM (internaliseArg arg_desc) (reverse args)+          let args'' = concatMap tag args'+          letTupExp' desc $+            I.Apply+              fname+              args''+              [I.Prim rettype]+              (Safe, loc, [])+        | otherwise -> do+          args' <- concat . reverse <$> mapM (internaliseArg arg_desc) (reverse args)+          fst <$> funcall desc qfname args' loc++  bindExtSizes ret retext ses+  return ses+internaliseExp desc (E.LetPat pat e body (Info ret, Info retext) _) = do+  ses <- internalisePat desc pat e body (internaliseExp desc)+  bindExtSizes (E.toStruct ret) retext ses+  return ses+internaliseExp desc (E.LetFun ofname (tparams, params, retdecl, Info rettype, body) letbody _ loc) = do+  internaliseValBind $+    E.ValBind Nothing ofname retdecl (Info (rettype, [])) tparams params body Nothing mempty loc+  internaliseExp desc letbody+internaliseExp desc (E.DoLoop sparams mergepat mergeexp form loopbody (Info (ret, retext)) loc) = do+  ses <- internaliseExp "loop_init" mergeexp+  ((loopbody', (form', shapepat, mergepat', mergeinit')), initstms) <-+    collectStms $ handleForm ses form++  addStms initstms+  mergeinit_ts' <- mapM subExpType mergeinit'++  ctxinit <- argShapes (map I.paramName shapepat) mergepat' mergeinit_ts'++  let ctxmerge = zip shapepat ctxinit+      valmerge = zip mergepat' mergeinit'+      dropCond = case form of+        E.While {} -> drop 1+        _ -> id++  -- Ensure that the result of the loop matches the shapes of the+  -- merge parameters.  XXX: Ideally they should already match (by+  -- the source language type rules), but some of our+  -- transformations (esp. defunctionalisation) strips out some size+  -- information.  For a type-correct source program, these reshapes+  -- should simplify away.+  let merge = ctxmerge ++ valmerge+      merge_ts = map (I.paramType . fst) merge+  loopbody'' <-+    localScope (scopeOfFParams $ map fst merge) $+      inScopeOf form' $+        insertStmsM $+          resultBodyM+            =<< ensureArgShapes+              "shape of loop result does not match shapes in loop parameter"+              loc+              (map (I.paramName . fst) ctxmerge)+              merge_ts+            =<< bodyBind loopbody'++  attrs <- asks envAttrs+  loop_res <-+    map I.Var . dropCond+      <$> attributing+        attrs+        (letTupExp desc (I.DoLoop ctxmerge valmerge form' loopbody''))+  bindExtSizes (E.toStruct ret) retext loop_res+  return loop_res+  where+    sparams' = map (`TypeParamDim` mempty) sparams++    forLoop mergepat' shapepat mergeinit form' =+      bodyFromStms $+        inScopeOf form' $ do+          ses <- internaliseExp "loopres" loopbody+          sets <- mapM subExpType ses+          shapeargs <- argShapes (map I.paramName shapepat) mergepat' sets+          return+            ( shapeargs ++ ses,+              ( form',+                shapepat,+                mergepat',+                mergeinit+              )+            )++    handleForm mergeinit (E.ForIn x arr) = do+      arr' <- internaliseExpToVars "for_in_arr" arr+      arr_ts <- mapM lookupType arr'+      let w = arraysSize 0 arr_ts++      i <- newVName "i"++      bindingLoopParams sparams' mergepat $+        \shapepat mergepat' ->+          bindingLambdaParams [x] (map rowType arr_ts) $ \x_params -> do+            let loopvars = zip x_params arr'+            forLoop mergepat' shapepat mergeinit $+              I.ForLoop i Int64 w loopvars+    handleForm mergeinit (E.For i num_iterations) = do+      num_iterations' <- internaliseExp1 "upper_bound" num_iterations+      i' <- internaliseIdent i+      num_iterations_t <- I.subExpType num_iterations'+      it <- case num_iterations_t of+        I.Prim (IntType it) -> return it+        _ -> error "internaliseExp DoLoop: invalid type"++      bindingLoopParams sparams' mergepat $+        \shapepat mergepat' ->+          forLoop mergepat' shapepat mergeinit $+            I.ForLoop i' it num_iterations' []+    handleForm mergeinit (E.While cond) =+      bindingLoopParams sparams' mergepat $ \shapepat mergepat' -> do+        mergeinit_ts <- mapM subExpType mergeinit+        -- We need to insert 'cond' twice - once for the initial+        -- condition (do we enter the loop at all?), and once with the+        -- result values of the loop (do we continue into the next+        -- iteration?).  This is safe, as the type rules for the+        -- external language guarantees that 'cond' does not consume+        -- anything.+        shapeinit <- argShapes (map I.paramName shapepat) mergepat' mergeinit_ts++        (loop_initial_cond, init_loop_cond_bnds) <- collectStms $ do+          forM_ (zip shapepat shapeinit) $ \(p, se) ->+            letBindNames [paramName p] $ BasicOp $ SubExp se+          forM_ (zip mergepat' mergeinit) $ \(p, se) ->+            unless (se == I.Var (paramName p)) $+              letBindNames [paramName p] $+                BasicOp $+                  case se of+                    I.Var v+                      | not $ primType $ paramType p ->+                        Reshape (map DimCoercion $ arrayDims $ paramType p) v+                    _ -> SubExp se+          internaliseExp1 "loop_cond" cond++        addStms init_loop_cond_bnds++        bodyFromStms $ do+          ses <- internaliseExp "loopres" loopbody+          sets <- mapM subExpType ses+          loop_while <- newParam "loop_while" $ I.Prim I.Bool+          shapeargs <- argShapes (map I.paramName shapepat) mergepat' sets++          -- Careful not to clobber anything.+          loop_end_cond_body <- renameBody <=< insertStmsM $ do+            forM_ (zip shapepat shapeargs) $ \(p, se) ->+              unless (se == I.Var (paramName p)) $+                letBindNames [paramName p] $ BasicOp $ SubExp se+            forM_ (zip mergepat' ses) $ \(p, se) ->+              unless (se == I.Var (paramName p)) $+                letBindNames [paramName p] $+                  BasicOp $+                    case se of+                      I.Var v+                        | not $ primType $ paramType p ->+                          Reshape (map DimCoercion $ arrayDims $ paramType p) v+                      _ -> SubExp se+            resultBody <$> internaliseExp "loop_cond" cond+          loop_end_cond <- bodyBind loop_end_cond_body++          return+            ( shapeargs ++ loop_end_cond ++ ses,+              ( I.WhileLoop $ I.paramName loop_while,+                shapepat,+                loop_while : mergepat',+                loop_initial_cond : mergeinit+              )+            )+internaliseExp desc (E.LetWith name src idxs ve body t loc) = do+  let pat = E.Id (E.identName name) (E.identType name) loc+      src_t = E.fromStruct <$> E.identType src+      e = E.Update (E.Var (E.qualName $ E.identName src) src_t loc) idxs ve loc+  internaliseExp desc $ E.LetPat pat e body (t, Info []) loc+internaliseExp desc (E.Update src slice ve loc) = do+  ves <- internaliseExp "lw_val" ve+  srcs <- internaliseExpToVars "src" src+  dims <- case srcs of+    [] -> return [] -- Will this happen?+    v : _ -> I.arrayDims <$> lookupType v+  (idxs', cs) <- internaliseSlice loc dims slice++  let comb sname ve' = do+        sname_t <- lookupType sname+        let full_slice = fullSlice sname_t idxs'+            rowtype = sname_t `setArrayDims` sliceDims full_slice+        ve'' <-+          ensureShape+            "shape of value does not match shape of source array"+            loc+            rowtype+            "lw_val_correct_shape"+            ve'+        letInPlace desc sname full_slice $ BasicOp $ SubExp ve''+  certifying cs $ map I.Var <$> zipWithM comb srcs ves+internaliseExp desc (E.RecordUpdate src fields ve _ _) = do+  src' <- internaliseExp desc src+  ve' <- internaliseExp desc ve+  replace (E.typeOf src `setAliases` ()) fields ve' src'+  where+    replace (E.Scalar (E.Record m)) (f : fs) ve' src'+      | Just t <- M.lookup f m = do+        i <-+          fmap sum $+            mapM (internalisedTypeSize . snd) $+              takeWhile ((/= f) . fst) $ sortFields m+        k <- internalisedTypeSize t+        let (bef, to_update, aft) = splitAt3 i k src'+        src'' <- replace t fs ve' to_update+        return $ bef ++ src'' ++ aft+    replace _ _ ve' _ = return ve'+internaliseExp desc (E.Attr attr e _) =+  local f $ internaliseExp desc e+  where+    attrs = oneAttr $ internaliseAttr attr+    f env+      | "unsafe" `inAttrs` attrs,+        not $ envSafe env =+        env {envDoBoundsChecks = False}+      | otherwise =+        env {envAttrs = envAttrs env <> attrs}+internaliseExp desc (E.Assert e1 e2 (Info check) loc) = do+  e1' <- internaliseExp1 "assert_cond" e1+  c <- assert "assert_c" e1' (errorMsg [ErrorString $ "Assertion is false: " <> check]) loc+  -- Make sure there are some bindings to certify.+  certifying c $ mapM rebind =<< internaliseExp desc e2+  where+    rebind v = do+      v' <- newVName "assert_res"+      letBindNames [v'] $ I.BasicOp $ I.SubExp v+      return $ I.Var v'+internaliseExp _ (E.Constr c es (Info (E.Scalar (E.Sum fs))) _) = do+  (ts, constr_map) <- internaliseSumType $ M.map (map E.toStruct) fs+  es' <- concat <$> mapM (internaliseExp "payload") es++  let noExt _ = return $ intConst Int64 0+  ts' <- instantiateShapes noExt $ map fromDecl ts++  case M.lookup c constr_map of+    Just (i, js) ->+      (intConst Int8 (toInteger i) :) <$> clauses 0 ts' (zip js es')+    Nothing ->+      error "internaliseExp Constr: missing constructor"+  where+    clauses j (t : ts) js_to_es+      | Just e <- j `lookup` js_to_es =+        (e :) <$> clauses (j + 1) ts js_to_es+      | otherwise = do+        blank <- letSubExp "zero" =<< eBlank t+        (blank :) <$> clauses (j + 1) ts js_to_es+    clauses _ [] _ =+      return []+internaliseExp _ (E.Constr _ _ (Info t) loc) =+  error $ "internaliseExp: constructor with type " ++ pretty t ++ " at " ++ locStr loc+internaliseExp desc (E.Match e cs (Info ret, Info retext) _) = do+  ses <- internaliseExp (desc ++ "_scrutinee") e+  res <-+    case NE.uncons cs of+      (CasePat pCase eCase _, Nothing) -> do+        (_, pertinent) <- generateCond pCase ses+        internalisePat' pCase pertinent eCase (internaliseExp desc)+      (c, Just cs') -> do+        let CasePat pLast eLast _ = NE.last cs'+        bFalse <- do+          (_, pertinent) <- generateCond pLast ses+          eLast' <- internalisePat' pLast pertinent eLast internaliseBody+          foldM (\bf c' -> eBody $ return $ generateCaseIf ses c' bf) eLast' $+            reverse $ NE.init cs'+        letTupExp' desc =<< generateCaseIf ses c bFalse+  bindExtSizes (E.toStruct ret) retext res+  return res++-- The "interesting" cases are over, now it's mostly boilerplate.++internaliseExp _ (E.Literal v _) =+  return [I.Constant $ internalisePrimValue v]+internaliseExp _ (E.IntLit v (Info t) _) =+  case t of+    E.Scalar (E.Prim (E.Signed it)) ->+      return [I.Constant $ I.IntValue $ intValue it v]+    E.Scalar (E.Prim (E.Unsigned it)) ->+      return [I.Constant $ I.IntValue $ intValue it v]+    E.Scalar (E.Prim (E.FloatType ft)) ->+      return [I.Constant $ I.FloatValue $ floatValue ft v]+    _ -> error $ "internaliseExp: nonsensical type for integer literal: " ++ pretty t+internaliseExp _ (E.FloatLit v (Info t) _) =+  case t of+    E.Scalar (E.Prim (E.FloatType ft)) ->+      return [I.Constant $ I.FloatValue $ floatValue ft v]+    _ -> error $ "internaliseExp: nonsensical type for float literal: " ++ pretty t+internaliseExp desc (E.If ce te fe (Info ret, Info retext) _) = do+  ses <-+    letTupExp' desc+      =<< eIf+        (BasicOp . SubExp <$> internaliseExp1 "cond" ce)+        (internaliseBody te)+        (internaliseBody fe)+  bindExtSizes (E.toStruct ret) retext ses+  return ses++-- Builtin operators are handled specially because they are+-- overloaded.+internaliseExp desc (E.BinOp (op, _) _ (xe, _) (ye, _) _ _ loc)+  | Just internalise <- isOverloadedFunction op [xe, ye] loc =+    internalise desc+-- User-defined operators are just the same as a function call.+internaliseExp+  desc+  ( E.BinOp+      (op, oploc)+      (Info t)+      (xarg, Info (xt, xext))+      (yarg, Info (yt, yext))+      _+      (Info retext)+      loc+    ) =+    internaliseExp desc $+      E.Apply+        ( E.Apply+            (E.Var op (Info t) oploc)+            xarg+            (Info (E.diet xt, xext))+            (Info $ foldFunType [E.fromStruct yt] t, Info [])+            loc+        )+        yarg+        (Info (E.diet yt, yext))+        (Info t, Info retext)+        loc+internaliseExp desc (E.Project k e (Info rt) _) = do+  n <- internalisedTypeSize $ rt `setAliases` ()+  i' <- fmap sum $+    mapM internalisedTypeSize $+      case E.typeOf e `setAliases` () of+        E.Scalar (Record fs) ->+          map snd $ takeWhile ((/= k) . fst) $ sortFields fs+        t -> [t]+  take n . drop i' <$> internaliseExp desc e+internaliseExp _ e@E.Lambda {} =+  error $ "internaliseExp: Unexpected lambda at " ++ locStr (srclocOf e)+internaliseExp _ e@E.OpSection {} =+  error $ "internaliseExp: Unexpected operator section at " ++ locStr (srclocOf e)+internaliseExp _ e@E.OpSectionLeft {} =+  error $ "internaliseExp: Unexpected left operator section at " ++ locStr (srclocOf e)+internaliseExp _ e@E.OpSectionRight {} =+  error $ "internaliseExp: Unexpected right operator section at " ++ locStr (srclocOf e)+internaliseExp _ e@E.ProjectSection {} =+  error $ "internaliseExp: Unexpected projection section at " ++ locStr (srclocOf e)+internaliseExp _ e@E.IndexSection {} =+  error $ "internaliseExp: Unexpected index section at " ++ locStr (srclocOf e)++internaliseArg :: String -> (E.Exp, Maybe VName) -> InternaliseM [SubExp]+internaliseArg desc (arg, argdim) = do+  arg' <- internaliseExp desc arg+  case (arg', argdim) of+    ([se], Just d) -> letBindNames [d] $ BasicOp $ SubExp se+    _ -> return ()+  return arg'++generateCond :: E.Pattern -> [I.SubExp] -> InternaliseM (I.SubExp, [I.SubExp])+generateCond orig_p orig_ses = do+  (cmps, pertinent, _) <- compares orig_p orig_ses+  cmp <- letSubExp "matches" =<< eAll cmps+  return (cmp, pertinent)+  where+    -- Literals are always primitive values.+    compares (E.PatternLit e _ _) (se : ses) = do+      e' <- internaliseExp1 "constant" e+      t' <- elemType <$> subExpType se+      cmp <- letSubExp "match_lit" $ I.BasicOp $ I.CmpOp (I.CmpEq t') e' se+      return ([cmp], [se], ses)+    compares (E.PatternConstr c (Info (E.Scalar (E.Sum fs))) pats _) (se : ses) = do+      (payload_ts, m) <- internaliseSumType $ M.map (map toStruct) fs+      case M.lookup c m of+        Just (i, payload_is) -> do+          let i' = intConst Int8 $ toInteger i+          let (payload_ses, ses') = splitAt (length payload_ts) ses+          cmp <- letSubExp "match_constr" $ I.BasicOp $ I.CmpOp (I.CmpEq int8) i' se+          (cmps, pertinent, _) <- comparesMany pats $ map (payload_ses !!) payload_is+          return (cmp : cmps, pertinent, ses')+        Nothing ->+          error "generateCond: missing constructor"+    compares (E.PatternConstr _ (Info t) _ _) _ =+      error $ "generateCond: PatternConstr has nonsensical type: " ++ pretty t+    compares (E.Id _ t loc) ses =+      compares (E.Wildcard t loc) ses+    compares (E.Wildcard (Info t) _) ses = do+      n <- internalisedTypeSize $ E.toStruct t+      let (id_ses, rest_ses) = splitAt n ses+      return ([], id_ses, rest_ses)+    compares (E.PatternParens pat _) ses =+      compares pat ses+    compares (E.TuplePattern pats _) ses =+      comparesMany pats ses+    compares (E.RecordPattern fs _) ses =+      comparesMany (map snd $ E.sortFields $ M.fromList fs) ses+    compares (E.PatternAscription pat _ _) ses =+      compares pat ses+    compares pat [] =+      error $ "generateCond: No values left for pattern " ++ pretty pat++    comparesMany [] ses = return ([], [], ses)+    comparesMany (pat : pats) ses = do+      (cmps1, pertinent1, ses') <- compares pat ses+      (cmps2, pertinent2, ses'') <- comparesMany pats ses'+      return+        ( cmps1 <> cmps2,+          pertinent1 <> pertinent2,+          ses''+        )++generateCaseIf :: [I.SubExp] -> Case -> I.Body -> InternaliseM I.Exp+generateCaseIf ses (CasePat p eCase _) bFail = do+  (cond, pertinent) <- generateCond p ses+  eCase' <- internalisePat' p pertinent eCase internaliseBody+  eIf (eSubExp cond) (return eCase') (return bFail)++internalisePat ::+  String ->+  E.Pattern ->+  E.Exp ->+  E.Exp ->+  (E.Exp -> InternaliseM a) ->+  InternaliseM a+internalisePat desc p e body m = do+  ses <- internaliseExp desc' e+  internalisePat' p ses body m+  where+    desc' = case S.toList $ E.patternIdents p of+      [v] -> baseString $ E.identName v+      _ -> desc++internalisePat' ::+  E.Pattern ->+  [I.SubExp] ->+  E.Exp ->+  (E.Exp -> InternaliseM a) ->+  InternaliseM a+internalisePat' p ses body m = do+  ses_ts <- mapM subExpType ses+  stmPattern p ses_ts $ \pat_names -> do+    forM_ (zip pat_names ses) $ \(v, se) ->+      letBindNames [v] $ I.BasicOp $ I.SubExp se+    m body++internaliseSlice ::+  SrcLoc ->+  [SubExp] ->+  [E.DimIndex] ->+  InternaliseM ([I.DimIndex SubExp], Certificates)+internaliseSlice loc dims idxs = do+  (idxs', oks, parts) <- unzip3 <$> zipWithM internaliseDimIndex dims idxs+  ok <- letSubExp "index_ok" =<< eAll oks+  let msg =+        errorMsg $+          ["Index ["] ++ intercalate [", "] parts+            ++ ["] out of bounds for array of shape ["]+            ++ intersperse "][" (map ErrorInt64 $ take (length idxs) dims)+            ++ ["]."]+  c <- assert "index_certs" ok msg loc+  return (idxs', c)++internaliseDimIndex ::+  SubExp ->+  E.DimIndex ->+  InternaliseM (I.DimIndex SubExp, SubExp, [ErrorMsgPart SubExp])+internaliseDimIndex w (E.DimFix i) = do+  (i', _) <- internaliseDimExp "i" i+  let lowerBound =+        I.BasicOp $+          I.CmpOp (I.CmpSle I.Int64) (I.constant (0 :: I.Int64)) i'+      upperBound =+        I.BasicOp $+          I.CmpOp (I.CmpSlt I.Int64) i' w+  ok <- letSubExp "bounds_check" =<< eBinOp I.LogAnd (pure lowerBound) (pure upperBound)+  return (I.DimFix i', ok, [ErrorInt64 i'])++-- Special-case an important common case that otherwise leads to horrible code.+internaliseDimIndex+  w+  ( E.DimSlice+      Nothing+      Nothing+      (Just (E.Negate (E.IntLit 1 _ _) _))+    ) = do+    w_minus_1 <-+      letSubExp "w_minus_1" $+        BasicOp $ I.BinOp (Sub Int64 I.OverflowWrap) w one+    return+      ( I.DimSlice w_minus_1 w $ intConst Int64 (-1),+        constant True,+        mempty+      )+    where+      one = constant (1 :: Int64)+internaliseDimIndex w (E.DimSlice i j s) = do+  s' <- maybe (return one) (fmap fst . internaliseDimExp "s") s+  s_sign <- letSubExp "s_sign" $ BasicOp $ I.UnOp (I.SSignum Int64) s'+  backwards <- letSubExp "backwards" $ I.BasicOp $ I.CmpOp (I.CmpEq int64) s_sign negone+  w_minus_1 <- letSubExp "w_minus_1" $ BasicOp $ I.BinOp (Sub Int64 I.OverflowWrap) w one+  let i_def =+        letSubExp "i_def" $+          I.If+            backwards+            (resultBody [w_minus_1])+            (resultBody [zero])+            $ ifCommon [I.Prim int64]+      j_def =+        letSubExp "j_def" $+          I.If+            backwards+            (resultBody [negone])+            (resultBody [w])+            $ ifCommon [I.Prim int64]+  i' <- maybe i_def (fmap fst . internaliseDimExp "i") i+  j' <- maybe j_def (fmap fst . internaliseDimExp "j") j+  j_m_i <- letSubExp "j_m_i" $ BasicOp $ I.BinOp (Sub Int64 I.OverflowWrap) j' i'+  -- Something like a division-rounding-up, but accomodating negative+  -- operands.+  let divRounding x y =+        eBinOp+          (SQuot Int64 Unsafe)+          ( eBinOp+              (Add Int64 I.OverflowWrap)+              x+              (eBinOp (Sub Int64 I.OverflowWrap) y (eSignum $ toExp s'))+          )+          y+  n <- letSubExp "n" =<< divRounding (toExp j_m_i) (toExp s')++  -- Bounds checks depend on whether we are slicing forwards or+  -- backwards.  If forwards, we must check '0 <= i && i <= j'.  If+  -- backwards, '-1 <= j && j <= i'.  In both cases, we check '0 <=+  -- i+n*s && i+(n-1)*s < w'.  We only check if the slice is nonempty.+  empty_slice <- letSubExp "empty_slice" $ I.BasicOp $ I.CmpOp (CmpEq int64) n zero++  m <- letSubExp "m" $ I.BasicOp $ I.BinOp (Sub Int64 I.OverflowWrap) n one+  m_t_s <- letSubExp "m_t_s" $ I.BasicOp $ I.BinOp (Mul Int64 I.OverflowWrap) m s'+  i_p_m_t_s <- letSubExp "i_p_m_t_s" $ I.BasicOp $ I.BinOp (Add Int64 I.OverflowWrap) i' m_t_s+  zero_leq_i_p_m_t_s <-+    letSubExp "zero_leq_i_p_m_t_s" $+      I.BasicOp $ I.CmpOp (I.CmpSle Int64) zero i_p_m_t_s+  i_p_m_t_s_leq_w <-+    letSubExp "i_p_m_t_s_leq_w" $+      I.BasicOp $ I.CmpOp (I.CmpSle Int64) i_p_m_t_s w+  i_p_m_t_s_lth_w <-+    letSubExp "i_p_m_t_s_leq_w" $+      I.BasicOp $ I.CmpOp (I.CmpSlt Int64) i_p_m_t_s w++  zero_lte_i <- letSubExp "zero_lte_i" $ I.BasicOp $ I.CmpOp (I.CmpSle Int64) zero i'+  i_lte_j <- letSubExp "i_lte_j" $ I.BasicOp $ I.CmpOp (I.CmpSle Int64) i' j'+  forwards_ok <-+    letSubExp "forwards_ok"+      =<< eAll [zero_lte_i, zero_lte_i, i_lte_j, zero_leq_i_p_m_t_s, i_p_m_t_s_lth_w]++  negone_lte_j <- letSubExp "negone_lte_j" $ I.BasicOp $ I.CmpOp (I.CmpSle Int64) negone j'+  j_lte_i <- letSubExp "j_lte_i" $ I.BasicOp $ I.CmpOp (I.CmpSle Int64) j' i'+  backwards_ok <-+    letSubExp "backwards_ok"+      =<< eAll+        [negone_lte_j, negone_lte_j, j_lte_i, zero_leq_i_p_m_t_s, i_p_m_t_s_leq_w]++  slice_ok <-+    letSubExp "slice_ok" $+      I.If+        backwards+        (resultBody [backwards_ok])+        (resultBody [forwards_ok])+        $ ifCommon [I.Prim I.Bool]+  ok_or_empty <-+    letSubExp "ok_or_empty" $+      I.BasicOp $ I.BinOp I.LogOr empty_slice slice_ok++  let parts = case (i, j, s) of+        (_, _, Just {}) ->+          [ maybe "" (const $ ErrorInt64 i') i,+            ":",+            maybe "" (const $ ErrorInt64 j') j,+            ":",+            ErrorInt64 s'+          ]+        (_, Just {}, _) ->+          [ maybe "" (const $ ErrorInt64 i') i,+            ":",+            ErrorInt64 j'+          ]+            ++ maybe mempty (const [":", ErrorInt64 s']) s+        (_, Nothing, Nothing) ->+          [ErrorInt64 i', ":"]+  return (I.DimSlice i' n s', ok_or_empty, parts)+  where+    zero = constant (0 :: Int64)+    negone = constant (-1 :: Int64)+    one = constant (1 :: Int64)++internaliseScanOrReduce ::+  String ->+  String ->+  (SubExp -> I.Lambda -> [SubExp] -> [VName] -> InternaliseM (SOAC SOACS)) ->+  (E.Exp, E.Exp, E.Exp, SrcLoc) ->+  InternaliseM [SubExp]+internaliseScanOrReduce desc what f (lam, ne, arr, loc) = do+  arrs <- internaliseExpToVars (what ++ "_arr") arr+  nes <- internaliseExp (what ++ "_ne") ne+  nes' <- forM (zip nes arrs) $ \(ne', arr') -> do+    rowtype <- I.stripArray 1 <$> lookupType arr'+    ensureShape+      "Row shape of input array does not match shape of neutral element"+      loc+      rowtype+      (what ++ "_ne_right_shape")+      ne'+  nests <- mapM I.subExpType nes'+  arrts <- mapM lookupType arrs+  lam' <- internaliseFoldLambda internaliseLambda lam nests arrts+  w <- arraysSize 0 <$> mapM lookupType arrs+  letTupExp' desc . I.Op =<< f w lam' nes' arrs++internaliseHist ::+  String ->+  E.Exp ->+  E.Exp ->+  E.Exp ->+  E.Exp ->+  E.Exp ->+  E.Exp ->+  SrcLoc ->+  InternaliseM [SubExp]+internaliseHist desc rf hist op ne buckets img loc = do+  rf' <- internaliseExp1 "hist_rf" rf+  ne' <- internaliseExp "hist_ne" ne+  hist' <- internaliseExpToVars "hist_hist" hist+  buckets' <-+    letExp "hist_buckets" . BasicOp . SubExp+      =<< internaliseExp1 "hist_buckets" buckets+  img' <- internaliseExpToVars "hist_img" img++  -- reshape neutral element to have same size as the destination array+  ne_shp <- forM (zip ne' hist') $ \(n, h) -> do+    rowtype <- I.stripArray 1 <$> lookupType h+    ensureShape+      "Row shape of destination array does not match shape of neutral element"+      loc+      rowtype+      "hist_ne_right_shape"+      n+  ne_ts <- mapM I.subExpType ne_shp+  his_ts <- mapM lookupType hist'+  op' <- internaliseFoldLambda internaliseLambda op ne_ts his_ts++  -- reshape return type of bucket function to have same size as neutral element+  -- (modulo the index)+  bucket_param <- newParam "bucket_p" $ I.Prim int64+  img_params <- mapM (newParam "img_p" . rowType) =<< mapM lookupType img'+  let params = bucket_param : img_params+      rettype = I.Prim int64 : ne_ts+      body = mkBody mempty $ map (I.Var . paramName) params+  body' <-+    localScope (scopeOfLParams params) $+      ensureResultShape+        "Row shape of value array does not match row shape of hist target"+        (srclocOf img)+        rettype+        body++  -- get sizes of histogram and image arrays+  w_hist <- arraysSize 0 <$> mapM lookupType hist'+  w_img <- arraysSize 0 <$> mapM lookupType img'++  -- Generate an assertion and reshapes to ensure that buckets' and+  -- img' are the same size.+  b_shape <- I.arrayShape <$> lookupType buckets'+  let b_w = shapeSize 0 b_shape+  cmp <- letSubExp "bucket_cmp" $ I.BasicOp $ I.CmpOp (I.CmpEq I.int64) b_w w_img+  c <-+    assert+      "bucket_cert"+      cmp+      "length of index and value array does not match"+      loc+  buckets'' <-+    certifying c $+      letExp (baseString buckets') $+        I.BasicOp $ I.Reshape (reshapeOuter [DimCoercion w_img] 1 b_shape) buckets'++  letTupExp' desc $+    I.Op $+      I.Hist w_img [HistOp w_hist rf' hist' ne_shp op'] (I.Lambda params body' rettype) $ buckets'' : img'++internaliseStreamMap ::+  String ->+  StreamOrd ->+  E.Exp ->+  E.Exp ->+  InternaliseM [SubExp]+internaliseStreamMap desc o lam arr = do+  arrs <- internaliseExpToVars "stream_input" arr+  lam' <- internaliseStreamMapLambda internaliseLambda lam $ map I.Var arrs+  w <- arraysSize 0 <$> mapM lookupType arrs+  let form = I.Parallel o Commutative (I.Lambda [] (mkBody mempty []) []) []+  letTupExp' desc $ I.Op $ I.Stream w form lam' arrs++internaliseStreamRed ::+  String ->+  StreamOrd ->+  Commutativity ->+  E.Exp ->+  E.Exp ->+  E.Exp ->+  InternaliseM [SubExp]+internaliseStreamRed desc o comm lam0 lam arr = do+  arrs <- internaliseExpToVars "stream_input" arr+  rowts <- mapM (fmap I.rowType . lookupType) arrs+  (lam_params, lam_body) <-+    internaliseStreamLambda internaliseLambda lam rowts+  let (chunk_param, _, lam_val_params) =+        partitionChunkedFoldParameters 0 lam_params++  -- Synthesize neutral elements by applying the fold function+  -- to an empty chunk.+  letBindNames [I.paramName chunk_param] $+    I.BasicOp $ I.SubExp $ constant (0 :: Int64)+  forM_ lam_val_params $ \p ->+    letBindNames [I.paramName p] $+      I.BasicOp $+        I.Scratch (I.elemType $ I.paramType p) $+          I.arrayDims $ I.paramType p+  nes <- bodyBind =<< renameBody lam_body++  nes_ts <- mapM I.subExpType nes+  outsz <- arraysSize 0 <$> mapM lookupType arrs+  let acc_arr_tps = [I.arrayOf t (I.Shape [outsz]) NoUniqueness | t <- nes_ts]+  lam0' <- internaliseFoldLambda internaliseLambda lam0 nes_ts acc_arr_tps++  let lam0_acc_params = take (length nes) $ I.lambdaParams lam0'+  lam_acc_params <- forM lam0_acc_params $ \p -> do+    name <- newVName $ baseString $ I.paramName p+    return p {I.paramName = name}++  -- Make sure the chunk size parameter comes first.+  let lam_params' = chunk_param : lam_acc_params ++ lam_val_params++  body_with_lam0 <-+    ensureResultShape+      "shape of result does not match shape of initial value"+      (srclocOf lam0)+      nes_ts+      <=< insertStmsM+      $ localScope (scopeOfLParams lam_params') $ do+        lam_res <- bodyBind lam_body+        lam_res' <-+          ensureArgShapes+            "shape of chunk function result does not match shape of initial value"+            (srclocOf lam)+            []+            (map I.typeOf $ I.lambdaParams lam0')+            lam_res+        new_lam_res <-+          eLambda lam0' $+            map eSubExp $+              map (I.Var . paramName) lam_acc_params ++ lam_res'+        return $ resultBody new_lam_res++  let form = I.Parallel o comm lam0' nes+      lam' =+        I.Lambda+          { lambdaParams = lam_params',+            lambdaBody = body_with_lam0,+            lambdaReturnType = nes_ts+          }+  w <- arraysSize 0 <$> mapM lookupType arrs+  letTupExp' desc $ I.Op $ I.Stream w form lam' arrs++internaliseExp1 :: String -> E.Exp -> InternaliseM I.SubExp+internaliseExp1 desc e = do+  vs <- internaliseExp desc e+  case vs of+    [se] -> return se+    _ -> error "Internalise.internaliseExp1: was passed not just a single subexpression"++-- | Promote to dimension type as appropriate for the original type.+-- Also return original type.+internaliseDimExp :: String -> E.Exp -> InternaliseM (I.SubExp, IntType)+internaliseDimExp s e = do+  e' <- internaliseExp1 s e+  case E.typeOf e of+    E.Scalar (E.Prim (Signed it)) -> (,it) <$> asIntS Int64 e'+    _ -> error "internaliseDimExp: bad type"++internaliseExpToVars :: String -> E.Exp -> InternaliseM [I.VName]+internaliseExpToVars desc e =+  mapM asIdent =<< internaliseExp desc e+  where+    asIdent (I.Var v) = return v+    asIdent se = letExp desc $ I.BasicOp $ I.SubExp se++internaliseOperation ::+  String ->+  E.Exp ->+  (I.VName -> InternaliseM I.BasicOp) ->+  InternaliseM [I.SubExp]+internaliseOperation s e op = do+  vs <- internaliseExpToVars s e+  letSubExps s =<< mapM (fmap I.BasicOp . op) vs++certifyingNonzero ::+  SrcLoc ->+  IntType ->+  SubExp ->+  InternaliseM a ->+  InternaliseM a+certifyingNonzero loc t x m = do+  zero <-+    letSubExp "zero" $+      I.BasicOp $+        CmpOp (CmpEq (IntType t)) x (intConst t 0)+  nonzero <- letSubExp "nonzero" $ I.BasicOp $ UnOp Not zero+  c <- assert "nonzero_cert" nonzero "division by zero" loc+  certifying c m++certifyingNonnegative ::+  SrcLoc ->+  IntType ->+  SubExp ->+  InternaliseM a ->+  InternaliseM a+certifyingNonnegative loc t x m = do+  nonnegative <-+    letSubExp "nonnegative" $+      I.BasicOp $+        CmpOp (CmpSle t) (intConst t 0) x+  c <- assert "nonzero_cert" nonnegative "negative exponent" loc+  certifying c m++internaliseBinOp ::+  SrcLoc ->+  String ->+  E.BinOp ->+  I.SubExp ->+  I.SubExp ->+  E.PrimType ->+  E.PrimType ->+  InternaliseM [I.SubExp]+internaliseBinOp _ desc E.Plus x y (E.Signed t) _ =+  simpleBinOp desc (I.Add t I.OverflowWrap) x y+internaliseBinOp _ desc E.Plus x y (E.Unsigned t) _ =+  simpleBinOp desc (I.Add t I.OverflowWrap) x y+internaliseBinOp _ desc E.Plus x y (E.FloatType t) _ =+  simpleBinOp desc (I.FAdd t) x y+internaliseBinOp _ desc E.Minus x y (E.Signed t) _ =+  simpleBinOp desc (I.Sub t I.OverflowWrap) x y+internaliseBinOp _ desc E.Minus x y (E.Unsigned t) _ =+  simpleBinOp desc (I.Sub t I.OverflowWrap) x y+internaliseBinOp _ desc E.Minus x y (E.FloatType t) _ =+  simpleBinOp desc (I.FSub t) x y+internaliseBinOp _ desc E.Times x y (E.Signed t) _ =+  simpleBinOp desc (I.Mul t I.OverflowWrap) x y+internaliseBinOp _ desc E.Times x y (E.Unsigned t) _ =+  simpleBinOp desc (I.Mul t I.OverflowWrap) x y+internaliseBinOp _ desc E.Times x y (E.FloatType t) _ =+  simpleBinOp desc (I.FMul t) x y+internaliseBinOp loc desc E.Divide x y (E.Signed t) _ =+  certifyingNonzero loc t y $+    simpleBinOp desc (I.SDiv t I.Unsafe) x y+internaliseBinOp loc desc E.Divide x y (E.Unsigned t) _ =+  certifyingNonzero loc t y $+    simpleBinOp desc (I.UDiv t I.Unsafe) x y+internaliseBinOp _ desc E.Divide x y (E.FloatType t) _ =+  simpleBinOp desc (I.FDiv t) x y+internaliseBinOp _ desc E.Pow x y (E.FloatType t) _ =+  simpleBinOp desc (I.FPow t) x y+internaliseBinOp loc desc E.Pow x y (E.Signed t) _ =+  certifyingNonnegative loc t y $+    simpleBinOp desc (I.Pow t) x y+internaliseBinOp _ desc E.Pow x y (E.Unsigned t) _ =+  simpleBinOp desc (I.Pow t) x y+internaliseBinOp loc desc E.Mod x y (E.Signed t) _ =+  certifyingNonzero loc t y $+    simpleBinOp desc (I.SMod t I.Unsafe) x y+internaliseBinOp loc desc E.Mod x y (E.Unsigned t) _ =+  certifyingNonzero loc t y $+    simpleBinOp desc (I.UMod t I.Unsafe) x y+internaliseBinOp _ desc E.Mod x y (E.FloatType t) _ =+  simpleBinOp desc (I.FMod t) x y+internaliseBinOp loc desc E.Quot x y (E.Signed t) _ =+  certifyingNonzero loc t y $+    simpleBinOp desc (I.SQuot t I.Unsafe) x y+internaliseBinOp loc desc E.Quot x y (E.Unsigned t) _ =+  certifyingNonzero loc t y $+    simpleBinOp desc (I.UDiv t I.Unsafe) x y+internaliseBinOp loc desc E.Rem x y (E.Signed t) _ =+  certifyingNonzero loc t y $+    simpleBinOp desc (I.SRem t I.Unsafe) x y+internaliseBinOp loc desc E.Rem x y (E.Unsigned t) _ =+  certifyingNonzero loc t y $+    simpleBinOp desc (I.UMod t I.Unsafe) x y+internaliseBinOp _ desc E.ShiftR x y (E.Signed t) _ =+  simpleBinOp desc (I.AShr t) x y+internaliseBinOp _ desc E.ShiftR x y (E.Unsigned t) _ =+  simpleBinOp desc (I.LShr t) x y+internaliseBinOp _ desc E.ShiftL x y (E.Signed t) _ =+  simpleBinOp desc (I.Shl t) x y+internaliseBinOp _ desc E.ShiftL x y (E.Unsigned t) _ =+  simpleBinOp desc (I.Shl t) x y+internaliseBinOp _ desc E.Band x y (E.Signed t) _ =+  simpleBinOp desc (I.And t) x y+internaliseBinOp _ desc E.Band x y (E.Unsigned t) _ =+  simpleBinOp desc (I.And t) x y+internaliseBinOp _ desc E.Xor x y (E.Signed t) _ =+  simpleBinOp desc (I.Xor t) x y+internaliseBinOp _ desc E.Xor x y (E.Unsigned t) _ =+  simpleBinOp desc (I.Xor t) x y+internaliseBinOp _ desc E.Bor x y (E.Signed t) _ =+  simpleBinOp desc (I.Or t) x y+internaliseBinOp _ desc E.Bor x y (E.Unsigned t) _ =+  simpleBinOp desc (I.Or t) x y+internaliseBinOp _ desc E.Equal x y t _ =+  simpleCmpOp desc (I.CmpEq $ internalisePrimType t) x y+internaliseBinOp _ desc E.NotEqual x y t _ = do+  eq <- letSubExp (desc ++ "true") $ I.BasicOp $ I.CmpOp (I.CmpEq $ internalisePrimType t) x y+  fmap pure $ letSubExp desc $ I.BasicOp $ I.UnOp I.Not eq+internaliseBinOp _ desc E.Less x y (E.Signed t) _ =+  simpleCmpOp desc (I.CmpSlt t) x y+internaliseBinOp _ desc E.Less x y (E.Unsigned t) _ =+  simpleCmpOp desc (I.CmpUlt t) x y+internaliseBinOp _ desc E.Leq x y (E.Signed t) _ =+  simpleCmpOp desc (I.CmpSle t) x y+internaliseBinOp _ desc E.Leq x y (E.Unsigned t) _ =+  simpleCmpOp desc (I.CmpUle t) x y+internaliseBinOp _ desc E.Greater x y (E.Signed t) _ =+  simpleCmpOp desc (I.CmpSlt t) y x -- Note the swapped x and y+internaliseBinOp _ desc E.Greater x y (E.Unsigned t) _ =+  simpleCmpOp desc (I.CmpUlt t) y x -- Note the swapped x and y+internaliseBinOp _ desc E.Geq x y (E.Signed t) _ =+  simpleCmpOp desc (I.CmpSle t) y x -- Note the swapped x and y+internaliseBinOp _ desc E.Geq x y (E.Unsigned t) _ =+  simpleCmpOp desc (I.CmpUle t) y x -- Note the swapped x and y+internaliseBinOp _ desc E.Less x y (E.FloatType t) _ =+  simpleCmpOp desc (I.FCmpLt t) x y+internaliseBinOp _ desc E.Leq x y (E.FloatType t) _ =+  simpleCmpOp desc (I.FCmpLe t) x y+internaliseBinOp _ desc E.Greater x y (E.FloatType t) _ =+  simpleCmpOp desc (I.FCmpLt t) y x -- Note the swapped x and y+internaliseBinOp _ desc E.Geq x y (E.FloatType t) _ =+  simpleCmpOp desc (I.FCmpLe t) y x -- Note the swapped x and y++-- Relational operators for booleans.+internaliseBinOp _ desc E.Less x y E.Bool _ =+  simpleCmpOp desc I.CmpLlt x y+internaliseBinOp _ desc E.Leq x y E.Bool _ =+  simpleCmpOp desc I.CmpLle x y+internaliseBinOp _ desc E.Greater x y E.Bool _ =+  simpleCmpOp desc I.CmpLlt y x -- Note the swapped x and y+internaliseBinOp _ desc E.Geq x y E.Bool _ =+  simpleCmpOp desc I.CmpLle y x -- Note the swapped x and y+internaliseBinOp _ _ op _ _ t1 t2 =+  error $+    "Invalid binary operator " ++ pretty op+      ++ " with operand types "+      ++ pretty t1+      ++ ", "+      ++ pretty t2++simpleBinOp ::+  String ->+  I.BinOp ->+  I.SubExp ->+  I.SubExp ->+  InternaliseM [I.SubExp]+simpleBinOp desc bop x y =+  letTupExp' desc $ I.BasicOp $ I.BinOp bop x y++simpleCmpOp ::+  String ->+  I.CmpOp ->+  I.SubExp ->+  I.SubExp ->+  InternaliseM [I.SubExp]+simpleCmpOp desc op x y =+  letTupExp' desc $ I.BasicOp $ I.CmpOp op x y++findFuncall ::+  E.Exp ->+  InternaliseM+    ( E.QualName VName,+      [(E.Exp, Maybe VName)],+      E.StructType,+      [VName]+    )+findFuncall (E.Var fname (Info t) _) =+  return (fname, [], E.toStruct t, [])+findFuncall (E.Apply f arg (Info (_, argext)) (Info ret, Info retext) _) = do+  (fname, args, _, _) <- findFuncall f+  return (fname, args ++ [(arg, argext)], E.toStruct ret, retext)+findFuncall e =+  error $ "Invalid function expression in application: " ++ pretty e++internaliseLambda :: InternaliseLambda+internaliseLambda (E.Parens e _) rowtypes =+  internaliseLambda e rowtypes+internaliseLambda (E.Lambda params body _ (Info (_, rettype)) _) rowtypes =+  bindingLambdaParams params rowtypes $ \params' -> do+    body' <- internaliseBody body+    rettype' <- internaliseLambdaReturnType rettype+    return (params', body', rettype')+internaliseLambda e _ = error $ "internaliseLambda: unexpected expression:\n" ++ pretty e++-- | Some operators and functions are overloaded or otherwise special+-- - we detect and treat them here.+isOverloadedFunction ::+  E.QualName VName ->+  [E.Exp] ->+  SrcLoc ->+  Maybe (String -> InternaliseM [SubExp])+isOverloadedFunction qname args loc = do+  guard $ baseTag (qualLeaf qname) <= maxIntrinsicTag+  let handlers =+        [ handleSign,+          handleIntrinsicOps,+          handleOps,+          handleSOACs,+          handleRest+        ]+  msum [h args $ baseString $ qualLeaf qname | h <- handlers]+  where+    handleSign [x] "sign_i8" = Just $ toSigned I.Int8 x+    handleSign [x] "sign_i16" = Just $ toSigned I.Int16 x+    handleSign [x] "sign_i32" = Just $ toSigned I.Int32 x+    handleSign [x] "sign_i64" = Just $ toSigned I.Int64 x+    handleSign [x] "unsign_i8" = Just $ toUnsigned I.Int8 x+    handleSign [x] "unsign_i16" = Just $ toUnsigned I.Int16 x+    handleSign [x] "unsign_i32" = Just $ toUnsigned I.Int32 x+    handleSign [x] "unsign_i64" = Just $ toUnsigned I.Int64 x+    handleSign _ _ = Nothing++    handleIntrinsicOps [x] s+      | Just unop <- find ((== s) . pretty) allUnOps = Just $ \desc -> do+        x' <- internaliseExp1 "x" x+        fmap pure $ letSubExp desc $ I.BasicOp $ I.UnOp unop x'+    handleIntrinsicOps [TupLit [x, y] _] s+      | Just bop <- find ((== s) . pretty) allBinOps = Just $ \desc -> do+        x' <- internaliseExp1 "x" x+        y' <- internaliseExp1 "y" y+        fmap pure $ letSubExp desc $ I.BasicOp $ I.BinOp bop x' y'+      | Just cmp <- find ((== s) . pretty) allCmpOps = Just $ \desc -> do+        x' <- internaliseExp1 "x" x+        y' <- internaliseExp1 "y" y+        fmap pure $ letSubExp desc $ I.BasicOp $ I.CmpOp cmp x' y'+    handleIntrinsicOps [x] s+      | Just conv <- find ((== s) . pretty) allConvOps = Just $ \desc -> do+        x' <- internaliseExp1 "x" x+        fmap pure $ letSubExp desc $ I.BasicOp $ I.ConvOp conv x'+    handleIntrinsicOps _ _ = Nothing++    -- Short-circuiting operators are magical.+    handleOps [x, y] "&&" = Just $ \desc ->+      internaliseExp desc $+        E.If x y (E.Literal (E.BoolValue False) mempty) (Info $ E.Scalar $ E.Prim E.Bool, Info []) mempty+    handleOps [x, y] "||" = Just $ \desc ->+      internaliseExp desc $+        E.If x (E.Literal (E.BoolValue True) mempty) y (Info $ E.Scalar $ E.Prim E.Bool, Info []) mempty+    -- Handle equality and inequality specially, to treat the case of+    -- arrays.+    handleOps [xe, ye] op+      | Just cmp_f <- isEqlOp op = Just $ \desc -> do+        xe' <- internaliseExp "x" xe+        ye' <- internaliseExp "y" ye+        rs <- zipWithM (doComparison desc) xe' ye'+        cmp_f desc =<< letSubExp "eq" =<< eAll rs+      where+        isEqlOp "!=" = Just $ \desc eq ->+          letTupExp' desc $ I.BasicOp $ I.UnOp I.Not eq+        isEqlOp "==" = Just $ \_ eq ->+          return [eq]+        isEqlOp _ = Nothing++        doComparison desc x y = do+          x_t <- I.subExpType x+          y_t <- I.subExpType y+          case x_t of+            I.Prim t -> letSubExp desc $ I.BasicOp $ I.CmpOp (I.CmpEq t) x y+            _ -> do+              let x_dims = I.arrayDims x_t+                  y_dims = I.arrayDims y_t+              dims_match <- forM (zip x_dims y_dims) $ \(x_dim, y_dim) ->+                letSubExp "dim_eq" $ I.BasicOp $ I.CmpOp (I.CmpEq int64) x_dim y_dim+              shapes_match <- letSubExp "shapes_match" =<< eAll dims_match+              compare_elems_body <- runBodyBinder $ do+                -- Flatten both x and y.+                x_num_elems <-+                  letSubExp "x_num_elems"+                    =<< foldBinOp (I.Mul Int64 I.OverflowUndef) (constant (1 :: Int64)) x_dims+                x' <- letExp "x" $ I.BasicOp $ I.SubExp x+                y' <- letExp "x" $ I.BasicOp $ I.SubExp y+                x_flat <- letExp "x_flat" $ I.BasicOp $ I.Reshape [I.DimNew x_num_elems] x'+                y_flat <- letExp "y_flat" $ I.BasicOp $ I.Reshape [I.DimNew x_num_elems] y'++                -- Compare the elements.+                cmp_lam <- cmpOpLambda $ I.CmpEq (elemType x_t)+                cmps <-+                  letExp "cmps" $+                    I.Op $+                      I.Screma x_num_elems (I.mapSOAC cmp_lam) [x_flat, y_flat]++                -- Check that all were equal.+                and_lam <- binOpLambda I.LogAnd I.Bool+                reduce <- I.reduceSOAC [Reduce Commutative and_lam [constant True]]+                all_equal <- letSubExp "all_equal" $ I.Op $ I.Screma x_num_elems reduce [cmps]+                return $ resultBody [all_equal]++              letSubExp "arrays_equal" $+                I.If shapes_match compare_elems_body (resultBody [constant False]) $+                  ifCommon [I.Prim I.Bool]+    handleOps [x, y] name+      | Just bop <- find ((name ==) . pretty) [minBound .. maxBound :: E.BinOp] =+        Just $ \desc -> do+          x' <- internaliseExp1 "x" x+          y' <- internaliseExp1 "y" y+          case (E.typeOf x, E.typeOf y) of+            (E.Scalar (E.Prim t1), E.Scalar (E.Prim t2)) ->+              internaliseBinOp loc desc bop x' y' t1 t2+            _ -> error "Futhark.Internalise.internaliseExp: non-primitive type in BinOp."+    handleOps _ _ = Nothing++    handleSOACs [TupLit [lam, arr] _] "map" = Just $ \desc -> do+      arr' <- internaliseExpToVars "map_arr" arr+      lam' <- internaliseMapLambda internaliseLambda lam $ map I.Var arr'+      w <- arraysSize 0 <$> mapM lookupType arr'+      letTupExp' desc $+        I.Op $+          I.Screma w (I.mapSOAC lam') arr'+    handleSOACs [TupLit [k, lam, arr] _] "partition" = do+      k' <- fromIntegral <$> fromInt32 k+      Just $ \_desc -> do+        arrs <- internaliseExpToVars "partition_input" arr+        lam' <- internalisePartitionLambda internaliseLambda k' lam $ map I.Var arrs+        uncurry (++) <$> partitionWithSOACS (fromIntegral k') lam' arrs+      where+        fromInt32 (Literal (SignedValue (Int32Value k')) _) = Just k'+        fromInt32 (IntLit k' (Info (E.Scalar (E.Prim (Signed Int32)))) _) = Just $ fromInteger k'+        fromInt32 _ = Nothing+    handleSOACs [TupLit [lam, ne, arr] _] "reduce" = Just $ \desc ->+      internaliseScanOrReduce desc "reduce" reduce (lam, ne, arr, loc)+      where+        reduce w red_lam nes arrs =+          I.Screma w+            <$> I.reduceSOAC [Reduce Noncommutative red_lam nes] <*> pure arrs+    handleSOACs [TupLit [lam, ne, arr] _] "reduce_comm" = Just $ \desc ->+      internaliseScanOrReduce desc "reduce" reduce (lam, ne, arr, loc)+      where+        reduce w red_lam nes arrs =+          I.Screma w+            <$> I.reduceSOAC [Reduce Commutative red_lam nes] <*> pure arrs+    handleSOACs [TupLit [lam, ne, arr] _] "scan" = Just $ \desc ->+      internaliseScanOrReduce desc "scan" reduce (lam, ne, arr, loc)+      where+        reduce w scan_lam nes arrs =+          I.Screma w <$> I.scanSOAC [Scan scan_lam nes] <*> pure arrs+    handleSOACs [TupLit [op, f, arr] _] "reduce_stream" = Just $ \desc ->+      internaliseStreamRed desc InOrder Noncommutative op f arr+    handleSOACs [TupLit [op, f, arr] _] "reduce_stream_per" = Just $ \desc ->+      internaliseStreamRed desc Disorder Commutative op f arr+    handleSOACs [TupLit [f, arr] _] "map_stream" = Just $ \desc ->+      internaliseStreamMap desc InOrder f arr+    handleSOACs [TupLit [f, arr] _] "map_stream_per" = Just $ \desc ->+      internaliseStreamMap desc Disorder f arr+    handleSOACs [TupLit [rf, dest, op, ne, buckets, img] _] "hist" = Just $ \desc ->+      internaliseHist desc rf dest op ne buckets img loc+    handleSOACs _ _ = Nothing++    handleRest [x] "!" = Just $ complementF x+    handleRest [x] "opaque" = Just $ \desc ->+      mapM (letSubExp desc . BasicOp . Opaque) =<< internaliseExp "opaque_arg" x+    handleRest [E.TupLit [a, si, v] _] "scatter" = Just $ scatterF a si v+    handleRest [E.TupLit [n, m, arr] _] "unflatten" = Just $ \desc -> do+      arrs <- internaliseExpToVars "unflatten_arr" arr+      n' <- internaliseExp1 "n" n+      m' <- internaliseExp1 "m" m+      -- The unflattened dimension needs to have the same number of elements+      -- as the original dimension.+      old_dim <- I.arraysSize 0 <$> mapM lookupType arrs+      dim_ok <-+        letSubExp "dim_ok"+          =<< eCmpOp+            (I.CmpEq I.int64)+            (eBinOp (I.Mul Int64 I.OverflowUndef) (eSubExp n') (eSubExp m'))+            (eSubExp old_dim)+      dim_ok_cert <-+        assert+          "dim_ok_cert"+          dim_ok+          "new shape has different number of elements than old shape"+          loc+      certifying dim_ok_cert $+        forM arrs $ \arr' -> do+          arr_t <- lookupType arr'+          letSubExp desc $+            I.BasicOp $+              I.Reshape (reshapeOuter [DimNew n', DimNew m'] 1 $ I.arrayShape arr_t) arr'+    handleRest [arr] "flatten" = Just $ \desc -> do+      arrs <- internaliseExpToVars "flatten_arr" arr+      forM arrs $ \arr' -> do+        arr_t <- lookupType arr'+        let n = arraySize 0 arr_t+            m = arraySize 1 arr_t+        k <- letSubExp "flat_dim" $ I.BasicOp $ I.BinOp (Mul Int64 I.OverflowUndef) n m+        letSubExp desc $+          I.BasicOp $+            I.Reshape (reshapeOuter [DimNew k] 2 $ I.arrayShape arr_t) arr'+    handleRest [TupLit [x, y] _] "concat" = Just $ \desc -> do+      xs <- internaliseExpToVars "concat_x" x+      ys <- internaliseExpToVars "concat_y" y+      outer_size <- arraysSize 0 <$> mapM lookupType xs+      let sumdims xsize ysize =+            letSubExp "conc_tmp" $+              I.BasicOp $+                I.BinOp (I.Add I.Int64 I.OverflowUndef) xsize ysize+      ressize <-+        foldM sumdims outer_size+          =<< mapM (fmap (arraysSize 0) . mapM lookupType) [ys]++      let conc xarr yarr =+            I.BasicOp $ I.Concat 0 xarr [yarr] ressize+      letSubExps desc $ zipWith conc xs ys+    handleRest [TupLit [offset, e] _] "rotate" = Just $ \desc -> do+      offset' <- internaliseExp1 "rotation_offset" offset+      internaliseOperation desc e $ \v -> do+        r <- I.arrayRank <$> lookupType v+        let zero = intConst Int64 0+            offsets = offset' : replicate (r -1) zero+        return $ I.Rotate offsets v+    handleRest [e] "transpose" = Just $ \desc ->+      internaliseOperation desc e $ \v -> do+        r <- I.arrayRank <$> lookupType v+        return $ I.Rearrange ([1, 0] ++ [2 .. r -1]) v+    handleRest [TupLit [x, y] _] "zip" = Just $ \desc ->+      (++) <$> internaliseExp (desc ++ "_zip_x") x+        <*> internaliseExp (desc ++ "_zip_y") y+    handleRest [x] "unzip" = Just $ flip internaliseExp x+    handleRest [x] "trace" = Just $ flip internaliseExp x+    handleRest [x] "break" = Just $ flip internaliseExp x+    handleRest _ _ = Nothing++    toSigned int_to e desc = do+      e' <- internaliseExp1 "trunc_arg" e+      case E.typeOf e of+        E.Scalar (E.Prim E.Bool) ->+          letTupExp' desc $+            I.If+              e'+              (resultBody [intConst int_to 1])+              (resultBody [intConst int_to 0])+              $ ifCommon [I.Prim $ I.IntType int_to]+        E.Scalar (E.Prim (E.Signed int_from)) ->+          letTupExp' desc $ I.BasicOp $ I.ConvOp (I.SExt int_from int_to) e'+        E.Scalar (E.Prim (E.Unsigned int_from)) ->+          letTupExp' desc $ I.BasicOp $ I.ConvOp (I.ZExt int_from int_to) e'+        E.Scalar (E.Prim (E.FloatType float_from)) ->+          letTupExp' desc $ I.BasicOp $ I.ConvOp (I.FPToSI float_from int_to) e'+        _ -> error "Futhark.Internalise: non-numeric type in ToSigned"++    toUnsigned int_to e desc = do+      e' <- internaliseExp1 "trunc_arg" e+      case E.typeOf e of+        E.Scalar (E.Prim E.Bool) ->+          letTupExp' desc $+            I.If+              e'+              (resultBody [intConst int_to 1])+              (resultBody [intConst int_to 0])+              $ ifCommon [I.Prim $ I.IntType int_to]+        E.Scalar (E.Prim (E.Signed int_from)) ->+          letTupExp' desc $ I.BasicOp $ I.ConvOp (I.ZExt int_from int_to) e'+        E.Scalar (E.Prim (E.Unsigned int_from)) ->+          letTupExp' desc $ I.BasicOp $ I.ConvOp (I.ZExt int_from int_to) e'+        E.Scalar (E.Prim (E.FloatType float_from)) ->+          letTupExp' desc $ I.BasicOp $ I.ConvOp (I.FPToUI float_from int_to) e'+        _ -> error "Futhark.Internalise.internaliseExp: non-numeric type in ToUnsigned"++    complementF e desc = do+      e' <- internaliseExp1 "complement_arg" e+      et <- subExpType e'+      case et of+        I.Prim (I.IntType t) ->+          letTupExp' desc $ I.BasicOp $ I.UnOp (I.Complement t) e'+        I.Prim I.Bool ->+          letTupExp' desc $ I.BasicOp $ I.UnOp I.Not e'+        _ ->+          error "Futhark.Internalise.internaliseExp: non-int/bool type in Complement"++    scatterF a si v desc = do+      si' <- letExp "write_si" . BasicOp . SubExp =<< internaliseExp1 "write_arg_i" si+      svs <- internaliseExpToVars "write_arg_v" v+      sas <- internaliseExpToVars "write_arg_a" a++      si_shape <- I.arrayShape <$> lookupType si'+      let si_w = shapeSize 0 si_shape+      sv_ts <- mapM lookupType svs++      svs' <- forM (zip svs sv_ts) $ \(sv, sv_t) -> do+        let sv_shape = I.arrayShape sv_t+            sv_w = arraySize 0 sv_t++        -- Generate an assertion and reshapes to ensure that sv and si' are the same+        -- size.+        cmp <-+          letSubExp "write_cmp" $+            I.BasicOp $+              I.CmpOp (I.CmpEq I.int64) si_w sv_w+        c <-+          assert+            "write_cert"+            cmp+            "length of index and value array does not match"+            loc+        certifying c $+          letExp (baseString sv ++ "_write_sv") $+            I.BasicOp $ I.Reshape (reshapeOuter [DimCoercion si_w] 1 sv_shape) sv++      indexType <- rowType <$> lookupType si'+      indexName <- newVName "write_index"+      valueNames <- replicateM (length sv_ts) $ newVName "write_value"++      sa_ts <- mapM lookupType sas+      let bodyTypes = replicate (length sv_ts) indexType ++ map rowType sa_ts+          paramTypes = indexType : map rowType sv_ts+          bodyNames = indexName : valueNames+          bodyParams = zipWith I.Param bodyNames paramTypes++      -- This body is pretty boring right now, as every input is exactly the output.+      -- But it can get funky later on if fused with something else.+      body <- localScope (scopeOfLParams bodyParams) $+        insertStmsM $ do+          let outs = replicate (length valueNames) indexName ++ valueNames+          results <- forM outs $ \name ->+            letSubExp "write_res" $ I.BasicOp $ I.SubExp $ I.Var name+          ensureResultShape+            "scatter value has wrong size"+            loc+            bodyTypes+            $ resultBody results++      let lam =+            I.Lambda+              { I.lambdaParams = bodyParams,+                I.lambdaReturnType = bodyTypes,+                I.lambdaBody = body+              }+          sivs = si' : svs'++      let sa_ws = map (arraySize 0) sa_ts+      letTupExp' desc $ I.Op $ I.Scatter si_w lam sivs $ zip3 sa_ws (repeat 1) sas++funcall ::+  String ->+  QualName VName ->+  [SubExp] ->+  SrcLoc ->+  InternaliseM ([SubExp], [I.ExtType])+funcall desc (QualName _ fname) args loc = do+  (fname', closure, shapes, value_paramts, fun_params, rettype_fun) <-+    lookupFunction fname+  argts <- mapM subExpType args++  shapeargs <- argShapes shapes fun_params argts+  let diets =+        replicate (length closure + length shapeargs) I.ObservePrim+          ++ map I.diet value_paramts+  args' <-+    ensureArgShapes+      "function arguments of wrong shape"+      loc+      (map I.paramName fun_params)+      (map I.paramType fun_params)+      (map I.Var closure ++ shapeargs ++ args)+  argts' <- mapM subExpType args'+  case rettype_fun $ zip args' argts' of+    Nothing ->+      error $+        "Cannot apply " ++ pretty fname ++ " to arguments\n "+          ++ pretty args'+          ++ "\nof types\n "+          ++ pretty argts'+          ++ "\nFunction has parameters\n "+          ++ pretty fun_params+    Just ts -> do+      safety <- askSafety+      attrs <- asks envAttrs+      ses <-+        attributing attrs $+          letTupExp' desc $+            I.Apply fname' (zip args' diets) ts (safety, loc, mempty)+      return (ses, map I.fromDecl ts)++-- Bind existential names defined by an expression, based on the+-- concrete values that expression evaluated to.  This most+-- importantly should be done after function calls, but also+-- everything else that can produce existentials in the source+-- language.+bindExtSizes :: E.StructType -> [VName] -> [SubExp] -> InternaliseM ()+bindExtSizes ret retext ses = do+  ts <- internaliseType ret+  ses_ts <- mapM subExpType ses++  let combine t1 t2 =+        mconcat $ zipWith combine' (arrayExtDims t1) (arrayDims t2)+      combine' (I.Free (I.Var v)) se+        | v `elem` retext = M.singleton v se+      combine' _ _ = mempty++  forM_ (M.toList $ mconcat $ zipWith combine ts ses_ts) $ \(v, se) ->+    letBindNames [v] $ BasicOp $ SubExp se++askSafety :: InternaliseM Safety+askSafety = do+  check <- asks envDoBoundsChecks+  return $ if check then I.Safe else I.Unsafe++-- Implement partitioning using maps, scans and writes.+partitionWithSOACS :: Int -> I.Lambda -> [I.VName] -> InternaliseM ([I.SubExp], [I.SubExp])+partitionWithSOACS k lam arrs = do+  arr_ts <- mapM lookupType arrs+  let w = arraysSize 0 arr_ts+  classes_and_increments <- letTupExp "increments" $ I.Op $ I.Screma w (mapSOAC lam) arrs+  (classes, increments) <- case classes_and_increments of+    classes : increments -> return (classes, take k increments)+    _ -> error "partitionWithSOACS"++  add_lam_x_params <-+    replicateM k $ I.Param <$> newVName "x" <*> pure (I.Prim int64)+  add_lam_y_params <-+    replicateM k $ I.Param <$> newVName "y" <*> pure (I.Prim int64)+  add_lam_body <- runBodyBinder $+    localScope (scopeOfLParams $ add_lam_x_params ++ add_lam_y_params) $+      fmap resultBody $+        forM (zip add_lam_x_params add_lam_y_params) $ \(x, y) ->+          letSubExp "z" $+            I.BasicOp $+              I.BinOp+                (I.Add Int64 I.OverflowUndef)+                (I.Var $ I.paramName x)+                (I.Var $ I.paramName y)+  let add_lam =+        I.Lambda+          { I.lambdaBody = add_lam_body,+            I.lambdaParams = add_lam_x_params ++ add_lam_y_params,+            I.lambdaReturnType = replicate k $ I.Prim int64+          }+      nes = replicate (length increments) $ intConst Int64 0++  scan <- I.scanSOAC [I.Scan add_lam nes]+  all_offsets <- letTupExp "offsets" $ I.Op $ I.Screma w scan increments++  -- We have the offsets for each of the partitions, but we also need+  -- the total sizes, which are the last elements in the offests.  We+  -- just have to be careful in case the array is empty.+  last_index <- letSubExp "last_index" $ I.BasicOp $ I.BinOp (I.Sub Int64 OverflowUndef) w $ constant (1 :: Int64)+  nonempty_body <- runBodyBinder $+    fmap resultBody $+      forM all_offsets $ \offset_array ->+        letSubExp "last_offset" $ I.BasicOp $ I.Index offset_array [I.DimFix last_index]+  let empty_body = resultBody $ replicate k $ constant (0 :: Int64)+  is_empty <- letSubExp "is_empty" $ I.BasicOp $ I.CmpOp (CmpEq int64) w $ constant (0 :: Int64)+  sizes <-+    letTupExp "partition_size" $+      I.If is_empty empty_body nonempty_body $+        ifCommon $ replicate k $ I.Prim int64++  -- The total size of all partitions must necessarily be equal to the+  -- size of the input array.++  -- Create scratch arrays for the result.+  blanks <- forM arr_ts $ \arr_t ->+    letExp "partition_dest" $+      I.BasicOp $+        Scratch (elemType arr_t) (w : drop 1 (I.arrayDims arr_t))++  -- Now write into the result.+  write_lam <- do+    c_param <- I.Param <$> newVName "c" <*> pure (I.Prim int64)+    offset_params <- replicateM k $ I.Param <$> newVName "offset" <*> pure (I.Prim int64)+    value_params <- forM arr_ts $ \arr_t ->+      I.Param <$> newVName "v" <*> pure (I.rowType arr_t)+    (offset, offset_stms) <-+      collectStms $+        mkOffsetLambdaBody+          (map I.Var sizes)+          (I.Var $ I.paramName c_param)+          0+          offset_params+    return+      I.Lambda+        { I.lambdaParams = c_param : offset_params ++ value_params,+          I.lambdaReturnType =+            replicate (length arr_ts) (I.Prim int64)+              ++ map I.rowType arr_ts,+          I.lambdaBody =+            mkBody offset_stms $+              replicate (length arr_ts) offset+                ++ map (I.Var . I.paramName) value_params+        }+  results <-+    letTupExp "partition_res" $+      I.Op $+        I.Scatter+          w+          write_lam+          (classes : all_offsets ++ arrs)+          $ zip3 (repeat w) (repeat 1) blanks+  sizes' <-+    letSubExp "partition_sizes" $+      I.BasicOp $+        I.ArrayLit (map I.Var sizes) $ I.Prim int64+  return (map I.Var results, [sizes'])+  where+    mkOffsetLambdaBody ::+      [SubExp] ->+      SubExp ->+      Int ->+      [I.LParam] ->+      InternaliseM SubExp+    mkOffsetLambdaBody _ _ _ [] =+      return $ constant (-1 :: Int64)+    mkOffsetLambdaBody sizes c i (p : ps) = do+      is_this_one <-+        letSubExp "is_this_one" $+          I.BasicOp $+            I.CmpOp (CmpEq int64) c $+              intConst Int64 $ toInteger i+      next_one <- mkOffsetLambdaBody sizes c (i + 1) ps+      this_one <-+        letSubExp "this_offset"+          =<< foldBinOp+            (Add Int64 OverflowUndef)+            (constant (-1 :: Int64))+            (I.Var (I.paramName p) : take i sizes)+      letSubExp "total_res" $+        I.If+          is_this_one+          (resultBody [this_one])+          (resultBody [next_one])+          $ ifCommon [I.Prim int64]++typeExpForError :: E.TypeExp VName -> InternaliseM [ErrorMsgPart SubExp]+typeExpForError (E.TEVar qn _) =+  return [ErrorString $ pretty qn]+typeExpForError (E.TEUnique te _) =+  ("*" :) <$> typeExpForError te+typeExpForError (E.TEArray te d _) = do+  d' <- dimExpForError d+  te' <- typeExpForError te+  return $ ["[", d', "]"] ++ te'+typeExpForError (E.TETuple tes _) = do+  tes' <- mapM typeExpForError tes+  return $ ["("] ++ intercalate [", "] tes' ++ [")"]+typeExpForError (E.TERecord fields _) = do+  fields' <- mapM onField fields+  return $ ["{"] ++ intercalate [", "] fields' ++ ["}"]+  where+    onField (k, te) =+      (ErrorString (pretty k ++ ": ") :) <$> typeExpForError te+typeExpForError (E.TEArrow _ t1 t2 _) = do+  t1' <- typeExpForError t1+  t2' <- typeExpForError t2+  return $ t1' ++ [" -> "] ++ t2'+typeExpForError (E.TEApply t arg _) = do+  t' <- typeExpForError t+  arg' <- case arg of+    TypeArgExpType argt -> typeExpForError argt+    TypeArgExpDim d _ -> pure <$> dimExpForError d+  return $ t' ++ [" "] ++ arg'+typeExpForError (E.TESum cs _) = do+  cs' <- mapM (onClause . snd) cs+  return $ intercalate [" | "] cs'+  where+    onClause c = do+      c' <- mapM typeExpForError c+      return $ intercalate [" "] c'++dimExpForError :: E.DimExp VName -> InternaliseM (ErrorMsgPart SubExp)+dimExpForError (DimExpNamed d _) = do+  substs <- lookupSubst $ E.qualLeaf d+  d' <- case substs of+    Just [v] -> return v+    _ -> return $ I.Var $ E.qualLeaf d+  return $ ErrorInt64 d'+dimExpForError (DimExpConst d _) =+  return $ ErrorString $ pretty d+dimExpForError DimExpAny = return ""++-- A smart constructor that compacts neighbouring literals for easier+-- reading in the IR.+errorMsg :: [ErrorMsgPart a] -> ErrorMsg a+errorMsg = ErrorMsg . compact+  where+    compact [] = []+    compact (ErrorString x : ErrorString y : parts) =+      compact (ErrorString (x ++ y) : parts)+    compact (x : y) = x : compact y
src/Futhark/Internalise/AccurateSizes.hs view
@@ -1,67 +1,87 @@ {-# LANGUAGE FlexibleContexts #-}+ module Futhark.Internalise.AccurateSizes-  ( argShapes-  , ensureResultShape-  , ensureResultExtShape-  , ensureExtShape-  , ensureShape-  , ensureArgShapes+  ( argShapes,+    ensureResultShape,+    ensureResultExtShape,+    ensureExtShape,+    ensureShape,+    ensureArgShapes,   )-  where+where  import Control.Monad-import Data.Maybe import qualified Data.Map.Strict as M-+import Data.Maybe import Futhark.Construct-import Futhark.Internalise.Monad import Futhark.IR.SOACS+import Futhark.Internalise.Monad import Futhark.Util (takeLast) -shapeMapping :: HasScope SOACS m =>-                [FParam] -> [Type]-             -> m (M.Map VName SubExp)+shapeMapping ::+  HasScope SOACS m =>+  [FParam] ->+  [Type] ->+  m (M.Map VName SubExp) shapeMapping all_params value_arg_types =   mconcat <$> zipWithM f value_params value_arg_types-  where value_params = takeLast (length value_arg_types) all_params+  where+    value_params = takeLast (length value_arg_types) all_params -        f (Param _ t1@Array{}) t2@Array{} =-          pure $ M.fromList $ mapMaybe match $ zip (arrayDims t1) (arrayDims t2)-        f _ _ =-          pure mempty+    f (Param _ t1@Array {}) t2@Array {} =+      pure $ M.fromList $ mapMaybe match $ zip (arrayDims t1) (arrayDims t2)+    f _ _ =+      pure mempty -        match (Var v, se) = Just (v, se)-        match _ = Nothing+    match (Var v, se) = Just (v, se)+    match _ = Nothing -argShapes :: (HasScope SOACS m, Monad m) =>-             [VName] -> [FParam] -> [Type] -> m [SubExp]+argShapes ::+  (HasScope SOACS m, Monad m) =>+  [VName] ->+  [FParam] ->+  [Type] ->+  m [SubExp] argShapes shapes all_params valargts = do   mapping <- shapeMapping all_params valargts   let addShape name =         case M.lookup name mapping of           Just se -> se-          _ -> intConst Int32 0 -- FIXME: we only need this because-                                -- the defunctionaliser throws away-                                -- sizes.+          _ -> intConst Int64 0 -- FIXME: we only need this because+          -- the defunctionaliser throws away+          -- sizes.   return $ map addShape shapes -ensureResultShape :: ErrorMsg SubExp -> SrcLoc -> [Type] -> Body-                  -> InternaliseM Body+ensureResultShape ::+  ErrorMsg SubExp ->+  SrcLoc ->+  [Type] ->+  Body ->+  InternaliseM Body ensureResultShape msg loc =   ensureResultExtShape msg loc . staticShapes -ensureResultExtShape :: ErrorMsg SubExp -> SrcLoc -> [ExtType] -> Body-                     -> InternaliseM Body+ensureResultExtShape ::+  ErrorMsg SubExp ->+  SrcLoc ->+  [ExtType] ->+  Body ->+  InternaliseM Body ensureResultExtShape msg loc rettype body =   insertStmsM $ do-    reses <- bodyBind =<<-             ensureResultExtShapeNoCtx msg loc rettype body+    reses <-+      bodyBind+        =<< ensureResultExtShapeNoCtx msg loc rettype body     ts <- mapM subExpType reses     let ctx = extractShapeContext rettype $ map arrayDims ts     mkBodyM mempty $ ctx ++ reses -ensureResultExtShapeNoCtx :: ErrorMsg SubExp -> SrcLoc -> [ExtType] -> Body-                          -> InternaliseM Body+ensureResultExtShapeNoCtx ::+  ErrorMsg SubExp ->+  SrcLoc ->+  [ExtType] ->+  Body ->+  InternaliseM Body ensureResultExtShapeNoCtx msg loc rettype body =   insertStmsM $ do     es <- bodyBind body@@ -70,38 +90,60 @@         rettype' = foldr (uncurry fixExt) rettype $ M.toList ext_mapping         assertProperShape t se =           let name = "result_proper_shape"-          in ensureExtShape msg loc t name se+           in ensureExtShape msg loc t name se     resultBodyM =<< zipWithM assertProperShape rettype' es -ensureExtShape :: ErrorMsg SubExp -> SrcLoc -> ExtType -> String -> SubExp-               -> InternaliseM SubExp+ensureExtShape ::+  ErrorMsg SubExp ->+  SrcLoc ->+  ExtType ->+  String ->+  SubExp ->+  InternaliseM SubExp ensureExtShape msg loc t name orig-  | Array{} <- t, Var v <- orig =+  | Array {} <- t,+    Var v <- orig =     Var <$> ensureShapeVar msg loc t name v   | otherwise = return orig -ensureShape :: ErrorMsg SubExp -> SrcLoc -> Type -> String -> SubExp-            -> InternaliseM SubExp+ensureShape ::+  ErrorMsg SubExp ->+  SrcLoc ->+  Type ->+  String ->+  SubExp ->+  InternaliseM SubExp ensureShape msg loc = ensureExtShape msg loc . staticShapes1  -- | Reshape the arguments to a function so that they fit the expected -- shape declarations.  Not used to change rank of arguments.  Assumes -- everything is otherwise type-correct.-ensureArgShapes :: (Typed (TypeBase Shape u)) =>-                   ErrorMsg SubExp -> SrcLoc -> [VName] -> [TypeBase Shape u] -> [SubExp]-                -> InternaliseM [SubExp]+ensureArgShapes ::+  (Typed (TypeBase Shape u)) =>+  ErrorMsg SubExp ->+  SrcLoc ->+  [VName] ->+  [TypeBase Shape u] ->+  [SubExp] ->+  InternaliseM [SubExp] ensureArgShapes msg loc shapes paramts args =   zipWithM ensureArgShape (expectedTypes shapes paramts args) args-  where ensureArgShape _ (Constant v) = return $ Constant v-        ensureArgShape t (Var v)-          | arrayRank t < 1 = return $ Var v-          | otherwise =-              ensureShape msg loc t (baseString v) $ Var v+  where+    ensureArgShape _ (Constant v) = return $ Constant v+    ensureArgShape t (Var v)+      | arrayRank t < 1 = return $ Var v+      | otherwise =+        ensureShape msg loc t (baseString v) $ Var v -ensureShapeVar :: ErrorMsg SubExp -> SrcLoc -> ExtType -> String -> VName-               -> InternaliseM VName+ensureShapeVar ::+  ErrorMsg SubExp ->+  SrcLoc ->+  ExtType ->+  String ->+  VName ->+  InternaliseM VName ensureShapeVar msg loc t name v-  | Array{} <- t = do+  | Array {} <- t = do     newdims <- arrayDims . removeExistentials t <$> lookupType v     olddims <- arrayDims <$> lookupType v     if newdims == olddims@@ -112,5 +154,6 @@         cs <- assert "empty_or_match_cert" all_match msg loc         certifying cs $ letExp name $ shapeCoerce newdims v   | otherwise = return v-  where checkDim desired has =-          letSubExp "dim_match" $ BasicOp $ CmpOp (CmpEq int32) desired has+  where+    checkDim desired has =+      letSubExp "dim_match" $ BasicOp $ CmpOp (CmpEq int64) desired has
src/Futhark/Internalise/Bindings.hs view
@@ -1,93 +1,98 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE Safe #-}+ -- | Internalising bindings. module Futhark.Internalise.Bindings-  (-    bindingParams-  , bindingLoopParams-  , bindingLambdaParams-  , stmPattern+  ( bindingParams,+    bindingLoopParams,+    bindingLambdaParams,+    stmPattern,   )-  where+where -import Control.Monad.State  hiding (mapM) import Control.Monad.Reader hiding (mapM)- import qualified Data.Map.Strict as M--import Language.Futhark as E hiding (matchDims) import qualified Futhark.IR.SOACS as I-import Futhark.MonadFreshNames import Futhark.Internalise.Monad import Futhark.Internalise.TypesValues import Futhark.Util+import Language.Futhark as E hiding (matchDims) -bindingParams :: [E.TypeParam] -> [E.Pattern]-              -> ([I.FParam] -> [[I.FParam]] -> InternaliseM a)-              -> InternaliseM a+bindingParams ::+  [E.TypeParam] ->+  [E.Pattern] ->+  ([I.FParam] -> [[I.FParam]] -> InternaliseM a) ->+  InternaliseM a bindingParams tparams params m = do   flattened_params <- mapM flattenPattern params   let params_idents = concat flattened_params   params_ts <-     internaliseParamTypes $-    map (flip E.setAliases () . E.unInfo . E.identType) params_idents+      map (flip E.setAliases () . E.unInfo . E.identType) params_idents   let num_param_idents = map length flattened_params       num_param_ts = map (sum . map length) $ chunks num_param_idents params_ts -  let shape_params = [ I.Param v $ I.Prim I.int32 | E.TypeParamDim v _ <- tparams ]-      shape_subst = M.fromList [ (I.paramName p, [I.Var $ I.paramName p]) | p <- shape_params ]+  let shape_params = [I.Param v $ I.Prim I.int64 | E.TypeParamDim v _ <- tparams]+      shape_subst = M.fromList [(I.paramName p, [I.Var $ I.paramName p]) | p <- shape_params]   bindingFlatPattern params_idents (concat params_ts) $ \valueparams ->-    I.localScope (I.scopeOfFParams $ shape_params++concat valueparams) $-    substitutingVars shape_subst $ m shape_params $-    chunks num_param_ts (concat valueparams)+    I.localScope (I.scopeOfFParams $ shape_params ++ concat valueparams) $+      substitutingVars shape_subst $+        m shape_params $+          chunks num_param_ts (concat valueparams) -bindingLoopParams :: [E.TypeParam] -> E.Pattern-                  -> ([I.FParam] -> [I.FParam] -> InternaliseM a)-                  -> InternaliseM a+bindingLoopParams ::+  [E.TypeParam] ->+  E.Pattern ->+  ([I.FParam] -> [I.FParam] -> InternaliseM a) ->+  InternaliseM a bindingLoopParams tparams pat m = do   pat_idents <- flattenPattern pat   pat_ts <- internaliseLoopParamType (E.patternStructType pat) -  let shape_params = [ I.Param v $ I.Prim I.int32 | E.TypeParamDim v _ <- tparams ]-      shape_subst = M.fromList [ (I.paramName p, [I.Var $ I.paramName p]) | p <- shape_params ]+  let shape_params = [I.Param v $ I.Prim I.int64 | E.TypeParamDim v _ <- tparams]+      shape_subst = M.fromList [(I.paramName p, [I.Var $ I.paramName p]) | p <- shape_params]    bindingFlatPattern pat_idents pat_ts $ \valueparams ->-    I.localScope (I.scopeOfFParams $ shape_params++concat valueparams) $-    substitutingVars shape_subst $ m shape_params $ concat valueparams+    I.localScope (I.scopeOfFParams $ shape_params ++ concat valueparams) $+      substitutingVars shape_subst $ m shape_params $ concat valueparams -bindingLambdaParams :: [E.Pattern] -> [I.Type]-                    -> ([I.LParam] -> InternaliseM a)-                    -> InternaliseM a+bindingLambdaParams ::+  [E.Pattern] ->+  [I.Type] ->+  ([I.LParam] -> InternaliseM a) ->+  InternaliseM a bindingLambdaParams params ts m = do   params_idents <- concat <$> mapM flattenPattern params    bindingFlatPattern params_idents ts $ \params' ->     I.localScope (I.scopeOfLParams $ concat params') $ m $ concat params' -processFlatPattern :: Show t => [E.Ident] -> [t]-                   -> InternaliseM ([[I.Param t]], VarSubstitutions)+processFlatPattern ::+  Show t =>+  [E.Ident] ->+  [t] ->+  InternaliseM ([[I.Param t]], VarSubstitutions) processFlatPattern x y = processFlatPattern' [] x y   where-    processFlatPattern' pat []       _  = do+    processFlatPattern' pat [] _ = do       let (vs, substs) = unzip pat           substs' = M.fromList substs           idents = reverse vs       return (idents, substs')--    processFlatPattern' pat (p:rest) ts = do+    processFlatPattern' pat (p : rest) ts = do       (ps, subst, rest_ts) <- handleMapping ts <$> internaliseBindee p       processFlatPattern' ((ps, (E.identName p, map (I.Var . I.paramName) subst)) : pat) rest rest_ts      handleMapping ts [] =       ([], [], ts)-    handleMapping ts (r:rs) =-        let (ps, reps, ts')    = handleMapping' ts r-            (pss, repss, ts'') = handleMapping ts' rs-        in (ps++pss, reps:repss, ts'')+    handleMapping ts (r : rs) =+      let (ps, reps, ts') = handleMapping' ts r+          (pss, repss, ts'') = handleMapping ts' rs+       in (ps ++ pss, reps : repss, ts'') -    handleMapping' (t:ts) vname =+    handleMapping' (t : ts) vname =       let v' = I.Param vname t-      in ([v'], v', ts)+       in ([v'], v', ts)     handleMapping' [] _ =       error $ "processFlatPattern: insufficient identifiers in pattern." ++ show (x, y) @@ -99,44 +104,50 @@         1 -> return [name]         _ -> replicateM n $ newVName $ baseString name -bindingFlatPattern :: Show t => [E.Ident] -> [t]-                   -> ([[I.Param t]] -> InternaliseM a)-                   -> InternaliseM a+bindingFlatPattern ::+  Show t =>+  [E.Ident] ->+  [t] ->+  ([[I.Param t]] -> InternaliseM a) ->+  InternaliseM a bindingFlatPattern idents ts m = do   (ps, substs) <- processFlatPattern idents ts-  local (\env -> env { envSubsts = substs `M.union` envSubsts env}) $+  local (\env -> env {envSubsts = substs `M.union` envSubsts env}) $     m ps  -- | Flatten a pattern.  Returns a list of identifiers.  The -- structural type of each identifier is returned separately. flattenPattern :: MonadFreshNames m => E.Pattern -> m [E.Ident] flattenPattern = flattenPattern'-  where flattenPattern' (E.PatternParens p _) =-          flattenPattern' p-        flattenPattern' (E.Wildcard t loc) = do-          name <- newVName "nameless"-          flattenPattern' $ E.Id name t loc-        flattenPattern' (E.Id v (Info t) loc) =-          return [E.Ident v (Info t) loc]-        -- XXX: treat empty tuples and records as bool.-        flattenPattern' (E.TuplePattern [] loc) =-          flattenPattern' (E.Wildcard (Info $ E.Scalar $ E.Prim E.Bool) loc)-        flattenPattern' (E.RecordPattern [] loc) =-          flattenPattern' (E.Wildcard (Info $ E.Scalar $ E.Prim E.Bool) loc)-        flattenPattern' (E.TuplePattern pats _) =-          concat <$> mapM flattenPattern' pats-        flattenPattern' (E.RecordPattern fs loc) =-          flattenPattern' $ E.TuplePattern (map snd $ sortFields $ M.fromList fs) loc-        flattenPattern' (E.PatternAscription p _ _) =-          flattenPattern' p-        flattenPattern' (E.PatternLit _ t loc) =-          flattenPattern' $ E.Wildcard t loc-        flattenPattern' (E.PatternConstr _ _ ps _) =-          concat <$> mapM flattenPattern' ps+  where+    flattenPattern' (E.PatternParens p _) =+      flattenPattern' p+    flattenPattern' (E.Wildcard t loc) = do+      name <- newVName "nameless"+      flattenPattern' $ E.Id name t loc+    flattenPattern' (E.Id v (Info t) loc) =+      return [E.Ident v (Info t) loc]+    -- XXX: treat empty tuples and records as bool.+    flattenPattern' (E.TuplePattern [] loc) =+      flattenPattern' (E.Wildcard (Info $ E.Scalar $ E.Prim E.Bool) loc)+    flattenPattern' (E.RecordPattern [] loc) =+      flattenPattern' (E.Wildcard (Info $ E.Scalar $ E.Prim E.Bool) loc)+    flattenPattern' (E.TuplePattern pats _) =+      concat <$> mapM flattenPattern' pats+    flattenPattern' (E.RecordPattern fs loc) =+      flattenPattern' $ E.TuplePattern (map snd $ sortFields $ M.fromList fs) loc+    flattenPattern' (E.PatternAscription p _ _) =+      flattenPattern' p+    flattenPattern' (E.PatternLit _ t loc) =+      flattenPattern' $ E.Wildcard t loc+    flattenPattern' (E.PatternConstr _ _ ps _) =+      concat <$> mapM flattenPattern' ps -stmPattern :: E.Pattern -> [I.Type]-           -> ([VName] -> InternaliseM a)-           -> InternaliseM a+stmPattern ::+  E.Pattern ->+  [I.Type] ->+  ([VName] -> InternaliseM a) ->+  InternaliseM a stmPattern pat ts m = do   pat' <- flattenPattern pat   let addShapeStms l =
src/Futhark/Internalise/Defunctionalise.hs view
@@ -1,1094 +1,1247 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE Trustworthy #-}--- | Defunctionalization of typed, monomorphic Futhark programs without modules.-module Futhark.Internalise.Defunctionalise-  ( transformProg ) where--import qualified Control.Arrow as Arrow-import           Control.Monad.Identity-import           Control.Monad.State-import           Control.Monad.RWS hiding (Sum)-import           Data.Bifunctor-import           Data.Bitraversable-import           Data.Foldable-import           Data.List (sortOn, nub, partition, tails)-import qualified Data.List.NonEmpty as NE-import           Data.Maybe-import qualified Data.Map.Strict as M-import qualified Data.Set as S-import qualified Data.Sequence as Seq--import           Futhark.MonadFreshNames-import           Language.Futhark-import           Language.Futhark.Traversals-import           Futhark.IR.Pretty ()---- | An expression or an extended 'Lambda' (with size parameters,--- which AST lambdas do not support).-data ExtExp = ExtLambda [TypeParam] [Pattern] Exp (Aliasing, StructType) SrcLoc-            | ExtExp Exp-  deriving (Show)---- | A static value stores additional information about the result of--- defunctionalization of an expression, aside from the residual expression.-data StaticVal = Dynamic PatternType-               | LambdaSV [VName] Pattern StructType ExtExp Env-                 -- ^ The 'VName's are shape parameters that are bound-                 -- by the 'Pattern'.-               | RecordSV [(Name, StaticVal)]-               | SumSV Name [StaticVal] [(Name, [PatternType])]-                 -- ^ The constructor that is actually present, plus-                 -- the others that are not.-               | DynamicFun (Exp, StaticVal) StaticVal-               | IntrinsicSV-  deriving (Show)---- | Environment mapping variable names to their associated static value.-type Env = M.Map VName StaticVal--localEnv :: Env -> DefM a -> DefM a-localEnv env = local $ Arrow.second (env<>)---- Even when using a "new" environment (for evaluating closures) we--- still ram the global environment of DynamicFuns in there.-localNewEnv :: Env -> DefM a -> DefM a-localNewEnv env = local $ \(globals, old_env) ->-  (globals, M.filterWithKey (\k _ -> k `S.member` globals) old_env <> env)--extendEnv :: VName -> StaticVal -> DefM a -> DefM a-extendEnv vn sv = localEnv (M.singleton vn sv)--askEnv :: DefM Env-askEnv = asks snd--isGlobal :: VName -> DefM a -> DefM a-isGlobal v = local $ Arrow.first (S.insert v)---- | Returns the defunctionalization environment restricted--- to the given set of variable names and types.-restrictEnvTo :: NameSet -> DefM Env-restrictEnvTo (NameSet m) = restrict <$> ask-  where restrict (globals, env) = M.mapMaybeWithKey keep env-          where keep k sv = do guard $ not $ k `S.member` globals-                               u <- M.lookup k m-                               Just $ restrict' u sv-        restrict' Nonunique (Dynamic t) =-          Dynamic $ t `setUniqueness`  Nonunique-        restrict' _ (Dynamic t) =-          Dynamic t-        restrict' u (LambdaSV dims pat t e env) =-          LambdaSV dims pat t e $ M.map (restrict' u) env-        restrict' u (RecordSV fields) =-          RecordSV $ map (fmap $ restrict' u) fields-        restrict' u (SumSV c svs fields) =-          SumSV c (map (restrict' u) svs) fields-        restrict' u (DynamicFun (e, sv1) sv2) =-          DynamicFun (e, restrict' u sv1) $ restrict' u sv2-        restrict' _ IntrinsicSV = IntrinsicSV---- | Defunctionalization monad.  The Reader environment tracks both--- the current Env as well as the set of globally defined dynamic--- functions.  This is used to avoid unnecessarily large closure--- environments.-newtype DefM a = DefM (RWS (S.Set VName, Env) (Seq.Seq ValBind) VNameSource a)-  deriving (Functor, Applicative, Monad,-            MonadReader (S.Set VName, Env),-            MonadWriter (Seq.Seq ValBind),-            MonadFreshNames)---- | Run a computation in the defunctionalization monad. Returns the result of--- the computation, a new name source, and a list of lifted function declations.-runDefM :: VNameSource -> DefM a -> (a, VNameSource, Seq.Seq ValBind)-runDefM src (DefM m) = runRWS m mempty src--collectFuns :: DefM a -> DefM (a, Seq.Seq ValBind)-collectFuns m = pass $ do-  (x, decs) <- listen m-  return ((x, decs), const mempty)---- | Looks up the associated static value for a given name in the environment.-lookupVar :: SrcLoc -> VName -> DefM StaticVal-lookupVar loc x = do-  env <- askEnv-  case M.lookup x env of-    Just sv -> return sv-    Nothing -- If the variable is unknown, it may refer to the 'intrinsics'-            -- module, which we will have to treat specially.-      | baseTag x <= maxIntrinsicTag -> return IntrinsicSV-      | otherwise -> -- Anything not in scope is going to be an-                     -- existential size.-          return $ Dynamic $ Scalar $ Prim $ Signed Int32-      | otherwise ->  error $ "Variable " ++ pretty x ++ " at "-                          ++ locStr loc ++ " is out of scope."---- Like patternDimNames, but ignores sizes that are only found in--- funtion types.-arraySizes :: StructType -> S.Set VName-arraySizes (Scalar Arrow{}) = mempty-arraySizes (Scalar (Record fields)) = foldMap arraySizes fields-arraySizes (Scalar (Sum cs)) = foldMap (foldMap arraySizes) cs-arraySizes (Scalar (TypeVar _ _ _ targs)) =-  mconcat $ map f targs-  where f (TypeArgDim (NamedDim d) _) = S.singleton $ qualLeaf d-        f TypeArgDim{} = mempty-        f (TypeArgType t _) = arraySizes t-arraySizes (Scalar Prim{}) = mempty-arraySizes (Array _ _ t shape) =-  arraySizes (Scalar t) <> foldMap dimName (shapeDims shape)-  where dimName :: DimDecl VName -> S.Set VName-        dimName (NamedDim qn) = S.singleton $ qualLeaf qn-        dimName _             = mempty--patternArraySizes :: Pattern -> S.Set VName-patternArraySizes = arraySizes . patternStructType--dimMapping :: Monoid a =>-              TypeBase (DimDecl VName) a-           -> TypeBase (DimDecl VName) a-           -> M.Map VName VName-dimMapping t1 t2 = execState (matchDims f t1 t2) mempty-  where f (NamedDim d1) (NamedDim d2) = do-          modify $ M.insert (qualLeaf d1) (qualLeaf d2)-          return $ NamedDim d1-        f d _ = return d--defuncFun :: [TypeParam] -> [Pattern] -> Exp -> (Aliasing, StructType) -> SrcLoc-          -> DefM (Exp, StaticVal)-defuncFun tparams pats e0 (closure, ret) loc = do-  when (any isTypeParam tparams) $-    error $ "Received a lambda with type parameters at " ++ locStr loc-         ++ ", but the defunctionalizer expects a monomorphic input program."-  -- Extract the first parameter of the lambda and "push" the-  -- remaining ones (if there are any) into the body of the lambda.-  let (dims, pat, ret', e0') = case pats of-        [] -> error "Received a lambda with no parameters."-        [pat'] -> (map typeParamName tparams, pat', ret, ExtExp e0)-        (pat' : pats') ->-          -- Split shape parameters into those that are determined by-          -- the first pattern, and those that are determined by later-          -- patterns.-          let bound_by_pat = (`S.member` patternArraySizes pat') . typeParamName-              (pat_dims, rest_dims) = partition bound_by_pat tparams-          in (map typeParamName pat_dims, pat',-              foldFunType (map (toStruct . patternType) pats') ret,-              ExtLambda rest_dims pats' e0 (closure, ret) loc)--  -- Construct a record literal that closes over the environment of-  -- the lambda.  Closed-over 'DynamicFun's are converted to their-  -- closure representation.-  let used = freeVars (Lambda pats e0 Nothing (Info (closure, ret)) loc)-             `without` mconcat (map oneName dims)-  used_env <- restrictEnvTo used--  -- The closure parts that are sizes are proactively turned into size-  -- parameters.-  let sizes_of_arrays = foldMap (arraySizes . toStruct . typeFromSV') used_env <>-                        patternArraySizes pat-      notSize = not . (`S.member` sizes_of_arrays)-      (fields, env) = unzip $ map closureFromDynamicFun $-                      filter (notSize . fst) $ M.toList used_env-      env' = M.fromList env-      closure_dims = S.toList sizes_of_arrays--  global <- asks fst--  return (RecordLit fields loc,-          LambdaSV (nub $ filter (`S.notMember` global) $-                    dims<>closure_dims) pat ret' e0' env')--  where closureFromDynamicFun (vn, DynamicFun (clsr_env, sv) _) =-          let name = nameFromString $ pretty vn-          in (RecordFieldExplicit name clsr_env mempty, (vn, sv))--        closureFromDynamicFun (vn, sv) =-          let name = nameFromString $ pretty vn-              tp' = typeFromSV' sv-          in (RecordFieldExplicit name-               (Var (qualName vn) (Info tp') mempty) mempty, (vn, sv))---- | Defunctionalization of an expression. Returns the residual expression and--- the associated static value in the defunctionalization monad.-defuncExp :: Exp -> DefM (Exp, StaticVal)--defuncExp e@Literal{} =-  return (e, Dynamic $ typeOf e)--defuncExp e@IntLit{} =-  return (e, Dynamic $ typeOf e)--defuncExp e@FloatLit{} =-  return (e, Dynamic $ typeOf e)--defuncExp e@StringLit{} =-  return (e, Dynamic $ typeOf e)--defuncExp (Parens e loc) = do-  (e', sv) <- defuncExp e-  return (Parens e' loc, sv)--defuncExp (QualParens qn e loc) = do-  (e', sv) <- defuncExp e-  return (QualParens qn e' loc, sv)--defuncExp (TupLit es loc) = do-  (es', svs) <- unzip <$> mapM defuncExp es-  return (TupLit es' loc, RecordSV $ zip tupleFieldNames svs)--defuncExp (RecordLit fs loc) = do-  (fs', names_svs) <- unzip <$> mapM defuncField fs-  return (RecordLit fs' loc, RecordSV names_svs)--  where defuncField (RecordFieldExplicit vn e loc') = do-          (e', sv) <- defuncExp e-          return (RecordFieldExplicit vn e' loc', (vn, sv))-        defuncField (RecordFieldImplicit vn _ loc') = do-          sv <- lookupVar loc' vn-          case sv of-            -- If the implicit field refers to a dynamic function, we-            -- convert it to an explicit field with a record closing over-            -- the environment and bind the corresponding static value.-            DynamicFun (e, sv') _ -> let vn' = baseName vn-                                     in return (RecordFieldExplicit vn' e loc',-                                                (vn', sv'))-            -- The field may refer to a functional expression, so we get the-            -- type from the static value and not the one from the AST.-            _ -> let tp = Info $ typeFromSV' sv-                 in return (RecordFieldImplicit vn tp loc', (baseName vn, sv))--defuncExp (ArrayLit es t@(Info t') loc) = do-  es' <- mapM defuncExp' es-  return (ArrayLit es' t loc, Dynamic t')--defuncExp (Range e1 me incl t@(Info t', _) loc) = do-  e1' <- defuncExp' e1-  me' <- mapM defuncExp' me-  incl' <- mapM defuncExp' incl-  return (Range e1' me' incl' t loc, Dynamic t')--defuncExp e@(Var qn _ loc) = do-  sv <- lookupVar loc (qualLeaf qn)-  case sv of-    -- If the variable refers to a dynamic function, we return its closure-    -- representation (i.e., a record expression capturing the free variables-    -- and a 'LambdaSV' static value) instead of the variable itself.-    DynamicFun closure _ -> return closure-    -- Intrinsic functions used as variables are eta-expanded, so we-    -- can get rid of them.-    IntrinsicSV -> do-      (pats, body, tp) <- etaExpand (typeOf e) e-      defuncExp $ Lambda pats body Nothing (Info (mempty, tp)) mempty-    _ -> let tp = typeFromSV' sv-         in return (Var qn (Info tp) loc, sv)--defuncExp (Ascript e0 tydecl loc)-  | orderZero (typeOf e0) = do (e0', sv) <- defuncExp e0-                               return (Ascript e0' tydecl loc, sv)-  | otherwise = defuncExp e0--defuncExp (Coerce e0 tydecl t loc)-  | orderZero (typeOf e0) = do (e0', sv) <- defuncExp e0-                               return (Coerce e0' tydecl t loc, sv)-  | otherwise = defuncExp e0--defuncExp (LetPat pat e1 e2 (Info t, retext) loc) = do-  (e1', sv1) <- defuncExp e1-  let env  = matchPatternSV pat sv1-      pat' = updatePattern' pat sv1-  (e2', sv2) <- localEnv env $ defuncExp e2-  -- To maintain any sizes going out of scope, we need to compute the-  -- old size substitution induced by retext and also apply it to the-  -- newly computed body type.-  let mapping = dimMapping (typeOf e2) t-      subst v = fromMaybe v $ M.lookup v mapping-      t' = first (fmap subst) $ typeOf e2'-  return (LetPat pat' e1' e2' (Info t', retext) loc, sv2)---- Local functions are handled by rewriting them to lambdas, so that--- the same machinery can be re-used.  But we may have to eta-expand--- first.-defuncExp (LetFun vn (dims, pats, _, Info ret, e1) e2 let_t loc)-  | Scalar Arrow{} <- ret = do-      (body_pats, e1', ret') <- etaExpand (fromStruct ret) e1-      let f = (dims, pats <> body_pats, Nothing, Info ret', e1')-      defuncExp $ LetFun vn f e2 let_t loc--  | otherwise = do-      (e1', sv1) <- defuncFun dims pats e1 (mempty, ret) loc-      (e2', sv2) <- localEnv (M.singleton vn sv1) $ defuncExp e2-      return (LetPat (Id vn (Info (typeOf e1')) loc) e1' e2' (Info $ typeOf e2', Info []) loc,-              sv2)--defuncExp (If e1 e2 e3 tp loc) = do-  (e1', _ ) <- defuncExp e1-  (e2', sv) <- defuncExp e2-  (e3', _ ) <- defuncExp e3-  return (If e1' e2' e3' tp loc, sv)--defuncExp e@(Apply f@(Var f' _ _) arg d (t, ext) loc)-  | baseTag (qualLeaf f') <= maxIntrinsicTag,-    TupLit es tuploc <- arg = do-      -- defuncSoacExp also works fine for non-SOACs.-      es' <- mapM defuncSoacExp es-      return (Apply f (TupLit es' tuploc) d (t, ext) loc,-              Dynamic $ typeOf e)--defuncExp e@Apply{} = defuncApply 0 e--defuncExp (Negate e0 loc) = do-  (e0', sv) <- defuncExp e0-  return (Negate e0' loc, sv)--defuncExp (Lambda pats e0 _ (Info (closure, ret)) loc) =-  defuncFun [] pats e0 (closure, ret) loc---- Operator sections are expected to be converted to lambda-expressions--- by the monomorphizer, so they should no longer occur at this point.-defuncExp OpSection{}      = error "defuncExp: unexpected operator section."-defuncExp OpSectionLeft{}  = error "defuncExp: unexpected operator section."-defuncExp OpSectionRight{} = error "defuncExp: unexpected operator section."-defuncExp ProjectSection{} = error "defuncExp: unexpected projection section."-defuncExp IndexSection{}   = error "defuncExp: unexpected projection section."--defuncExp (DoLoop sparams pat e1 form e3 ret loc) = do-  (e1', sv1) <- defuncExp e1-  let env1 = matchPatternSV pat sv1-  (form', env2) <- case form of-    For v e2      -> do e2' <- defuncExp' e2-                        return (For v e2', envFromIdent v)-    ForIn pat2 e2 -> do e2' <- defuncExp' e2-                        return (ForIn pat2 e2', envFromPattern pat2)-    While e2      -> do e2' <- localEnv env1 $ defuncExp' e2-                        return (While e2', mempty)-  (e3', sv) <- localEnv (env1 <> env2) $ defuncExp e3-  return (DoLoop sparams pat e1' form' e3' ret loc, sv)-  where envFromIdent (Ident vn (Info tp) _) =-          M.singleton vn $ Dynamic tp---- We handle BinOps by turning them into ordinary function applications.-defuncExp (BinOp (qn, qnloc) (Info t)-           (e1, Info (pt1, ext1)) (e2, Info (pt2, ext2))-           (Info ret) (Info retext) loc) =-  defuncExp $ Apply (Apply (Var qn (Info t) qnloc)-                     e1 (Info (diet pt1, ext1))-                     (Info (Scalar $ Arrow mempty Unnamed (fromStruct pt2) ret), Info []) loc)-                    e2 (Info (diet pt2, ext2)) (Info ret, Info retext) loc--defuncExp (Project vn e0 tp@(Info tp') loc) = do-  (e0', sv0) <- defuncExp e0-  case sv0 of-    RecordSV svs -> case lookup vn svs of-      Just sv -> return (Project vn e0' (Info $ typeFromSV' sv) loc, sv)-      Nothing -> error "Invalid record projection."-    Dynamic _ -> return (Project vn e0' tp loc, Dynamic tp')-    _ -> error $ "Projection of an expression with static value " ++ show sv0--defuncExp (LetWith id1 id2 idxs e1 body t loc) = do-  e1' <- defuncExp' e1-  sv1 <- lookupVar (identSrcLoc id2) $ identName id2-  idxs' <- mapM defuncDimIndex idxs-  (body', sv) <- extendEnv (identName id1) sv1 $ defuncExp body-  return (LetWith id1 id2 idxs' e1' body' t loc, sv)--defuncExp expr@(Index e0 idxs info loc) = do-  e0' <- defuncExp' e0-  idxs' <- mapM defuncDimIndex idxs-  return (Index e0' idxs' info loc, Dynamic $ typeOf expr)--defuncExp (Update e1 idxs e2 loc) = do-  (e1', sv) <- defuncExp e1-  idxs' <- mapM defuncDimIndex idxs-  e2' <- defuncExp' e2-  return (Update e1' idxs' e2' loc, sv)---- Note that we might change the type of the record field here.  This--- is not permitted in the type checker due to problems with type--- inference, but it actually works fine.-defuncExp (RecordUpdate e1 fs e2 _ loc) = do-  (e1', sv1) <- defuncExp e1-  (e2', sv2) <- defuncExp e2-  let sv = staticField sv1 sv2 fs-  return (RecordUpdate e1' fs e2' (Info $ typeFromSV' sv1) loc,-          sv)-  where staticField (RecordSV svs) sv2 (f:fs') =-          case lookup f svs of-            Just sv -> RecordSV $-                       (f, staticField sv sv2 fs') : filter ((/=f) . fst) svs-            Nothing -> error "Invalid record projection."-        staticField (Dynamic t@(Scalar Record{})) sv2 fs'@(_:_) =-          staticField (svFromType t) sv2 fs'-        staticField _ sv2 _ = sv2--defuncExp (Assert e1 e2 desc loc) = do-  (e1', _) <- defuncExp e1-  (e2', sv) <- defuncExp e2-  return (Assert e1' e2' desc loc, sv)--defuncExp (Constr name es (Info (Scalar (Sum all_fs))) loc) = do-  (es', svs) <- unzip <$> mapM defuncExp es-  let sv = SumSV name svs $ M.toList $-           name `M.delete` M.map (map defuncType) all_fs-  return (Constr name es' (Info (typeFromSV' sv)) loc, sv)-  where defuncType :: Monoid als =>-                      TypeBase (DimDecl VName) als-                   -> TypeBase (DimDecl VName) als-        defuncType (Array as u t shape) = Array as u (defuncScalar t) shape-        defuncType (Scalar t) = Scalar $ defuncScalar t--        defuncScalar :: Monoid als =>-                        ScalarTypeBase (DimDecl VName) als-                     -> ScalarTypeBase (DimDecl VName) als-        defuncScalar (Record fs) = Record $ M.map defuncType fs-        defuncScalar Arrow{} = Record mempty-        defuncScalar (Sum fs) = Sum $ M.map (map defuncType) fs-        defuncScalar (Prim t) = Prim t-        defuncScalar (TypeVar as u tn targs) = TypeVar as u tn targs--defuncExp (Constr name _ (Info t) loc) =-  error $ "Constructor " ++ pretty name ++ " given type " ++-  pretty t ++ " at " ++ locStr loc--defuncExp (Match e cs t loc) = do-  (e', sv) <- defuncExp e-  csPairs  <- mapM (defuncCase sv) cs-  let cs' = fmap fst csPairs-      sv' = snd $ NE.head csPairs-  return (Match e' cs' t loc, sv')--defuncExp (Attr info e loc) = do-  (e', sv) <- defuncExp e-  return (Attr info e' loc, sv)---- | Same as 'defuncExp', except it ignores the static value.-defuncExp' :: Exp -> DefM Exp-defuncExp' = fmap fst . defuncExp--defuncExtExp :: ExtExp -> DefM (Exp, StaticVal)-defuncExtExp (ExtExp e) = defuncExp e-defuncExtExp (ExtLambda tparams pats e0 (closure, ret) loc) =-  defuncFun tparams pats e0 (closure, ret) loc--defuncCase :: StaticVal -> Case -> DefM (Case, StaticVal)-defuncCase sv (CasePat p e loc) = do-  let p'  = updatePattern' p sv-      env = matchPatternSV p sv-  (e', sv') <- localEnv env $ defuncExp e-  return (CasePat p' e' loc, sv')---- | Defunctionalize the function argument to a SOAC by eta-expanding if--- necessary and then defunctionalizing the body of the introduced lambda.-defuncSoacExp :: Exp -> DefM Exp-defuncSoacExp e@OpSection{}      = return e-defuncSoacExp e@OpSectionLeft{}  = return e-defuncSoacExp e@OpSectionRight{} = return e-defuncSoacExp e@ProjectSection{} = return e--defuncSoacExp (Parens e loc) =-  Parens <$> defuncSoacExp e <*> pure loc--defuncSoacExp (Lambda params e0 decl tp loc) = do-  let env = foldMap envFromPattern params-  e0' <- localEnv env $ defuncSoacExp e0-  return $ Lambda params e0' decl tp loc--defuncSoacExp e-  | Scalar Arrow{} <- typeOf e = do-      (pats, body, tp) <- etaExpand (typeOf e) e-      let env = foldMap envFromPattern pats-      body' <- localEnv env $ defuncExp' body-      return $ Lambda pats body' Nothing (Info (mempty, tp)) mempty-  | otherwise = defuncExp' e--etaExpand :: PatternType -> Exp -> DefM ([Pattern], Exp, StructType)-etaExpand e_t e = do-  let (ps, ret) = getType e_t-  (pats, vars) <- fmap unzip . forM ps $ \(p, t) -> do-    x <- case p of Named x -> pure x-                   Unnamed -> newNameFromString "x"-    return (Id x (Info t) mempty,-            Var (qualName x) (Info t) mempty)-  let e' = foldl' (\e1 (e2, t2, argtypes) ->-                     Apply e1 e2 (Info (diet t2, Nothing))-                     (Info (foldFunType argtypes ret), Info []) mempty)-           e $ zip3 vars (map snd ps) (drop 1 $ tails $ map snd ps)-  return (pats, e', toStruct ret)--  where getType (Scalar (Arrow _ p t1 t2)) =-          let (ps, r) = getType t2 in ((p,t1) : ps, r)-        getType t = ([], t)---- | Defunctionalize an indexing of a single array dimension.-defuncDimIndex :: DimIndexBase Info VName -> DefM (DimIndexBase Info VName)-defuncDimIndex (DimFix e1) = DimFix . fst <$> defuncExp e1-defuncDimIndex (DimSlice me1 me2 me3) =-  DimSlice <$> defunc' me1 <*> defunc' me2 <*> defunc' me3-  where defunc' = mapM defuncExp'---- | Defunctionalize a let-bound function, while preserving parameters--- that have order 0 types (i.e., non-functional).-defuncLet :: [TypeParam] -> [Pattern] -> Exp -> StructType-          -> DefM ([TypeParam], [Pattern], Exp, StaticVal)-defuncLet dims ps@(pat:pats) body rettype-  | patternOrderZero pat = do--      let bound_by_pat = (`S.member` patternDimNames pat) . typeParamName-          -- Take care to not include more size parameters than necessary.-          (pat_dims, rest_dims) = partition bound_by_pat dims-          env = envFromPattern pat <> envFromShapeParams pat_dims-      (rest_dims', pats', body', sv) <- localEnv env $ defuncLet rest_dims pats body rettype-      closure <- defuncFun dims ps body (mempty, rettype) mempty-      return (pat_dims ++ rest_dims', pat : pats', body', DynamicFun closure sv)-  | otherwise = do-      (e, sv) <- defuncFun dims ps body (mempty, rettype) mempty-      return ([], [], e, sv)--defuncLet _ [] body rettype = do-  (body', sv) <- defuncExp body-  return ([], [], body', imposeType sv rettype)-  where imposeType Dynamic{} t =-          Dynamic $ fromStruct t-        imposeType (RecordSV fs1) (Scalar (Record fs2)) =-          RecordSV $ M.toList $ M.intersectionWith imposeType (M.fromList fs1) fs2-        imposeType sv _ = sv--sizesForAll :: MonadFreshNames m => [Pattern] -> m ([VName], [Pattern])-sizesForAll params = do-  (params', sizes) <- runStateT (mapM (astMap tv) params) []-  return (sizes, params')-  where tv = identityMapper { mapOnPatternType = bitraverse onDim pure }-        onDim AnyDim = do v <- lift $ newVName "size"-                          modify (v:)-                          pure $ NamedDim $ qualName v-        onDim d = pure d---- | Defunctionalize an application expression at a given depth of application.--- Calls to dynamic (first-order) functions are preserved at much as possible,--- but a new lifted function is created if a dynamic function is only partially--- applied.-defuncApply :: Int -> Exp -> DefM (Exp, StaticVal)-defuncApply depth e@(Apply e1 e2 d t@(Info ret, Info ext) loc) = do-  let (argtypes, _) = unfoldFunType ret-  (e1', sv1) <- defuncApply (depth+1) e1-  (e2', sv2) <- defuncExp e2-  let e' = Apply e1' e2' d t loc-  case sv1 of-    LambdaSV dims pat e0_t e0 closure_env -> do-      let env' = matchPatternSV pat sv2-          env_dim = envFromDimNames dims-      (e0', sv) <- localNewEnv (env' <> closure_env <> env_dim) $ defuncExtExp e0--      let closure_pat = buildEnvPattern closure_env-          pat' = updatePattern pat sv2--      globals <- asks fst--      -- Lift lambda to top-level function definition.  We put in-      -- a lot of effort to try to infer the uniqueness attributes-      -- of the lifted function, but this is ultimately all a sham-      -- and a hack.  There is some piece we're missing.-      let params = [closure_pat, pat']-          params_for_rettype = params ++ svParams sv1 ++ svParams sv2-          svParams (LambdaSV _ sv_pat _ _ _) = [sv_pat]-          svParams _                         = []-          rettype = buildRetType closure_env params_for_rettype e0_t $ typeOf e0'--          already_bound = globals <> S.fromList dims <>-                          S.map identName (foldMap patternIdents params)-          more_dims = S.toList $-                      S.filter (`S.notMember` already_bound) $-                      foldMap patternArraySizes params--          -- Embed some information about the original function-          -- into the name of the lifted function, to make the-          -- result slightly more human-readable.-          liftedName i (Var f _ _) =-            "lifted_" ++ show i ++ "_" ++ baseString (qualLeaf f)-          liftedName i (Apply f _ _ _ _) =-            liftedName (i+1) f-          liftedName _ _ = "lifted"--      -- Ensure that no parameter sizes are AnyDim.  The internaliser-      -- expects this.  This is easy, because they are all-      -- first-order.-      (missing_dims, params') <- sizesForAll params--      fname <- newNameFromString $ liftedName (0::Int) e1-      liftValDec fname rettype (dims ++ more_dims ++ missing_dims)-        params' e0'--      let t1 = toStruct $ typeOf e1'-          t2 = toStruct $ typeOf e2'-          fname' = qualName fname-          fname'' = Var fname' (Info (Scalar $ Arrow mempty Unnamed (fromStruct t1) $-                                      Scalar $ Arrow mempty Unnamed (fromStruct t2) rettype))-                    loc--          -- FIXME: what if this application returns both a function-          -- and a value?-          callret | orderZero ret = (Info ret, Info ext)-                  | otherwise     = (Info rettype, Info ext)--      return (Parens (Apply (Apply fname'' e1'-                              (Info (Observe, Nothing))-                              (Info $ Scalar $ Arrow mempty Unnamed (fromStruct t2) rettype,-                               Info [])-                              loc)-                      e2' d callret loc) mempty, sv)--    -- If e1 is a dynamic function, we just leave the application in place,-    -- but we update the types since it may be partially applied or return-    -- a higher-order term.-    DynamicFun _ sv ->-      let (argtypes', rettype) = dynamicFunType sv argtypes-          restype = foldFunType argtypes' rettype `setAliases` aliases ret-          -- FIXME: what if this application returns both a function-          -- and a value?-          callret | orderZero ret = (Info ret, Info ext)-                  | otherwise     = (Info restype, Info ext)-          apply_e = Apply e1' e2' d callret loc-      in return (apply_e, sv)--    -- Propagate the 'IntrinsicsSV' until we reach the outermost application,-    -- where we construct a dynamic static value with the appropriate type.-    IntrinsicSV-      | depth == 0 ->-          -- If the intrinsic is fully applied, then we are done.-          -- Otherwise we need to eta-expand it and recursively-          -- defunctionalise. XXX: might it be better to simply-          -- eta-expand immediately any time we encounter a-          -- non-fully-applied intrinsic?-          if null argtypes-            then return (e', Dynamic $ typeOf e)-            else do (pats, body, tp) <- etaExpand (typeOf e') e'-                    defuncExp $ Lambda pats body Nothing (Info (mempty, tp)) mempty-      | otherwise -> return (e', IntrinsicSV)--    _ -> error $ "Application of an expression that is neither a static lambda "-              ++ "nor a dynamic function, but has static value: " ++ show sv1--defuncApply depth e@(Var qn (Info t) loc) = do-    let (argtypes, _) = unfoldFunType t-    sv <- lookupVar loc (qualLeaf qn)-    case sv of-      DynamicFun _ _-        | fullyApplied sv depth ->-            -- We still need to update the types in case the dynamic-            -- function returns a higher-order term.-            let (argtypes', rettype) = dynamicFunType sv argtypes-            in return (Var qn (Info (foldFunType argtypes' rettype)) loc, sv)--        | otherwise -> do-            fname <- newName $ qualLeaf qn-            let (dims, pats, e0, sv') = liftDynFun sv depth-                pats_names = S.map identName $ mconcat $ map patternIdents pats-                notInPats = (`S.notMember` pats_names)-                dims' = filter notInPats dims-                (argtypes', rettype) = dynamicFunType sv' argtypes-            liftValDec fname (fromStruct rettype) dims' pats e0-            return (Var (qualName fname)-                    (Info (foldFunType argtypes' $ fromStruct rettype)) loc, sv')--      IntrinsicSV -> return (e, IntrinsicSV)--      _ -> return (Var qn (Info (typeFromSV' sv)) loc, sv)--defuncApply depth (Parens e _) = defuncApply depth e--defuncApply _ expr = defuncExp expr---- | Check if a 'StaticVal' and a given application depth corresponds--- to a fully applied dynamic function.-fullyApplied :: StaticVal -> Int -> Bool-fullyApplied (DynamicFun _ sv) depth-  | depth == 0   = False-  | depth >  0   = fullyApplied sv (depth-1)-fullyApplied _ _ = True---- | Converts a dynamic function 'StaticVal' into a list of--- dimensions, a list of parameters, a function body, and the--- appropriate static value for applying the function at the given--- depth of partial application.-liftDynFun :: StaticVal -> Int -> ([VName], [Pattern], Exp, StaticVal)-liftDynFun (DynamicFun (e, sv) _) 0 = ([], [], e, sv)-liftDynFun (DynamicFun clsr@(_, LambdaSV dims pat _ _ _) sv) d-  | d > 0 =  let (dims', pats, e', sv') = liftDynFun sv (d-1)-             in (nub $ dims ++ dims', pat : pats, e', DynamicFun clsr sv')-liftDynFun sv _ = error $ "Tried to lift a StaticVal " ++ show sv-                       ++ ", but expected a dynamic function."---- | Converts a pattern to an environment that binds the individual names of the--- pattern to their corresponding types wrapped in a 'Dynamic' static value.-envFromPattern :: Pattern -> Env-envFromPattern pat = case pat of-  TuplePattern ps _       -> foldMap envFromPattern ps-  RecordPattern fs _      -> foldMap (envFromPattern . snd) fs-  PatternParens p _       -> envFromPattern p-  Id vn (Info t) _        -> M.singleton vn $ Dynamic t-  Wildcard _ _            -> mempty-  PatternAscription p _ _ -> envFromPattern p-  PatternLit{}            -> mempty-  PatternConstr _ _ ps _  -> foldMap envFromPattern ps---- | Create an environment that binds the shape parameters.-envFromShapeParams :: [TypeParamBase VName] -> Env-envFromShapeParams = envFromDimNames . map dim-  where dim (TypeParamDim vn _) = vn-        dim tparam = error $-          "The defunctionalizer expects a monomorphic input program,\n" ++-          "but it received a type parameter " ++ pretty tparam ++-          " at " ++ locStr (srclocOf tparam) ++ "."--envFromDimNames :: [VName] -> Env-envFromDimNames = M.fromList . flip zip (repeat $ Dynamic $ Scalar $ Prim $ Signed Int32)---- | Create a new top-level value declaration with the given function name,--- return type, list of parameters, and body expression.-liftValDec :: VName -> PatternType -> [VName] -> [Pattern] -> Exp -> DefM ()-liftValDec fname rettype dims pats body = tell $ Seq.singleton dec-  where dims' = map (`TypeParamDim` mempty) dims-        -- FIXME: this pass is still not correctly size-preserving, so-        -- forget those return sizes that we forgot to propagate along-        -- the way.  Hopefully the internaliser is conservative and-        -- will insert reshapes...-        bound_here = S.fromList dims <> S.map identName (foldMap patternIdents pats)-        anyDimIfNotBound (NamedDim v)-          | qualLeaf v `S.member` bound_here = NamedDim v-          | otherwise = AnyDim-        anyDimIfNotBound d = d-        rettype_st = first anyDimIfNotBound $ toStruct rettype--        dec = ValBind-          { valBindEntryPoint = Nothing-          , valBindName       = fname-          , valBindRetDecl    = Nothing-          , valBindRetType    = Info (rettype_st, [])-          , valBindTypeParams = dims'-          , valBindParams     = pats-          , valBindBody       = body-          , valBindDoc        = Nothing-          , valBindAttrs      = mempty-          , valBindLocation   = mempty-          }---- | Given a closure environment, construct a record pattern that--- binds the closed over variables.-buildEnvPattern :: Env -> Pattern-buildEnvPattern env = RecordPattern (map buildField $ M.toList env) mempty-  where buildField (vn, sv) =-          (nameFromString (pretty vn),-           Id vn (Info $ snd $ typeFromSV sv) mempty)---- | Given a closure environment pattern and the type of a term,--- construct the type of that term, where uniqueness is set to--- `Nonunique` for those arrays that are bound in the environment or--- pattern (except if they are unique there).  This ensures that a--- lifted function can create unique arrays as long as they do not--- alias any of its parameters.  XXX: it is not clear that this is a--- sufficient property, unfortunately.-buildRetType :: Env -> [Pattern] -> StructType -> PatternType -> PatternType-buildRetType env pats = comb-  where bound = foldMap oneName (M.keys env) <> foldMap patternVars pats-        boundAsUnique v =-          maybe False (unique . unInfo . identType) $-          find ((==v) . identName) $ S.toList $ foldMap patternIdents pats-        problematic v = (v `member` bound) && not (boundAsUnique v)-        comb (Scalar (Record fs_annot)) (Scalar (Record fs_got)) =-          Scalar $ Record $ M.intersectionWith comb fs_annot fs_got-        comb (Scalar (Sum cs_annot)) (Scalar (Sum cs_got)) =-          Scalar $ Sum $ M.intersectionWith (zipWith comb) cs_annot cs_got-        comb (Scalar Arrow{}) t =-          descend t-        comb got et =-          descend $ fromStruct got `setAliases` aliases et--        descend t@Array{}-          | any (problematic . aliasVar) (aliases t) = t `setUniqueness` Nonunique-        descend (Scalar (Record t)) = Scalar $ Record $ fmap descend t-        descend t = t---- | Compute the corresponding type for a given static value.-typeFromSV :: StaticVal -> ([VName], PatternType)-typeFromSV (Dynamic tp) =-  (mempty, tp)-typeFromSV (LambdaSV sizes _ _ _ env) =-  (sizes <> env_sizes,-   Scalar $ Record $ M.fromList $ map (fmap snd) env')-  where env' = map (bimap (nameFromString . pretty) typeFromSV) $ M.toList env-        env_sizes = concatMap (fst . snd) env'-typeFromSV (RecordSV ls) =-  let ts = map (fmap typeFromSV) ls-  in (concatMap (fst . snd) ts,-      Scalar $ Record $ M.fromList $ map (fmap snd) ts)-typeFromSV (DynamicFun (_, sv) _) =-  typeFromSV sv-typeFromSV (SumSV name svs fields) =-  let (sizes, svs') = unzip $ map typeFromSV svs-  in (concat sizes,-      Scalar $ Sum $ M.insert name svs' $ M.fromList fields)-typeFromSV IntrinsicSV =-  error "Tried to get the type from the static value of an intrinsic."--typeFromSV' :: StaticVal -> PatternType-typeFromSV' sv =-  let (sizes, t) = typeFromSV sv-  in unscopeType (S.fromList sizes) t---- | Construct the type for a fully-applied dynamic function from its--- static value and the original types of its arguments.-dynamicFunType :: StaticVal -> [PatternType] -> ([PatternType], PatternType)-dynamicFunType (DynamicFun _ sv) (p:ps) =-  let (ps', ret) = dynamicFunType sv ps in (p : ps', ret)-dynamicFunType sv _ = ([], typeFromSV' sv)---- | Match a pattern with its static value. Returns an environment with--- the identifier components of the pattern mapped to the corresponding--- subcomponents of the static value.-matchPatternSV :: PatternBase Info VName -> StaticVal -> Env-matchPatternSV (TuplePattern ps _) (RecordSV ls) =-  mconcat $ zipWith (\p (_, sv) -> matchPatternSV p sv) ps ls-matchPatternSV (RecordPattern ps _) (RecordSV ls)-  | ps' <- sortOn fst ps, ls' <- sortOn fst ls,-    map fst ps' == map fst ls' =-      mconcat $ zipWith (\(_, p) (_, sv) -> matchPatternSV p sv) ps' ls'-matchPatternSV (PatternParens pat _) sv = matchPatternSV pat sv-matchPatternSV (Id vn (Info t) _) sv =-  -- When matching a pattern with a zero-order STaticVal, the type of-  -- the pattern wins out.  This is important when matching a-  -- nonunique pattern with a unique value.-  if orderZeroSV sv-  then M.singleton vn $ Dynamic t-  else M.singleton vn sv-matchPatternSV (Wildcard _ _) _ = mempty-matchPatternSV (PatternAscription pat _ _) sv = matchPatternSV pat sv-matchPatternSV PatternLit{} _ = mempty-matchPatternSV (PatternConstr c1 _ ps _) (SumSV c2 ls fs)-  | c1 == c2 =-      mconcat $ zipWith matchPatternSV ps ls-  | Just ts <- lookup c1 fs =-      mconcat $ zipWith matchPatternSV ps $ map svFromType ts-  | otherwise =-      error $ "matchPatternSV: missing constructor in type: " ++ pretty c1-matchPatternSV (PatternConstr c1 _ ps _) (Dynamic (Scalar (Sum fs)))-  | Just ts <- M.lookup c1 fs =-      mconcat $ zipWith matchPatternSV ps $ map svFromType ts-  | otherwise =-      error $ "matchPatternSV: missing constructor in type: " ++ pretty c1-matchPatternSV pat (Dynamic t) = matchPatternSV pat $ svFromType t-matchPatternSV pat sv = error $ "Tried to match pattern " ++ pretty pat-                             ++ " with static value " ++ show sv ++ "."--orderZeroSV :: StaticVal -> Bool-orderZeroSV Dynamic{} = True-orderZeroSV (RecordSV fields) = all (orderZeroSV . snd) fields-orderZeroSV _ = False---- | Given a pattern and the static value for the defunctionalized argument,--- update the pattern to reflect the changes in the types.-updatePattern :: Pattern -> StaticVal -> Pattern-updatePattern (TuplePattern ps loc) (RecordSV svs) =-  TuplePattern (zipWith updatePattern ps $ map snd svs) loc-updatePattern (RecordPattern ps loc) (RecordSV svs)-  | ps' <- sortOn fst ps, svs' <- sortOn fst svs =-      RecordPattern (zipWith (\(n, p) (_, sv) ->-                                (n, updatePattern p sv)) ps' svs') loc-updatePattern (PatternParens pat loc) sv =-  PatternParens (updatePattern pat sv) loc-updatePattern (Id vn (Info tp) loc) sv =-  Id vn (Info $ comb tp (snd (typeFromSV sv)  `setUniqueness` Nonunique)) loc-  -- Preserve any original zeroth-order types.-  where comb (Scalar Arrow{}) t2 = t2-        comb (Scalar (Record m1)) (Scalar (Record m2)) =-          Scalar $ Record $ M.intersectionWith comb m1 m2-        comb (Scalar (Sum m1)) (Scalar (Sum m2)) =-          Scalar $ Sum $ M.intersectionWith (zipWith comb) m1 m2-        comb t1 _ = t1 -- t1 must be array or prim.-updatePattern pat@(Wildcard (Info tp) loc) sv-  | orderZero tp = pat-  | otherwise = Wildcard (Info $ snd $ typeFromSV sv) loc-updatePattern (PatternAscription pat tydecl loc) sv-  | orderZero . unInfo $ expandedType tydecl =-      PatternAscription (updatePattern pat sv) tydecl loc-  | otherwise = updatePattern pat sv-updatePattern p@PatternLit{} _ = p-updatePattern pat@(PatternConstr c1 (Info t) ps loc) sv@(SumSV _ svs _)-  | orderZero t = pat-  | otherwise = PatternConstr c1 (Info t') ps' loc-  where t' = snd (typeFromSV sv) `setUniqueness` Nonunique-        ps' = zipWith updatePattern ps svs-updatePattern (PatternConstr c1 _ ps loc) (Dynamic t) =-  PatternConstr c1 (Info t) ps loc-updatePattern pat (Dynamic t) = updatePattern pat (svFromType t)-updatePattern pat sv =-  error $ "Tried to update pattern " ++ pretty pat-       ++ "to reflect the static value " ++ show sv---- Like updatePattern, but discard sizes.  This is used for--- let-bindings, where we might otherwise introduce sizes that are--- free.-updatePattern' :: Pattern -> StaticVal -> Pattern-updatePattern' pat sv =-  let pat' = updatePattern pat sv-      (sizes, _) = typeFromSV sv-      tr = identityMapper { mapOnPatternType =-                              pure . unscopeType (S.fromList sizes)-                          }-  in runIdentity $ astMap tr pat'---- | Convert a record (or tuple) type to a record static value. This is used for--- "unwrapping" tuples and records that are nested in 'Dynamic' static values.-svFromType :: PatternType -> StaticVal-svFromType (Scalar (Record fs)) = RecordSV . M.toList $ M.map svFromType fs-svFromType t                    = Dynamic t---- A set of names where we also track uniqueness.-newtype NameSet = NameSet (M.Map VName Uniqueness) deriving (Show)--instance Semigroup NameSet where-  NameSet x <> NameSet y = NameSet $ M.unionWith max x y--instance Monoid NameSet where-  mempty = NameSet mempty--without :: NameSet -> NameSet -> NameSet-without (NameSet x) (NameSet y) = NameSet $ x `M.difference` y--member :: VName -> NameSet -> Bool-member v (NameSet m) = v `M.member` m--ident :: Ident -> NameSet-ident v = NameSet $ M.singleton (identName v) (uniqueness $ unInfo $ identType v)--oneName :: VName -> NameSet-oneName v = NameSet $ M.singleton v Nonunique--names :: S.Set VName -> NameSet-names = foldMap oneName---- | Compute the set of free variables of an expression.-freeVars :: Exp -> NameSet-freeVars expr = case expr of-  Literal{}            -> mempty-  IntLit{}             -> mempty-  FloatLit{}           -> mempty-  StringLit{}          -> mempty-  Parens e _           -> freeVars e-  QualParens _ e _     -> freeVars e-  TupLit es _          -> foldMap freeVars es--  RecordLit fs _       -> foldMap freeVarsField fs-    where freeVarsField (RecordFieldExplicit _ e _)  = freeVars e-          freeVarsField (RecordFieldImplicit vn t _) = ident $ Ident vn t mempty--  ArrayLit es t _      -> foldMap freeVars es <>-                          names (typeDimNames $ unInfo t)-  Range e me incl _ _  -> freeVars e <> foldMap freeVars me <>-                          foldMap freeVars incl-  Var qn (Info t) _    -> NameSet $ M.singleton (qualLeaf qn) $ uniqueness t-  Ascript e t _        -> freeVars e <> names (typeDimNames $ unInfo $ expandedType t)-  Coerce e t _ _       -> freeVars e <> names (typeDimNames $ unInfo $ expandedType t)-  LetPat pat e1 e2 _ _ -> freeVars e1 <> ((names (patternDimNames pat) <> freeVars e2)-                                          `without` patternVars pat)--  LetFun vn (_, pats, _, _, e1) e2 _ _ ->-    ((freeVars e1 <> names (foldMap patternDimNames pats))-      `without` foldMap patternVars pats) <>-    (freeVars e2 `without` oneName vn)--  If e1 e2 e3 _ _           -> freeVars e1 <> freeVars e2 <> freeVars e3-  Apply e1 e2 _ _ _         -> freeVars e1 <> freeVars e2-  Negate e _                -> freeVars e-  Lambda pats e0 _ _ _      -> (names (foldMap patternDimNames pats) <> freeVars e0)-                               `without` foldMap patternVars pats-  OpSection{}                 -> mempty-  OpSectionLeft _  _ e _ _ _  -> freeVars e-  OpSectionRight _ _ e _ _ _  -> freeVars e-  ProjectSection{}            -> mempty-  IndexSection idxs _ _       -> foldMap freeDimIndex idxs--  DoLoop sparams pat e1 form e3 _ _ ->-    let (e2fv, e2ident) = formVars form-    in freeVars e1 <> e2fv <>-       (freeVars e3 `without`-        (names (S.fromList sparams) <> patternVars pat <> e2ident))-    where formVars (For v e2) = (freeVars e2, ident v)-          formVars (ForIn p e2)   = (freeVars e2, patternVars p)-          formVars (While e2)     = (freeVars e2, mempty)--  BinOp (qn, _) _ (e1, _) (e2, _) _ _ _ -> oneName (qualLeaf qn) <>-                                           freeVars e1 <> freeVars e2-  Project _ e _ _                -> freeVars e--  LetWith id1 id2 idxs e1 e2 _ _ ->-    ident id2 <> foldMap freeDimIndex idxs <> freeVars e1 <>-    (freeVars e2 `without` ident id1)--  Index e idxs _ _    -> freeVars e  <> foldMap freeDimIndex idxs-  Update e1 idxs e2 _ -> freeVars e1 <> foldMap freeDimIndex idxs <> freeVars e2-  RecordUpdate e1 _ e2 _ _ -> freeVars e1 <> freeVars e2--  Assert e1 e2 _ _    -> freeVars e1 <> freeVars e2-  Constr _ es _ _     -> foldMap freeVars es-  Attr _ e _          -> freeVars e-  Match e cs _ _      -> freeVars e <> foldMap caseFV cs-    where caseFV (CasePat p eCase _) = (names (patternDimNames p) <> freeVars eCase)-                                       `without` patternVars p--freeDimIndex :: DimIndexBase Info VName -> NameSet-freeDimIndex (DimFix e) = freeVars e-freeDimIndex (DimSlice me1 me2 me3) =-  foldMap (foldMap freeVars) [me1, me2, me3]---- | Extract all the variable names bound in a pattern.-patternVars :: Pattern -> NameSet-patternVars = mconcat . map ident . S.toList . patternIdents---- | Defunctionalize a top-level value binding. Returns the--- transformed result as well as an environment that binds the name of--- the value binding to the static value of the transformed body.  The--- boolean is true if the function is a 'DynamicFun'.-defuncValBind :: ValBind -> DefM (ValBind, Env, Bool)---- Eta-expand entry points with a functional return type.-defuncValBind (ValBind entry name _ (Info (rettype, retext)) tparams params body _ attrs loc)-  | Scalar Arrow{} <- rettype = do-      (body_pats, body', rettype') <- etaExpand (fromStruct rettype) body-      defuncValBind $ ValBind entry name Nothing-        (Info (rettype', retext))-        tparams (params <> body_pats) body' Nothing attrs loc--defuncValBind valbind@(ValBind _ name retdecl (Info (rettype, retext)) tparams params body _ _ _) = do-  (tparams', params', body', sv) <- defuncLet tparams params body rettype-  let rettype' = combineTypeShapes rettype $ anySizes $ toStruct $ typeOf body'-  (missing_dims, params'') <- sizesForAll params'-  return ( valbind { valBindRetDecl    = retdecl-                   , valBindRetType    = Info (if null params'-                                               then rettype' `setUniqueness` Nonunique-                                               else rettype',-                                               retext)-                   , valBindTypeParams = tparams' ++-                                         map (`TypeParamDim` mempty) missing_dims-                   , valBindParams     = params''-                   , valBindBody       = body'-                   }-         , M.singleton name sv-         , case sv of DynamicFun{} -> True-                      Dynamic{}    -> True-                      _            -> False)---- | Defunctionalize a list of top-level declarations.-defuncVals :: [ValBind] -> DefM (Seq.Seq ValBind)-defuncVals [] = return mempty-defuncVals (valbind : ds) = do-  ((valbind', env, dyn), defs) <- collectFuns $ defuncValBind valbind-  ds' <- localEnv env $ if dyn-                        then isGlobal (valBindName valbind') $ defuncVals ds-                        else defuncVals ds-  return $ defs <> Seq.singleton valbind' <> ds'---- | Transform a list of top-level value bindings. May produce new--- lifted function definitions, which are placed in front of the--- resulting list of declarations.-transformProg :: MonadFreshNames m => [ValBind] -> m [ValBind]-transformProg decs = modifyNameSource $ \namesrc ->-  let (decs', namesrc', liftedDecs) = runDefM namesrc $ defuncVals decs-  in (toList $ liftedDecs <> decs', namesrc')+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE Trustworthy #-}++-- | Defunctionalization of typed, monomorphic Futhark programs without modules.+module Futhark.Internalise.Defunctionalise (transformProg) where++import qualified Control.Arrow as Arrow+import Control.Monad.Identity+import Control.Monad.RWS hiding (Sum)+import Control.Monad.State+import Data.Bifunctor+import Data.Bitraversable+import Data.Foldable+import Data.List (nub, partition, sortOn, tails)+import qualified Data.List.NonEmpty as NE+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Data.Sequence as Seq+import qualified Data.Set as S+import Futhark.IR.Pretty ()+import Futhark.MonadFreshNames+import Language.Futhark+import Language.Futhark.Traversals++-- | An expression or an extended 'Lambda' (with size parameters,+-- which AST lambdas do not support).+data ExtExp+  = ExtLambda [TypeParam] [Pattern] Exp (Aliasing, StructType) SrcLoc+  | ExtExp Exp+  deriving (Show)++-- | A static value stores additional information about the result of+-- defunctionalization of an expression, aside from the residual expression.+data StaticVal+  = Dynamic PatternType+  | -- | The 'VName's are shape parameters that are bound+    -- by the 'Pattern'.+    LambdaSV [VName] Pattern StructType ExtExp Env+  | RecordSV [(Name, StaticVal)]+  | -- | The constructor that is actually present, plus+    -- the others that are not.+    SumSV Name [StaticVal] [(Name, [PatternType])]+  | DynamicFun (Exp, StaticVal) StaticVal+  | IntrinsicSV+  deriving (Show)++-- | Environment mapping variable names to their associated static value.+type Env = M.Map VName StaticVal++localEnv :: Env -> DefM a -> DefM a+localEnv env = local $ Arrow.second (env <>)++-- Even when using a "new" environment (for evaluating closures) we+-- still ram the global environment of DynamicFuns in there.+localNewEnv :: Env -> DefM a -> DefM a+localNewEnv env = local $ \(globals, old_env) ->+  (globals, M.filterWithKey (\k _ -> k `S.member` globals) old_env <> env)++extendEnv :: VName -> StaticVal -> DefM a -> DefM a+extendEnv vn sv = localEnv (M.singleton vn sv)++askEnv :: DefM Env+askEnv = asks snd++isGlobal :: VName -> DefM a -> DefM a+isGlobal v = local $ Arrow.first (S.insert v)++-- | Returns the defunctionalization environment restricted+-- to the given set of variable names and types.+restrictEnvTo :: NameSet -> DefM Env+restrictEnvTo (NameSet m) = restrict <$> ask+  where+    restrict (globals, env) = M.mapMaybeWithKey keep env+      where+        keep k sv = do+          guard $ not $ k `S.member` globals+          u <- M.lookup k m+          Just $ restrict' u sv+    restrict' Nonunique (Dynamic t) =+      Dynamic $ t `setUniqueness` Nonunique+    restrict' _ (Dynamic t) =+      Dynamic t+    restrict' u (LambdaSV dims pat t e env) =+      LambdaSV dims pat t e $ M.map (restrict' u) env+    restrict' u (RecordSV fields) =+      RecordSV $ map (fmap $ restrict' u) fields+    restrict' u (SumSV c svs fields) =+      SumSV c (map (restrict' u) svs) fields+    restrict' u (DynamicFun (e, sv1) sv2) =+      DynamicFun (e, restrict' u sv1) $ restrict' u sv2+    restrict' _ IntrinsicSV = IntrinsicSV++-- | Defunctionalization monad.  The Reader environment tracks both+-- the current Env as well as the set of globally defined dynamic+-- functions.  This is used to avoid unnecessarily large closure+-- environments.+newtype DefM a = DefM (RWS (S.Set VName, Env) (Seq.Seq ValBind) VNameSource a)+  deriving+    ( Functor,+      Applicative,+      Monad,+      MonadReader (S.Set VName, Env),+      MonadWriter (Seq.Seq ValBind),+      MonadFreshNames+    )++-- | Run a computation in the defunctionalization monad. Returns the result of+-- the computation, a new name source, and a list of lifted function declations.+runDefM :: VNameSource -> DefM a -> (a, VNameSource, Seq.Seq ValBind)+runDefM src (DefM m) = runRWS m mempty src++collectFuns :: DefM a -> DefM (a, Seq.Seq ValBind)+collectFuns m = pass $ do+  (x, decs) <- listen m+  return ((x, decs), const mempty)++-- | Looks up the associated static value for a given name in the environment.+lookupVar :: SrcLoc -> VName -> DefM StaticVal+lookupVar loc x = do+  env <- askEnv+  case M.lookup x env of+    Just sv -> return sv+    Nothing -- If the variable is unknown, it may refer to the 'intrinsics'+    -- module, which we will have to treat specially.+      | baseTag x <= maxIntrinsicTag -> return IntrinsicSV+      | otherwise -> -- Anything not in scope is going to be an+      -- existential size.+        return $ Dynamic $ Scalar $ Prim $ Signed Int64+      | otherwise ->+        error $+          "Variable " ++ pretty x ++ " at "+            ++ locStr loc+            ++ " is out of scope."++-- Like patternDimNames, but ignores sizes that are only found in+-- funtion types.+arraySizes :: StructType -> S.Set VName+arraySizes (Scalar Arrow {}) = mempty+arraySizes (Scalar (Record fields)) = foldMap arraySizes fields+arraySizes (Scalar (Sum cs)) = foldMap (foldMap arraySizes) cs+arraySizes (Scalar (TypeVar _ _ _ targs)) =+  mconcat $ map f targs+  where+    f (TypeArgDim (NamedDim d) _) = S.singleton $ qualLeaf d+    f TypeArgDim {} = mempty+    f (TypeArgType t _) = arraySizes t+arraySizes (Scalar Prim {}) = mempty+arraySizes (Array _ _ t shape) =+  arraySizes (Scalar t) <> foldMap dimName (shapeDims shape)+  where+    dimName :: DimDecl VName -> S.Set VName+    dimName (NamedDim qn) = S.singleton $ qualLeaf qn+    dimName _ = mempty++patternArraySizes :: Pattern -> S.Set VName+patternArraySizes = arraySizes . patternStructType++dimMapping ::+  Monoid a =>+  TypeBase (DimDecl VName) a ->+  TypeBase (DimDecl VName) a ->+  M.Map VName VName+dimMapping t1 t2 = execState (matchDims f t1 t2) mempty+  where+    f (NamedDim d1) (NamedDim d2) = do+      modify $ M.insert (qualLeaf d1) (qualLeaf d2)+      return $ NamedDim d1+    f d _ = return d++defuncFun ::+  [TypeParam] ->+  [Pattern] ->+  Exp ->+  (Aliasing, StructType) ->+  SrcLoc ->+  DefM (Exp, StaticVal)+defuncFun tparams pats e0 (closure, ret) loc = do+  when (any isTypeParam tparams) $+    error $+      "Received a lambda with type parameters at " ++ locStr loc+        ++ ", but the defunctionalizer expects a monomorphic input program."+  -- Extract the first parameter of the lambda and "push" the+  -- remaining ones (if there are any) into the body of the lambda.+  let (dims, pat, ret', e0') = case pats of+        [] -> error "Received a lambda with no parameters."+        [pat'] -> (map typeParamName tparams, pat', ret, ExtExp e0)+        (pat' : pats') ->+          -- Split shape parameters into those that are determined by+          -- the first pattern, and those that are determined by later+          -- patterns.+          let bound_by_pat = (`S.member` patternArraySizes pat') . typeParamName+              (pat_dims, rest_dims) = partition bound_by_pat tparams+           in ( map typeParamName pat_dims,+                pat',+                foldFunType (map (toStruct . patternType) pats') ret,+                ExtLambda rest_dims pats' e0 (closure, ret) loc+              )++  -- Construct a record literal that closes over the environment of+  -- the lambda.  Closed-over 'DynamicFun's are converted to their+  -- closure representation.+  let used =+        freeVars (Lambda pats e0 Nothing (Info (closure, ret)) loc)+          `without` mconcat (map oneName dims)+  used_env <- restrictEnvTo used++  -- The closure parts that are sizes are proactively turned into size+  -- parameters.+  let sizes_of_arrays =+        foldMap (arraySizes . toStruct . typeFromSV') used_env+          <> patternArraySizes pat+      notSize = not . (`S.member` sizes_of_arrays)+      (fields, env) =+        unzip $+          map closureFromDynamicFun $+            filter (notSize . fst) $ M.toList used_env+      env' = M.fromList env+      closure_dims = S.toList sizes_of_arrays++  global <- asks fst++  return+    ( RecordLit fields loc,+      LambdaSV+        ( nub $+            filter (`S.notMember` global) $+              dims <> closure_dims+        )+        pat+        ret'+        e0'+        env'+    )+  where+    closureFromDynamicFun (vn, DynamicFun (clsr_env, sv) _) =+      let name = nameFromString $ pretty vn+       in (RecordFieldExplicit name clsr_env mempty, (vn, sv))+    closureFromDynamicFun (vn, sv) =+      let name = nameFromString $ pretty vn+          tp' = typeFromSV' sv+       in ( RecordFieldExplicit+              name+              (Var (qualName vn) (Info tp') mempty)+              mempty,+            (vn, sv)+          )++-- | Defunctionalization of an expression. Returns the residual expression and+-- the associated static value in the defunctionalization monad.+defuncExp :: Exp -> DefM (Exp, StaticVal)+defuncExp e@Literal {} =+  return (e, Dynamic $ typeOf e)+defuncExp e@IntLit {} =+  return (e, Dynamic $ typeOf e)+defuncExp e@FloatLit {} =+  return (e, Dynamic $ typeOf e)+defuncExp e@StringLit {} =+  return (e, Dynamic $ typeOf e)+defuncExp (Parens e loc) = do+  (e', sv) <- defuncExp e+  return (Parens e' loc, sv)+defuncExp (QualParens qn e loc) = do+  (e', sv) <- defuncExp e+  return (QualParens qn e' loc, sv)+defuncExp (TupLit es loc) = do+  (es', svs) <- unzip <$> mapM defuncExp es+  return (TupLit es' loc, RecordSV $ zip tupleFieldNames svs)+defuncExp (RecordLit fs loc) = do+  (fs', names_svs) <- unzip <$> mapM defuncField fs+  return (RecordLit fs' loc, RecordSV names_svs)+  where+    defuncField (RecordFieldExplicit vn e loc') = do+      (e', sv) <- defuncExp e+      return (RecordFieldExplicit vn e' loc', (vn, sv))+    defuncField (RecordFieldImplicit vn _ loc') = do+      sv <- lookupVar loc' vn+      case sv of+        -- If the implicit field refers to a dynamic function, we+        -- convert it to an explicit field with a record closing over+        -- the environment and bind the corresponding static value.+        DynamicFun (e, sv') _ ->+          let vn' = baseName vn+           in return+                ( RecordFieldExplicit vn' e loc',+                  (vn', sv')+                )+        -- The field may refer to a functional expression, so we get the+        -- type from the static value and not the one from the AST.+        _ ->+          let tp = Info $ typeFromSV' sv+           in return (RecordFieldImplicit vn tp loc', (baseName vn, sv))+defuncExp (ArrayLit es t@(Info t') loc) = do+  es' <- mapM defuncExp' es+  return (ArrayLit es' t loc, Dynamic t')+defuncExp (Range e1 me incl t@(Info t', _) loc) = do+  e1' <- defuncExp' e1+  me' <- mapM defuncExp' me+  incl' <- mapM defuncExp' incl+  return (Range e1' me' incl' t loc, Dynamic t')+defuncExp e@(Var qn _ loc) = do+  sv <- lookupVar loc (qualLeaf qn)+  case sv of+    -- If the variable refers to a dynamic function, we return its closure+    -- representation (i.e., a record expression capturing the free variables+    -- and a 'LambdaSV' static value) instead of the variable itself.+    DynamicFun closure _ -> return closure+    -- Intrinsic functions used as variables are eta-expanded, so we+    -- can get rid of them.+    IntrinsicSV -> do+      (pats, body, tp) <- etaExpand (typeOf e) e+      defuncExp $ Lambda pats body Nothing (Info (mempty, tp)) mempty+    _ ->+      let tp = typeFromSV' sv+       in return (Var qn (Info tp) loc, sv)+defuncExp (Ascript e0 tydecl loc)+  | orderZero (typeOf e0) = do+    (e0', sv) <- defuncExp e0+    return (Ascript e0' tydecl loc, sv)+  | otherwise = defuncExp e0+defuncExp (Coerce e0 tydecl t loc)+  | orderZero (typeOf e0) = do+    (e0', sv) <- defuncExp e0+    return (Coerce e0' tydecl t loc, sv)+  | otherwise = defuncExp e0+defuncExp (LetPat pat e1 e2 (Info t, retext) loc) = do+  (e1', sv1) <- defuncExp e1+  let env = matchPatternSV pat sv1+      pat' = updatePattern' pat sv1+  (e2', sv2) <- localEnv env $ defuncExp e2+  -- To maintain any sizes going out of scope, we need to compute the+  -- old size substitution induced by retext and also apply it to the+  -- newly computed body type.+  let mapping = dimMapping (typeOf e2) t+      subst v = fromMaybe v $ M.lookup v mapping+      t' = first (fmap subst) $ typeOf e2'+  return (LetPat pat' e1' e2' (Info t', retext) loc, sv2)++-- Local functions are handled by rewriting them to lambdas, so that+-- the same machinery can be re-used.  But we may have to eta-expand+-- first.+defuncExp (LetFun vn (dims, pats, _, Info ret, e1) e2 let_t loc)+  | Scalar Arrow {} <- ret = do+    (body_pats, e1', ret') <- etaExpand (fromStruct ret) e1+    let f = (dims, pats <> body_pats, Nothing, Info ret', e1')+    defuncExp $ LetFun vn f e2 let_t loc+  | otherwise = do+    (e1', sv1) <- defuncFun dims pats e1 (mempty, ret) loc+    (e2', sv2) <- localEnv (M.singleton vn sv1) $ defuncExp e2+    return+      ( LetPat (Id vn (Info (typeOf e1')) loc) e1' e2' (Info $ typeOf e2', Info []) loc,+        sv2+      )+defuncExp (If e1 e2 e3 tp loc) = do+  (e1', _) <- defuncExp e1+  (e2', sv) <- defuncExp e2+  (e3', _) <- defuncExp e3+  return (If e1' e2' e3' tp loc, sv)+defuncExp e@(Apply f@(Var f' _ _) arg d (t, ext) loc)+  | baseTag (qualLeaf f') <= maxIntrinsicTag,+    TupLit es tuploc <- arg = do+    -- defuncSoacExp also works fine for non-SOACs.+    es' <- mapM defuncSoacExp es+    return+      ( Apply f (TupLit es' tuploc) d (t, ext) loc,+        Dynamic $ typeOf e+      )+defuncExp e@Apply {} = defuncApply 0 e+defuncExp (Negate e0 loc) = do+  (e0', sv) <- defuncExp e0+  return (Negate e0' loc, sv)+defuncExp (Lambda pats e0 _ (Info (closure, ret)) loc) =+  defuncFun [] pats e0 (closure, ret) loc+-- Operator sections are expected to be converted to lambda-expressions+-- by the monomorphizer, so they should no longer occur at this point.+defuncExp OpSection {} = error "defuncExp: unexpected operator section."+defuncExp OpSectionLeft {} = error "defuncExp: unexpected operator section."+defuncExp OpSectionRight {} = error "defuncExp: unexpected operator section."+defuncExp ProjectSection {} = error "defuncExp: unexpected projection section."+defuncExp IndexSection {} = error "defuncExp: unexpected projection section."+defuncExp (DoLoop sparams pat e1 form e3 ret loc) = do+  (e1', sv1) <- defuncExp e1+  let env1 = matchPatternSV pat sv1+  (form', env2) <- case form of+    For v e2 -> do+      e2' <- defuncExp' e2+      return (For v e2', envFromIdent v)+    ForIn pat2 e2 -> do+      e2' <- defuncExp' e2+      return (ForIn pat2 e2', envFromPattern pat2)+    While e2 -> do+      e2' <- localEnv env1 $ defuncExp' e2+      return (While e2', mempty)+  (e3', sv) <- localEnv (env1 <> env2) $ defuncExp e3+  return (DoLoop sparams pat e1' form' e3' ret loc, sv)+  where+    envFromIdent (Ident vn (Info tp) _) =+      M.singleton vn $ Dynamic tp++-- We handle BinOps by turning them into ordinary function applications.+defuncExp+  ( BinOp+      (qn, qnloc)+      (Info t)+      (e1, Info (pt1, ext1))+      (e2, Info (pt2, ext2))+      (Info ret)+      (Info retext)+      loc+    ) =+    defuncExp $+      Apply+        ( Apply+            (Var qn (Info t) qnloc)+            e1+            (Info (diet pt1, ext1))+            (Info (Scalar $ Arrow mempty Unnamed (fromStruct pt2) ret), Info [])+            loc+        )+        e2+        (Info (diet pt2, ext2))+        (Info ret, Info retext)+        loc+defuncExp (Project vn e0 tp@(Info tp') loc) = do+  (e0', sv0) <- defuncExp e0+  case sv0 of+    RecordSV svs -> case lookup vn svs of+      Just sv -> return (Project vn e0' (Info $ typeFromSV' sv) loc, sv)+      Nothing -> error "Invalid record projection."+    Dynamic _ -> return (Project vn e0' tp loc, Dynamic tp')+    _ -> error $ "Projection of an expression with static value " ++ show sv0+defuncExp (LetWith id1 id2 idxs e1 body t loc) = do+  e1' <- defuncExp' e1+  sv1 <- lookupVar (identSrcLoc id2) $ identName id2+  idxs' <- mapM defuncDimIndex idxs+  (body', sv) <- extendEnv (identName id1) sv1 $ defuncExp body+  return (LetWith id1 id2 idxs' e1' body' t loc, sv)+defuncExp expr@(Index e0 idxs info loc) = do+  e0' <- defuncExp' e0+  idxs' <- mapM defuncDimIndex idxs+  return (Index e0' idxs' info loc, Dynamic $ typeOf expr)+defuncExp (Update e1 idxs e2 loc) = do+  (e1', sv) <- defuncExp e1+  idxs' <- mapM defuncDimIndex idxs+  e2' <- defuncExp' e2+  return (Update e1' idxs' e2' loc, sv)++-- Note that we might change the type of the record field here.  This+-- is not permitted in the type checker due to problems with type+-- inference, but it actually works fine.+defuncExp (RecordUpdate e1 fs e2 _ loc) = do+  (e1', sv1) <- defuncExp e1+  (e2', sv2) <- defuncExp e2+  let sv = staticField sv1 sv2 fs+  return+    ( RecordUpdate e1' fs e2' (Info $ typeFromSV' sv1) loc,+      sv+    )+  where+    staticField (RecordSV svs) sv2 (f : fs') =+      case lookup f svs of+        Just sv ->+          RecordSV $+            (f, staticField sv sv2 fs') : filter ((/= f) . fst) svs+        Nothing -> error "Invalid record projection."+    staticField (Dynamic t@(Scalar Record {})) sv2 fs'@(_ : _) =+      staticField (svFromType t) sv2 fs'+    staticField _ sv2 _ = sv2+defuncExp (Assert e1 e2 desc loc) = do+  (e1', _) <- defuncExp e1+  (e2', sv) <- defuncExp e2+  return (Assert e1' e2' desc loc, sv)+defuncExp (Constr name es (Info (Scalar (Sum all_fs))) loc) = do+  (es', svs) <- unzip <$> mapM defuncExp es+  let sv =+        SumSV name svs $+          M.toList $+            name `M.delete` M.map (map defuncType) all_fs+  return (Constr name es' (Info (typeFromSV' sv)) loc, sv)+  where+    defuncType ::+      Monoid als =>+      TypeBase (DimDecl VName) als ->+      TypeBase (DimDecl VName) als+    defuncType (Array as u t shape) = Array as u (defuncScalar t) shape+    defuncType (Scalar t) = Scalar $ defuncScalar t++    defuncScalar ::+      Monoid als =>+      ScalarTypeBase (DimDecl VName) als ->+      ScalarTypeBase (DimDecl VName) als+    defuncScalar (Record fs) = Record $ M.map defuncType fs+    defuncScalar Arrow {} = Record mempty+    defuncScalar (Sum fs) = Sum $ M.map (map defuncType) fs+    defuncScalar (Prim t) = Prim t+    defuncScalar (TypeVar as u tn targs) = TypeVar as u tn targs+defuncExp (Constr name _ (Info t) loc) =+  error $+    "Constructor " ++ pretty name ++ " given type "+      ++ pretty t+      ++ " at "+      ++ locStr loc+defuncExp (Match e cs t loc) = do+  (e', sv) <- defuncExp e+  csPairs <- mapM (defuncCase sv) cs+  let cs' = fmap fst csPairs+      sv' = snd $ NE.head csPairs+  return (Match e' cs' t loc, sv')+defuncExp (Attr info e loc) = do+  (e', sv) <- defuncExp e+  return (Attr info e' loc, sv)++-- | Same as 'defuncExp', except it ignores the static value.+defuncExp' :: Exp -> DefM Exp+defuncExp' = fmap fst . defuncExp++defuncExtExp :: ExtExp -> DefM (Exp, StaticVal)+defuncExtExp (ExtExp e) = defuncExp e+defuncExtExp (ExtLambda tparams pats e0 (closure, ret) loc) =+  defuncFun tparams pats e0 (closure, ret) loc++defuncCase :: StaticVal -> Case -> DefM (Case, StaticVal)+defuncCase sv (CasePat p e loc) = do+  let p' = updatePattern' p sv+      env = matchPatternSV p sv+  (e', sv') <- localEnv env $ defuncExp e+  return (CasePat p' e' loc, sv')++-- | Defunctionalize the function argument to a SOAC by eta-expanding if+-- necessary and then defunctionalizing the body of the introduced lambda.+defuncSoacExp :: Exp -> DefM Exp+defuncSoacExp e@OpSection {} = return e+defuncSoacExp e@OpSectionLeft {} = return e+defuncSoacExp e@OpSectionRight {} = return e+defuncSoacExp e@ProjectSection {} = return e+defuncSoacExp (Parens e loc) =+  Parens <$> defuncSoacExp e <*> pure loc+defuncSoacExp (Lambda params e0 decl tp loc) = do+  let env = foldMap envFromPattern params+  e0' <- localEnv env $ defuncSoacExp e0+  return $ Lambda params e0' decl tp loc+defuncSoacExp e+  | Scalar Arrow {} <- typeOf e = do+    (pats, body, tp) <- etaExpand (typeOf e) e+    let env = foldMap envFromPattern pats+    body' <- localEnv env $ defuncExp' body+    return $ Lambda pats body' Nothing (Info (mempty, tp)) mempty+  | otherwise = defuncExp' e++etaExpand :: PatternType -> Exp -> DefM ([Pattern], Exp, StructType)+etaExpand e_t e = do+  let (ps, ret) = getType e_t+  (pats, vars) <- fmap unzip . forM ps $ \(p, t) -> do+    x <- case p of+      Named x -> pure x+      Unnamed -> newNameFromString "x"+    return+      ( Id x (Info t) mempty,+        Var (qualName x) (Info t) mempty+      )+  let e' =+        foldl'+          ( \e1 (e2, t2, argtypes) ->+              Apply+                e1+                e2+                (Info (diet t2, Nothing))+                (Info (foldFunType argtypes ret), Info [])+                mempty+          )+          e+          $ zip3 vars (map snd ps) (drop 1 $ tails $ map snd ps)+  return (pats, e', toStruct ret)+  where+    getType (Scalar (Arrow _ p t1 t2)) =+      let (ps, r) = getType t2 in ((p, t1) : ps, r)+    getType t = ([], t)++-- | Defunctionalize an indexing of a single array dimension.+defuncDimIndex :: DimIndexBase Info VName -> DefM (DimIndexBase Info VName)+defuncDimIndex (DimFix e1) = DimFix . fst <$> defuncExp e1+defuncDimIndex (DimSlice me1 me2 me3) =+  DimSlice <$> defunc' me1 <*> defunc' me2 <*> defunc' me3+  where+    defunc' = mapM defuncExp'++-- | Defunctionalize a let-bound function, while preserving parameters+-- that have order 0 types (i.e., non-functional).+defuncLet ::+  [TypeParam] ->+  [Pattern] ->+  Exp ->+  StructType ->+  DefM ([TypeParam], [Pattern], Exp, StaticVal)+defuncLet dims ps@(pat : pats) body rettype+  | patternOrderZero pat = do+    let bound_by_pat = (`S.member` patternDimNames pat) . typeParamName+        -- Take care to not include more size parameters than necessary.+        (pat_dims, rest_dims) = partition bound_by_pat dims+        env = envFromPattern pat <> envFromShapeParams pat_dims+    (rest_dims', pats', body', sv) <- localEnv env $ defuncLet rest_dims pats body rettype+    closure <- defuncFun dims ps body (mempty, rettype) mempty+    return (pat_dims ++ rest_dims', pat : pats', body', DynamicFun closure sv)+  | otherwise = do+    (e, sv) <- defuncFun dims ps body (mempty, rettype) mempty+    return ([], [], e, sv)+defuncLet _ [] body rettype = do+  (body', sv) <- defuncExp body+  return ([], [], body', imposeType sv rettype)+  where+    imposeType Dynamic {} t =+      Dynamic $ fromStruct t+    imposeType (RecordSV fs1) (Scalar (Record fs2)) =+      RecordSV $ M.toList $ M.intersectionWith imposeType (M.fromList fs1) fs2+    imposeType sv _ = sv++sizesForAll :: MonadFreshNames m => [Pattern] -> m ([VName], [Pattern])+sizesForAll params = do+  (params', sizes) <- runStateT (mapM (astMap tv) params) []+  return (sizes, params')+  where+    tv = identityMapper {mapOnPatternType = bitraverse onDim pure}+    onDim AnyDim = do+      v <- lift $ newVName "size"+      modify (v :)+      pure $ NamedDim $ qualName v+    onDim d = pure d++-- | Defunctionalize an application expression at a given depth of application.+-- Calls to dynamic (first-order) functions are preserved at much as possible,+-- but a new lifted function is created if a dynamic function is only partially+-- applied.+defuncApply :: Int -> Exp -> DefM (Exp, StaticVal)+defuncApply depth e@(Apply e1 e2 d t@(Info ret, Info ext) loc) = do+  let (argtypes, _) = unfoldFunType ret+  (e1', sv1) <- defuncApply (depth + 1) e1+  (e2', sv2) <- defuncExp e2+  let e' = Apply e1' e2' d t loc+  case sv1 of+    LambdaSV dims pat e0_t e0 closure_env -> do+      let env' = matchPatternSV pat sv2+          env_dim = envFromDimNames dims+      (e0', sv) <- localNewEnv (env' <> closure_env <> env_dim) $ defuncExtExp e0++      let closure_pat = buildEnvPattern closure_env+          pat' = updatePattern pat sv2++      globals <- asks fst++      -- Lift lambda to top-level function definition.  We put in+      -- a lot of effort to try to infer the uniqueness attributes+      -- of the lifted function, but this is ultimately all a sham+      -- and a hack.  There is some piece we're missing.+      let params = [closure_pat, pat']+          params_for_rettype = params ++ svParams sv1 ++ svParams sv2+          svParams (LambdaSV _ sv_pat _ _ _) = [sv_pat]+          svParams _ = []+          rettype = buildRetType closure_env params_for_rettype e0_t $ typeOf e0'++          already_bound =+            globals <> S.fromList dims+              <> S.map identName (foldMap patternIdents params)+          more_dims =+            S.toList $+              S.filter (`S.notMember` already_bound) $+                foldMap patternArraySizes params++          -- Embed some information about the original function+          -- into the name of the lifted function, to make the+          -- result slightly more human-readable.+          liftedName i (Var f _ _) =+            "lifted_" ++ show i ++ "_" ++ baseString (qualLeaf f)+          liftedName i (Apply f _ _ _ _) =+            liftedName (i + 1) f+          liftedName _ _ = "lifted"++      -- Ensure that no parameter sizes are AnyDim.  The internaliser+      -- expects this.  This is easy, because they are all+      -- first-order.+      (missing_dims, params') <- sizesForAll params++      fname <- newNameFromString $ liftedName (0 :: Int) e1+      liftValDec+        fname+        rettype+        (dims ++ more_dims ++ missing_dims)+        params'+        e0'++      let t1 = toStruct $ typeOf e1'+          t2 = toStruct $ typeOf e2'+          fname' = qualName fname+          fname'' =+            Var+              fname'+              ( Info+                  ( Scalar $+                      Arrow mempty Unnamed (fromStruct t1) $+                        Scalar $ Arrow mempty Unnamed (fromStruct t2) rettype+                  )+              )+              loc++          -- FIXME: what if this application returns both a function+          -- and a value?+          callret+            | orderZero ret = (Info ret, Info ext)+            | otherwise = (Info rettype, Info ext)++      return+        ( Parens+            ( Apply+                ( Apply+                    fname''+                    e1'+                    (Info (Observe, Nothing))+                    ( Info $ Scalar $ Arrow mempty Unnamed (fromStruct t2) rettype,+                      Info []+                    )+                    loc+                )+                e2'+                d+                callret+                loc+            )+            mempty,+          sv+        )++    -- If e1 is a dynamic function, we just leave the application in place,+    -- but we update the types since it may be partially applied or return+    -- a higher-order term.+    DynamicFun _ sv ->+      let (argtypes', rettype) = dynamicFunType sv argtypes+          restype = foldFunType argtypes' rettype `setAliases` aliases ret+          -- FIXME: what if this application returns both a function+          -- and a value?+          callret+            | orderZero ret = (Info ret, Info ext)+            | otherwise = (Info restype, Info ext)+          apply_e = Apply e1' e2' d callret loc+       in return (apply_e, sv)+    -- Propagate the 'IntrinsicsSV' until we reach the outermost application,+    -- where we construct a dynamic static value with the appropriate type.+    IntrinsicSV+      | depth == 0 ->+        -- If the intrinsic is fully applied, then we are done.+        -- Otherwise we need to eta-expand it and recursively+        -- defunctionalise. XXX: might it be better to simply+        -- eta-expand immediately any time we encounter a+        -- non-fully-applied intrinsic?+        if null argtypes+          then return (e', Dynamic $ typeOf e)+          else do+            (pats, body, tp) <- etaExpand (typeOf e') e'+            defuncExp $ Lambda pats body Nothing (Info (mempty, tp)) mempty+      | otherwise -> return (e', IntrinsicSV)+    _ ->+      error $+        "Application of an expression that is neither a static lambda "+          ++ "nor a dynamic function, but has static value: "+          ++ show sv1+defuncApply depth e@(Var qn (Info t) loc) = do+  let (argtypes, _) = unfoldFunType t+  sv <- lookupVar loc (qualLeaf qn)+  case sv of+    DynamicFun _ _+      | fullyApplied sv depth ->+        -- We still need to update the types in case the dynamic+        -- function returns a higher-order term.+        let (argtypes', rettype) = dynamicFunType sv argtypes+         in return (Var qn (Info (foldFunType argtypes' rettype)) loc, sv)+      | otherwise -> do+        fname <- newName $ qualLeaf qn+        let (dims, pats, e0, sv') = liftDynFun sv depth+            pats_names = S.map identName $ mconcat $ map patternIdents pats+            notInPats = (`S.notMember` pats_names)+            dims' = filter notInPats dims+            (argtypes', rettype) = dynamicFunType sv' argtypes+        liftValDec fname (fromStruct rettype) dims' pats e0+        return+          ( Var+              (qualName fname)+              (Info (foldFunType argtypes' $ fromStruct rettype))+              loc,+            sv'+          )+    IntrinsicSV -> return (e, IntrinsicSV)+    _ -> return (Var qn (Info (typeFromSV' sv)) loc, sv)+defuncApply depth (Parens e _) = defuncApply depth e+defuncApply _ expr = defuncExp expr++-- | Check if a 'StaticVal' and a given application depth corresponds+-- to a fully applied dynamic function.+fullyApplied :: StaticVal -> Int -> Bool+fullyApplied (DynamicFun _ sv) depth+  | depth == 0 = False+  | depth > 0 = fullyApplied sv (depth -1)+fullyApplied _ _ = True++-- | Converts a dynamic function 'StaticVal' into a list of+-- dimensions, a list of parameters, a function body, and the+-- appropriate static value for applying the function at the given+-- depth of partial application.+liftDynFun :: StaticVal -> Int -> ([VName], [Pattern], Exp, StaticVal)+liftDynFun (DynamicFun (e, sv) _) 0 = ([], [], e, sv)+liftDynFun (DynamicFun clsr@(_, LambdaSV dims pat _ _ _) sv) d+  | d > 0 =+    let (dims', pats, e', sv') = liftDynFun sv (d -1)+     in (nub $ dims ++ dims', pat : pats, e', DynamicFun clsr sv')+liftDynFun sv _ =+  error $+    "Tried to lift a StaticVal " ++ show sv+      ++ ", but expected a dynamic function."++-- | Converts a pattern to an environment that binds the individual names of the+-- pattern to their corresponding types wrapped in a 'Dynamic' static value.+envFromPattern :: Pattern -> Env+envFromPattern pat = case pat of+  TuplePattern ps _ -> foldMap envFromPattern ps+  RecordPattern fs _ -> foldMap (envFromPattern . snd) fs+  PatternParens p _ -> envFromPattern p+  Id vn (Info t) _ -> M.singleton vn $ Dynamic t+  Wildcard _ _ -> mempty+  PatternAscription p _ _ -> envFromPattern p+  PatternLit {} -> mempty+  PatternConstr _ _ ps _ -> foldMap envFromPattern ps++-- | Create an environment that binds the shape parameters.+envFromShapeParams :: [TypeParamBase VName] -> Env+envFromShapeParams = envFromDimNames . map dim+  where+    dim (TypeParamDim vn _) = vn+    dim tparam =+      error $+        "The defunctionalizer expects a monomorphic input program,\n"+          ++ "but it received a type parameter "+          ++ pretty tparam+          ++ " at "+          ++ locStr (srclocOf tparam)+          ++ "."++envFromDimNames :: [VName] -> Env+envFromDimNames = M.fromList . flip zip (repeat $ Dynamic $ Scalar $ Prim $ Signed Int64)++-- | Create a new top-level value declaration with the given function name,+-- return type, list of parameters, and body expression.+liftValDec :: VName -> PatternType -> [VName] -> [Pattern] -> Exp -> DefM ()+liftValDec fname rettype dims pats body = tell $ Seq.singleton dec+  where+    dims' = map (`TypeParamDim` mempty) dims+    -- FIXME: this pass is still not correctly size-preserving, so+    -- forget those return sizes that we forgot to propagate along+    -- the way.  Hopefully the internaliser is conservative and+    -- will insert reshapes...+    bound_here = S.fromList dims <> S.map identName (foldMap patternIdents pats)+    anyDimIfNotBound (NamedDim v)+      | qualLeaf v `S.member` bound_here = NamedDim v+      | otherwise = AnyDim+    anyDimIfNotBound d = d+    rettype_st = first anyDimIfNotBound $ toStruct rettype++    dec =+      ValBind+        { valBindEntryPoint = Nothing,+          valBindName = fname,+          valBindRetDecl = Nothing,+          valBindRetType = Info (rettype_st, []),+          valBindTypeParams = dims',+          valBindParams = pats,+          valBindBody = body,+          valBindDoc = Nothing,+          valBindAttrs = mempty,+          valBindLocation = mempty+        }++-- | Given a closure environment, construct a record pattern that+-- binds the closed over variables.+buildEnvPattern :: Env -> Pattern+buildEnvPattern env = RecordPattern (map buildField $ M.toList env) mempty+  where+    buildField (vn, sv) =+      ( nameFromString (pretty vn),+        Id vn (Info $ snd $ typeFromSV sv) mempty+      )++-- | Given a closure environment pattern and the type of a term,+-- construct the type of that term, where uniqueness is set to+-- `Nonunique` for those arrays that are bound in the environment or+-- pattern (except if they are unique there).  This ensures that a+-- lifted function can create unique arrays as long as they do not+-- alias any of its parameters.  XXX: it is not clear that this is a+-- sufficient property, unfortunately.+buildRetType :: Env -> [Pattern] -> StructType -> PatternType -> PatternType+buildRetType env pats = comb+  where+    bound = foldMap oneName (M.keys env) <> foldMap patternVars pats+    boundAsUnique v =+      maybe False (unique . unInfo . identType) $+        find ((== v) . identName) $ S.toList $ foldMap patternIdents pats+    problematic v = (v `member` bound) && not (boundAsUnique v)+    comb (Scalar (Record fs_annot)) (Scalar (Record fs_got)) =+      Scalar $ Record $ M.intersectionWith comb fs_annot fs_got+    comb (Scalar (Sum cs_annot)) (Scalar (Sum cs_got)) =+      Scalar $ Sum $ M.intersectionWith (zipWith comb) cs_annot cs_got+    comb (Scalar Arrow {}) t =+      descend t+    comb got et =+      descend $ fromStruct got `setAliases` aliases et++    descend t@Array {}+      | any (problematic . aliasVar) (aliases t) = t `setUniqueness` Nonunique+    descend (Scalar (Record t)) = Scalar $ Record $ fmap descend t+    descend t = t++-- | Compute the corresponding type for a given static value.+typeFromSV :: StaticVal -> ([VName], PatternType)+typeFromSV (Dynamic tp) =+  (mempty, tp)+typeFromSV (LambdaSV sizes _ _ _ env) =+  ( sizes <> env_sizes,+    Scalar $ Record $ M.fromList $ map (fmap snd) env'+  )+  where+    env' = map (bimap (nameFromString . pretty) typeFromSV) $ M.toList env+    env_sizes = concatMap (fst . snd) env'+typeFromSV (RecordSV ls) =+  let ts = map (fmap typeFromSV) ls+   in ( concatMap (fst . snd) ts,+        Scalar $ Record $ M.fromList $ map (fmap snd) ts+      )+typeFromSV (DynamicFun (_, sv) _) =+  typeFromSV sv+typeFromSV (SumSV name svs fields) =+  let (sizes, svs') = unzip $ map typeFromSV svs+   in ( concat sizes,+        Scalar $ Sum $ M.insert name svs' $ M.fromList fields+      )+typeFromSV IntrinsicSV =+  error "Tried to get the type from the static value of an intrinsic."++typeFromSV' :: StaticVal -> PatternType+typeFromSV' sv =+  let (sizes, t) = typeFromSV sv+   in unscopeType (S.fromList sizes) t++-- | Construct the type for a fully-applied dynamic function from its+-- static value and the original types of its arguments.+dynamicFunType :: StaticVal -> [PatternType] -> ([PatternType], PatternType)+dynamicFunType (DynamicFun _ sv) (p : ps) =+  let (ps', ret) = dynamicFunType sv ps in (p : ps', ret)+dynamicFunType sv _ = ([], typeFromSV' sv)++-- | Match a pattern with its static value. Returns an environment with+-- the identifier components of the pattern mapped to the corresponding+-- subcomponents of the static value.+matchPatternSV :: PatternBase Info VName -> StaticVal -> Env+matchPatternSV (TuplePattern ps _) (RecordSV ls) =+  mconcat $ zipWith (\p (_, sv) -> matchPatternSV p sv) ps ls+matchPatternSV (RecordPattern ps _) (RecordSV ls)+  | ps' <- sortOn fst ps,+    ls' <- sortOn fst ls,+    map fst ps' == map fst ls' =+    mconcat $ zipWith (\(_, p) (_, sv) -> matchPatternSV p sv) ps' ls'+matchPatternSV (PatternParens pat _) sv = matchPatternSV pat sv+matchPatternSV (Id vn (Info t) _) sv =+  -- When matching a pattern with a zero-order STaticVal, the type of+  -- the pattern wins out.  This is important when matching a+  -- nonunique pattern with a unique value.+  if orderZeroSV sv+    then M.singleton vn $ Dynamic t+    else M.singleton vn sv+matchPatternSV (Wildcard _ _) _ = mempty+matchPatternSV (PatternAscription pat _ _) sv = matchPatternSV pat sv+matchPatternSV PatternLit {} _ = mempty+matchPatternSV (PatternConstr c1 _ ps _) (SumSV c2 ls fs)+  | c1 == c2 =+    mconcat $ zipWith matchPatternSV ps ls+  | Just ts <- lookup c1 fs =+    mconcat $ zipWith matchPatternSV ps $ map svFromType ts+  | otherwise =+    error $ "matchPatternSV: missing constructor in type: " ++ pretty c1+matchPatternSV (PatternConstr c1 _ ps _) (Dynamic (Scalar (Sum fs)))+  | Just ts <- M.lookup c1 fs =+    mconcat $ zipWith matchPatternSV ps $ map svFromType ts+  | otherwise =+    error $ "matchPatternSV: missing constructor in type: " ++ pretty c1+matchPatternSV pat (Dynamic t) = matchPatternSV pat $ svFromType t+matchPatternSV pat sv =+  error $+    "Tried to match pattern " ++ pretty pat+      ++ " with static value "+      ++ show sv+      ++ "."++orderZeroSV :: StaticVal -> Bool+orderZeroSV Dynamic {} = True+orderZeroSV (RecordSV fields) = all (orderZeroSV . snd) fields+orderZeroSV _ = False++-- | Given a pattern and the static value for the defunctionalized argument,+-- update the pattern to reflect the changes in the types.+updatePattern :: Pattern -> StaticVal -> Pattern+updatePattern (TuplePattern ps loc) (RecordSV svs) =+  TuplePattern (zipWith updatePattern ps $ map snd svs) loc+updatePattern (RecordPattern ps loc) (RecordSV svs)+  | ps' <- sortOn fst ps,+    svs' <- sortOn fst svs =+    RecordPattern+      ( zipWith+          ( \(n, p) (_, sv) ->+              (n, updatePattern p sv)+          )+          ps'+          svs'+      )+      loc+updatePattern (PatternParens pat loc) sv =+  PatternParens (updatePattern pat sv) loc+updatePattern (Id vn (Info tp) loc) sv =+  Id vn (Info $ comb tp (snd (typeFromSV sv) `setUniqueness` Nonunique)) loc+  where+    -- Preserve any original zeroth-order types.+    comb (Scalar Arrow {}) t2 = t2+    comb (Scalar (Record m1)) (Scalar (Record m2)) =+      Scalar $ Record $ M.intersectionWith comb m1 m2+    comb (Scalar (Sum m1)) (Scalar (Sum m2)) =+      Scalar $ Sum $ M.intersectionWith (zipWith comb) m1 m2+    comb t1 _ = t1 -- t1 must be array or prim.+updatePattern pat@(Wildcard (Info tp) loc) sv+  | orderZero tp = pat+  | otherwise = Wildcard (Info $ snd $ typeFromSV sv) loc+updatePattern (PatternAscription pat tydecl loc) sv+  | orderZero . unInfo $ expandedType tydecl =+    PatternAscription (updatePattern pat sv) tydecl loc+  | otherwise = updatePattern pat sv+updatePattern p@PatternLit {} _ = p+updatePattern pat@(PatternConstr c1 (Info t) ps loc) sv@(SumSV _ svs _)+  | orderZero t = pat+  | otherwise = PatternConstr c1 (Info t') ps' loc+  where+    t' = snd (typeFromSV sv) `setUniqueness` Nonunique+    ps' = zipWith updatePattern ps svs+updatePattern (PatternConstr c1 _ ps loc) (Dynamic t) =+  PatternConstr c1 (Info t) ps loc+updatePattern pat (Dynamic t) = updatePattern pat (svFromType t)+updatePattern pat sv =+  error $+    "Tried to update pattern " ++ pretty pat+      ++ "to reflect the static value "+      ++ show sv++-- Like updatePattern, but discard sizes.  This is used for+-- let-bindings, where we might otherwise introduce sizes that are+-- free.+updatePattern' :: Pattern -> StaticVal -> Pattern+updatePattern' pat sv =+  let pat' = updatePattern pat sv+      (sizes, _) = typeFromSV sv+      tr =+        identityMapper+          { mapOnPatternType =+              pure . unscopeType (S.fromList sizes)+          }+   in runIdentity $ astMap tr pat'++-- | Convert a record (or tuple) type to a record static value. This is used for+-- "unwrapping" tuples and records that are nested in 'Dynamic' static values.+svFromType :: PatternType -> StaticVal+svFromType (Scalar (Record fs)) = RecordSV . M.toList $ M.map svFromType fs+svFromType t = Dynamic t++-- A set of names where we also track uniqueness.+newtype NameSet = NameSet (M.Map VName Uniqueness) deriving (Show)++instance Semigroup NameSet where+  NameSet x <> NameSet y = NameSet $ M.unionWith max x y++instance Monoid NameSet where+  mempty = NameSet mempty++without :: NameSet -> NameSet -> NameSet+without (NameSet x) (NameSet y) = NameSet $ x `M.difference` y++member :: VName -> NameSet -> Bool+member v (NameSet m) = v `M.member` m++ident :: Ident -> NameSet+ident v = NameSet $ M.singleton (identName v) (uniqueness $ unInfo $ identType v)++oneName :: VName -> NameSet+oneName v = NameSet $ M.singleton v Nonunique++names :: S.Set VName -> NameSet+names = foldMap oneName++-- | Compute the set of free variables of an expression.+freeVars :: Exp -> NameSet+freeVars expr = case expr of+  Literal {} -> mempty+  IntLit {} -> mempty+  FloatLit {} -> mempty+  StringLit {} -> mempty+  Parens e _ -> freeVars e+  QualParens _ e _ -> freeVars e+  TupLit es _ -> foldMap freeVars es+  RecordLit fs _ -> foldMap freeVarsField fs+    where+      freeVarsField (RecordFieldExplicit _ e _) = freeVars e+      freeVarsField (RecordFieldImplicit vn t _) = ident $ Ident vn t mempty+  ArrayLit es t _ ->+    foldMap freeVars es+      <> names (typeDimNames $ unInfo t)+  Range e me incl _ _ ->+    freeVars e <> foldMap freeVars me+      <> foldMap freeVars incl+  Var qn (Info t) _ -> NameSet $ M.singleton (qualLeaf qn) $ uniqueness t+  Ascript e t _ -> freeVars e <> names (typeDimNames $ unInfo $ expandedType t)+  Coerce e t _ _ -> freeVars e <> names (typeDimNames $ unInfo $ expandedType t)+  LetPat pat e1 e2 _ _ ->+    freeVars e1+      <> ( (names (patternDimNames pat) <> freeVars e2)+             `without` patternVars pat+         )+  LetFun vn (_, pats, _, _, e1) e2 _ _ ->+    ( (freeVars e1 <> names (foldMap patternDimNames pats))+        `without` foldMap patternVars pats+    )+      <> (freeVars e2 `without` oneName vn)+  If e1 e2 e3 _ _ -> freeVars e1 <> freeVars e2 <> freeVars e3+  Apply e1 e2 _ _ _ -> freeVars e1 <> freeVars e2+  Negate e _ -> freeVars e+  Lambda pats e0 _ _ _ ->+    (names (foldMap patternDimNames pats) <> freeVars e0)+      `without` foldMap patternVars pats+  OpSection {} -> mempty+  OpSectionLeft _ _ e _ _ _ -> freeVars e+  OpSectionRight _ _ e _ _ _ -> freeVars e+  ProjectSection {} -> mempty+  IndexSection idxs _ _ -> foldMap freeDimIndex idxs+  DoLoop sparams pat e1 form e3 _ _ ->+    let (e2fv, e2ident) = formVars form+     in freeVars e1 <> e2fv+          <> ( freeVars e3+                 `without` (names (S.fromList sparams) <> patternVars pat <> e2ident)+             )+    where+      formVars (For v e2) = (freeVars e2, ident v)+      formVars (ForIn p e2) = (freeVars e2, patternVars p)+      formVars (While e2) = (freeVars e2, mempty)+  BinOp (qn, _) _ (e1, _) (e2, _) _ _ _ ->+    oneName (qualLeaf qn)+      <> freeVars e1+      <> freeVars e2+  Project _ e _ _ -> freeVars e+  LetWith id1 id2 idxs e1 e2 _ _ ->+    ident id2 <> foldMap freeDimIndex idxs <> freeVars e1+      <> (freeVars e2 `without` ident id1)+  Index e idxs _ _ -> freeVars e <> foldMap freeDimIndex idxs+  Update e1 idxs e2 _ -> freeVars e1 <> foldMap freeDimIndex idxs <> freeVars e2+  RecordUpdate e1 _ e2 _ _ -> freeVars e1 <> freeVars e2+  Assert e1 e2 _ _ -> freeVars e1 <> freeVars e2+  Constr _ es _ _ -> foldMap freeVars es+  Attr _ e _ -> freeVars e+  Match e cs _ _ -> freeVars e <> foldMap caseFV cs+    where+      caseFV (CasePat p eCase _) =+        (names (patternDimNames p) <> freeVars eCase)+          `without` patternVars p++freeDimIndex :: DimIndexBase Info VName -> NameSet+freeDimIndex (DimFix e) = freeVars e+freeDimIndex (DimSlice me1 me2 me3) =+  foldMap (foldMap freeVars) [me1, me2, me3]++-- | Extract all the variable names bound in a pattern.+patternVars :: Pattern -> NameSet+patternVars = mconcat . map ident . S.toList . patternIdents++-- | Defunctionalize a top-level value binding. Returns the+-- transformed result as well as an environment that binds the name of+-- the value binding to the static value of the transformed body.  The+-- boolean is true if the function is a 'DynamicFun'.+defuncValBind :: ValBind -> DefM (ValBind, Env, Bool)+-- Eta-expand entry points with a functional return type.+defuncValBind (ValBind entry name _ (Info (rettype, retext)) tparams params body _ attrs loc)+  | Scalar Arrow {} <- rettype = do+    (body_pats, body', rettype') <- etaExpand (fromStruct rettype) body+    defuncValBind $+      ValBind+        entry+        name+        Nothing+        (Info (rettype', retext))+        tparams+        (params <> body_pats)+        body'+        Nothing+        attrs+        loc+defuncValBind valbind@(ValBind _ name retdecl (Info (rettype, retext)) tparams params body _ _ _) = do+  (tparams', params', body', sv) <- defuncLet tparams params body rettype+  let rettype' = combineTypeShapes rettype $ anySizes $ toStruct $ typeOf body'+  (missing_dims, params'') <- sizesForAll params'+  return+    ( valbind+        { valBindRetDecl = retdecl,+          valBindRetType =+            Info+              ( if null params'+                  then rettype' `setUniqueness` Nonunique+                  else rettype',+                retext+              ),+          valBindTypeParams =+            tparams'+              ++ map (`TypeParamDim` mempty) missing_dims,+          valBindParams = params'',+          valBindBody = body'+        },+      M.singleton name sv,+      case sv of+        DynamicFun {} -> True+        Dynamic {} -> True+        _ -> False+    )++-- | Defunctionalize a list of top-level declarations.+defuncVals :: [ValBind] -> DefM (Seq.Seq ValBind)+defuncVals [] = return mempty+defuncVals (valbind : ds) = do+  ((valbind', env, dyn), defs) <- collectFuns $ defuncValBind valbind+  ds' <-+    localEnv env $+      if dyn+        then isGlobal (valBindName valbind') $ defuncVals ds+        else defuncVals ds+  return $ defs <> Seq.singleton valbind' <> ds'++-- | Transform a list of top-level value bindings. May produce new+-- lifted function definitions, which are placed in front of the+-- resulting list of declarations.+transformProg :: MonadFreshNames m => [ValBind] -> m [ValBind]+transformProg decs = modifyNameSource $ \namesrc ->+  let (decs', namesrc', liftedDecs) = runDefM namesrc $ defuncVals decs+   in (toList $ liftedDecs <> decs', namesrc')
src/Futhark/Internalise/Defunctorise.hs view
@@ -1,22 +1,21 @@--- | Partially evaluate all modules away from a source Futhark--- program.  This is implemented as a source-to-source transformation. {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE Trustworthy #-}++-- | Partially evaluate all modules away from a source Futhark+-- program.  This is implemented as a source-to-source transformation. module Futhark.Internalise.Defunctorise (transformProg) where -import Control.Monad.RWS.Strict import Control.Monad.Identity+import Control.Monad.RWS.Strict import qualified Data.DList as DL import qualified Data.Map as M-import qualified Data.Set as S import Data.Maybe--import Prelude hiding (mod, abs)-+import qualified Data.Set as S import Futhark.MonadFreshNames import Language.Futhark+import Language.Futhark.Semantic (FileModule (..), Imports) import Language.Futhark.Traversals-import Language.Futhark.Semantic (Imports, FileModule(..))+import Prelude hiding (abs, mod)  -- | A substitution from names in the original program to names in the -- generated/residual program.@@ -24,53 +23,60 @@  lookupSubst :: VName -> Substitutions -> VName lookupSubst v substs = case M.lookup v substs of-                         Just v' | v' /= v -> lookupSubst v' substs-                         _ -> v+  Just v' | v' /= v -> lookupSubst v' substs+  _ -> v -data Mod = ModFun TySet Scope ModParam ModExp-           -- ^ A pairing of a lexical closure and a module function.-         | ModMod Scope-           -- ^ A non-parametric module.-         deriving (Show)+data Mod+  = -- | A pairing of a lexical closure and a module function.+    ModFun TySet Scope ModParam ModExp+  | -- | A non-parametric module.+    ModMod Scope+  deriving (Show)  modScope :: Mod -> Scope modScope (ModMod scope) = scope-modScope ModFun{} = mempty+modScope ModFun {} = mempty -data Scope = Scope { scopeSubsts :: Substitutions-                   , scopeMods :: M.Map VName Mod-                   }-         deriving (Show)+data Scope = Scope+  { scopeSubsts :: Substitutions,+    scopeMods :: M.Map VName Mod+  }+  deriving (Show)  lookupSubstInScope :: QualName VName -> Scope -> (QualName VName, Scope) lookupSubstInScope qn@(QualName quals name) scope@(Scope substs mods) =   case quals of     [] -> (qualName $ lookupSubst name substs, scope)-    q:qs ->+    q : qs ->       let q' = lookupSubst q substs-      in case M.lookup q' mods of-           Just (ModMod mod_scope) -> lookupSubstInScope (QualName qs name) mod_scope-           _ -> (qn, scope)+       in case M.lookup q' mods of+            Just (ModMod mod_scope) -> lookupSubstInScope (QualName qs name) mod_scope+            _ -> (qn, scope)  instance Semigroup Scope where-  Scope ss1 mt1 <> Scope ss2 mt2 = Scope (ss1<>ss2) (mt1<>mt2)+  Scope ss1 mt1 <> Scope ss2 mt2 = Scope (ss1 <> ss2) (mt1 <> mt2)  instance Monoid Scope where   mempty = Scope mempty mempty  type TySet = S.Set VName -data Env = Env { envScope :: Scope-               , envGenerating :: Bool-               , envImports :: M.Map String Scope-               , envAbs :: TySet-               }+data Env = Env+  { envScope :: Scope,+    envGenerating :: Bool,+    envImports :: M.Map String Scope,+    envAbs :: TySet+  }  newtype TransformM a = TransformM (RWS Env (DL.DList Dec) VNameSource a)-                   deriving (Applicative, Functor, Monad,-                             MonadFreshNames,-                             MonadReader Env,-                             MonadWriter (DL.DList Dec))+  deriving+    ( Applicative,+      Functor,+      Monad,+      MonadFreshNames,+      MonadReader Env,+      MonadWriter (DL.DList Dec)+    )  emit :: Dec -> TransformM () emit = tell . DL.singleton@@ -79,20 +85,24 @@ askScope = asks envScope  localScope :: (Scope -> Scope) -> TransformM a -> TransformM a-localScope f = local $ \env -> env { envScope = f $ envScope env }+localScope f = local $ \env -> env {envScope = f $ envScope env}  extendScope :: Scope -> TransformM a -> TransformM a extendScope (Scope substs mods) = localScope $ \scope ->-  scope { scopeSubsts = M.map (forward (scopeSubsts scope)) substs <> scopeSubsts scope-        , scopeMods = mods <> scopeMods scope }-  where forward old_substs v = fromMaybe v $ M.lookup v old_substs+  scope+    { scopeSubsts = M.map (forward (scopeSubsts scope)) substs <> scopeSubsts scope,+      scopeMods = mods <> scopeMods scope+    }+  where+    forward old_substs v = fromMaybe v $ M.lookup v old_substs  substituting :: Substitutions -> TransformM a -> TransformM a-substituting substs = extendScope mempty { scopeSubsts = substs }+substituting substs = extendScope mempty {scopeSubsts = substs}  boundName :: VName -> TransformM VName-boundName v = do g <- asks envGenerating-                 if g then newName v else return v+boundName v = do+  g <- asks envGenerating+  if g then newName v else return v  bindingNames :: [VName] -> TransformM Scope -> TransformM Scope bindingNames names m = do@@ -101,35 +111,41 @@   substituting substs $ mappend <$> m <*> pure (Scope substs mempty)  generating :: TransformM a -> TransformM a-generating = local $ \env -> env { envGenerating = True }+generating = local $ \env -> env {envGenerating = True}  bindingImport :: String -> Scope -> TransformM a -> TransformM a bindingImport name scope = local $ \env ->-  env { envImports = M.insert name scope $ envImports env }+  env {envImports = M.insert name scope $ envImports env}  bindingAbs :: TySet -> TransformM a -> TransformM a bindingAbs abs = local $ \env ->-  env { envAbs = abs <> envAbs env }+  env {envAbs = abs <> envAbs env}  lookupImport :: String -> TransformM Scope lookupImport name = maybe bad return =<< asks (M.lookup name . envImports)-  where bad = error $ "Unknown import: " ++ name+  where+    bad = error $ "Unknown import: " ++ name  lookupMod' :: QualName VName -> Scope -> Either String Mod lookupMod' mname scope =   let (mname', scope') = lookupSubstInScope mname scope-  in maybe (Left $ bad mname') Right $ M.lookup (qualLeaf mname') $ scopeMods scope'-  where bad mname' = "Unknown module: " ++ pretty mname ++ " (" ++ pretty mname' ++ ")"+   in maybe (Left $ bad mname') Right $ M.lookup (qualLeaf mname') $ scopeMods scope'+  where+    bad mname' = "Unknown module: " ++ pretty mname ++ " (" ++ pretty mname' ++ ")"  lookupMod :: QualName VName -> TransformM Mod lookupMod mname = either error return . lookupMod' mname =<< askScope  runTransformM :: VNameSource -> TransformM a -> (a, VNameSource, DL.DList Dec) runTransformM src (TransformM m) = runRWS m env src-  where env = Env mempty False mempty mempty+  where+    env = Env mempty False mempty mempty -maybeAscript :: SrcLoc -> Maybe (SigExp, Info (M.Map VName VName)) -> ModExp-             -> ModExp+maybeAscript ::+  SrcLoc ->+  Maybe (SigExp, Info (M.Map VName VName)) ->+  ModExp ->+  ModExp maybeAscript loc (Just (mtye, substs)) me = ModAscript me mtye substs loc maybeAscript _ Nothing me = me @@ -137,12 +153,14 @@ substituteInMod substs (ModMod (Scope mod_substs mod_mods)) =   -- Forward all substitutions.   ModMod $ Scope substs' $ M.map (substituteInMod substs) mod_mods-  where forward v = lookupSubst v $ mod_substs <> substs-        substs' = M.map forward substs+  where+    forward v = lookupSubst v $ mod_substs <> substs+    substs' = M.map forward substs substituteInMod substs (ModFun abs (Scope mod_substs mod_mods) mparam mbody) =-  ModFun abs (Scope (substs'<>mod_substs) mod_mods) mparam mbody-  where forward v = lookupSubst v mod_substs-        substs' = M.map forward substs+  ModFun abs (Scope (substs' <> mod_substs) mod_mods) mparam mbody+  where+    forward v = lookupSubst v mod_substs+    substs' = M.map forward substs  extendAbsTypes :: Substitutions -> TransformM a -> TransformM a extendAbsTypes ascript_substs m = do@@ -150,8 +168,11 @@   -- Some abstract types may have a different name on the inside, and   -- we need to make them visible, because substitutions involving   -- abstract types must be lifted out in transformModBind.-  let subst_abs = S.fromList $ map snd $ filter ((`S.member` abs) . fst) $-                  M.toList ascript_substs+  let subst_abs =+        S.fromList $+          map snd $+            filter ((`S.member` abs) . fst) $+              M.toList ascript_substs   bindingAbs subst_abs m  evalModExp :: ModExp -> TransformM Mod@@ -161,7 +182,7 @@ evalModExp (ModImport _ (Info fpath) _) = ModMod <$> lookupImport fpath evalModExp (ModAscript me _ (Info ascript_substs) _) =   extendAbsTypes ascript_substs $-  substituteInMod ascript_substs <$> evalModExp me+    substituteInMod ascript_substs <$> evalModExp me evalModExp (ModApply f arg (Info p_substs) (Info b_substs) loc) = do   f_mod <- evalModExp f   arg_mod <- evalModExp arg@@ -170,33 +191,43 @@       error $ "Cannot apply non-parametric module at " ++ locStr loc     ModFun f_abs f_closure f_p f_body ->       bindingAbs (f_abs <> S.fromList (unInfo (modParamAbs f_p))) $-      extendAbsTypes b_substs $ extendScope f_closure $ generating $ do-        outer_substs <- scopeSubsts <$> askScope-        abs <- asks envAbs-        let forward (k,v) = (lookupSubst k outer_substs, v)-            p_substs' = M.fromList $ map forward $ M.toList p_substs-            abs_substs = M.filterWithKey (const . flip S.member abs) $-                         M.map (`lookupSubst` scopeSubsts (modScope arg_mod)) p_substs' <>-                         scopeSubsts f_closure <>-                         scopeSubsts (modScope arg_mod)-        extendScope (Scope abs_substs (M.singleton (modParamName f_p) $-                                       substituteInMod p_substs' arg_mod)) $ do-          substs <- scopeSubsts <$> askScope-          x <- evalModExp f_body-          return $-            addSubsts abs abs_substs $-            -- The next one is dubious, but is necessary to-            -- propagate substitutions from the argument (see-            -- modules/functor24.fut).-            addSubstsModMod (scopeSubsts $ modScope arg_mod) $-            substituteInMod (b_substs <> substs) x-  where addSubsts abs substs (ModFun mabs (Scope msubsts mods) mp me) =-          ModFun (abs<>mabs) (Scope (substs<>msubsts) mods) mp me-        addSubsts _ substs (ModMod (Scope msubsts mods)) =-          ModMod $ Scope (substs<>msubsts) mods-        addSubstsModMod substs (ModMod (Scope msubsts mods)) =-          ModMod $ Scope (substs<>msubsts) mods-        addSubstsModMod _ m = m+        extendAbsTypes b_substs $+          extendScope f_closure $+            generating $ do+              outer_substs <- scopeSubsts <$> askScope+              abs <- asks envAbs+              let forward (k, v) = (lookupSubst k outer_substs, v)+                  p_substs' = M.fromList $ map forward $ M.toList p_substs+                  abs_substs =+                    M.filterWithKey (const . flip S.member abs) $+                      M.map (`lookupSubst` scopeSubsts (modScope arg_mod)) p_substs'+                        <> scopeSubsts f_closure+                        <> scopeSubsts (modScope arg_mod)+              extendScope+                ( Scope+                    abs_substs+                    ( M.singleton (modParamName f_p) $+                        substituteInMod p_substs' arg_mod+                    )+                )+                $ do+                  substs <- scopeSubsts <$> askScope+                  x <- evalModExp f_body+                  return $+                    addSubsts abs abs_substs $+                      -- The next one is dubious, but is necessary to+                      -- propagate substitutions from the argument (see+                      -- modules/functor24.fut).+                      addSubstsModMod (scopeSubsts $ modScope arg_mod) $+                        substituteInMod (b_substs <> substs) x+  where+    addSubsts abs substs (ModFun mabs (Scope msubsts mods) mp me) =+      ModFun (abs <> mabs) (Scope (substs <> msubsts) mods) mp me+    addSubsts _ substs (ModMod (Scope msubsts mods)) =+      ModMod $ Scope (substs <> msubsts) mods+    addSubstsModMod substs (ModMod (Scope msubsts mods)) =+      ModMod $ Scope (substs <> msubsts) mods+    addSubstsModMod _ m = m evalModExp (ModLambda p ascript e loc) = do   scope <- askScope   abs <- asks envAbs@@ -210,24 +241,26 @@ transformNames x = do   scope <- askScope   return $ runIdentity $ astMap (substituter scope) x-  where substituter scope =-          ASTMapper { mapOnExp = onExp scope-                    , mapOnName = \v ->-                        return $ qualLeaf $ fst $ lookupSubstInScope (qualName v) scope-                    , mapOnQualName = \v ->-                        return $ fst $ lookupSubstInScope v scope-                    , mapOnStructType = astMap (substituter scope)-                    , mapOnPatternType = astMap (substituter scope)-                    }-        onExp scope e =-          -- One expression is tricky, because it interacts with scoping rules.-          case e of-            QualParens (mn, _) e' _ ->-              case lookupMod' mn scope of-                Left err -> error err-                Right mod ->-                  astMap (substituter $ modScope mod<>scope) e'-            _ -> astMap (substituter scope) e+  where+    substituter scope =+      ASTMapper+        { mapOnExp = onExp scope,+          mapOnName = \v ->+            return $ qualLeaf $ fst $ lookupSubstInScope (qualName v) scope,+          mapOnQualName = \v ->+            return $ fst $ lookupSubstInScope v scope,+          mapOnStructType = astMap (substituter scope),+          mapOnPatternType = astMap (substituter scope)+        }+    onExp scope e =+      -- One expression is tricky, because it interacts with scoping rules.+      case e of+        QualParens (mn, _) e' _ ->+          case lookupMod' mn scope of+            Left err -> error err+            Right mod ->+              astMap (substituter $ modScope mod <> scope) e'+        _ -> astMap (substituter scope) e  transformTypeExp :: TypeExp VName -> TransformM (TypeExp VName) transformTypeExp = transformNames@@ -255,21 +288,29 @@ transformTypeBind :: TypeBind -> TransformM () transformTypeBind (TypeBind name l tparams te doc loc) = do   name' <- transformName name-  emit . TypeDec =<< (TypeBind name' l <$> traverse transformNames tparams-                      <*> transformTypeDecl te <*> pure doc <*> pure loc)+  emit . TypeDec+    =<< ( TypeBind name' l <$> traverse transformNames tparams+            <*> transformTypeDecl te+            <*> pure doc+            <*> pure loc+        )  transformModBind :: ModBind -> TransformM Scope transformModBind mb = do   let addParam p me = ModLambda p Nothing me $ srclocOf me-  mod <- evalModExp $ foldr addParam-         (maybeAscript (srclocOf mb) (modSignature mb) $ modExp mb) $-         modParams mb+  mod <-+    evalModExp $+      foldr+        addParam+        (maybeAscript (srclocOf mb) (modSignature mb) $ modExp mb)+        $ modParams mb   mname <- transformName $ modName mb   abs <- asks envAbs   -- Copy substitutions involving abstract types out, because they are   -- always resolved at the outermost level.-  let abs_substs = M.filterWithKey (const . flip S.member abs) $-                   scopeSubsts $ modScope mod+  let abs_substs =+        M.filterWithKey (const . flip S.member abs) $+          scopeSubsts $ modScope mod   return $ Scope abs_substs $ M.singleton mname mod  transformDecs :: [Dec] -> TransformM Scope@@ -298,28 +339,32 @@       extendScope scope $ mappend <$> transformDecs ds' <*> pure scope     ImportDec name name' loc : ds' ->       let d = LocalDec (OpenDec (ModImport name name' loc) loc) loc-      in transformDecs $ d : ds'+       in transformDecs $ d : ds'  transformImports :: Imports -> TransformM () transformImports [] = return ()-transformImports ((name,imp):imps) = do+transformImports ((name, imp) : imps) = do   let abs = S.fromList $ map qualLeaf $ M.keys $ fileAbs imp-  scope <- censor (fmap maybeHideEntryPoint) $-           bindingAbs abs $ transformDecs $ progDecs $ fileProg imp+  scope <-+    censor (fmap maybeHideEntryPoint) $+      bindingAbs abs $ transformDecs $ progDecs $ fileProg imp   bindingAbs abs $ bindingImport name scope $ transformImports imps   where     -- Only the "main" file (last import) is allowed to have entry points.     permit_entry_points = null imps      maybeHideEntryPoint (ValDec vdec) =-      ValDec vdec { valBindEntryPoint =-                      if permit_entry_points-                      then valBindEntryPoint vdec-                      else Nothing  }+      ValDec+        vdec+          { valBindEntryPoint =+              if permit_entry_points+                then valBindEntryPoint vdec+                else Nothing+          }     maybeHideEntryPoint d = d  -- | Perform defunctorisation. transformProg :: MonadFreshNames m => Imports -> m [Dec] transformProg prog = modifyNameSource $ \namesrc ->   let ((), namesrc', prog') = runTransformM namesrc $ transformImports prog-  in (DL.toList prog', namesrc')+   in (DL.toList prog', namesrc')
src/Futhark/Internalise/Lambdas.hs view
@@ -1,124 +1,151 @@ {-# LANGUAGE FlexibleContexts #-}+ module Futhark.Internalise.Lambdas-  ( InternaliseLambda-  , internaliseMapLambda-  , internaliseStreamMapLambda-  , internaliseFoldLambda-  , internaliseStreamLambda-  , internalisePartitionLambda+  ( InternaliseLambda,+    internaliseMapLambda,+    internaliseStreamMapLambda,+    internaliseFoldLambda,+    internaliseStreamLambda,+    internalisePartitionLambda,   )-  where+where -import Language.Futhark as E import Futhark.IR.SOACS as I-import Futhark.MonadFreshNames-import Futhark.Internalise.Monad import Futhark.Internalise.AccurateSizes+import Futhark.Internalise.Monad+import Language.Futhark as E  -- | A function for internalising lambdas. type InternaliseLambda =   E.Exp -> [I.Type] -> InternaliseM ([I.LParam], I.Body, [I.Type]) -internaliseMapLambda :: InternaliseLambda-                     -> E.Exp-                     -> [I.SubExp]-                     -> InternaliseM I.Lambda+internaliseMapLambda ::+  InternaliseLambda ->+  E.Exp ->+  [I.SubExp] ->+  InternaliseM I.Lambda internaliseMapLambda internaliseLambda lam args = do   argtypes <- mapM I.subExpType args   let rowtypes = map I.rowType argtypes   (params, body, rettype) <- internaliseLambda lam rowtypes-  body' <- localScope (scopeOfLParams params) $-           ensureResultShape-           (ErrorMsg [ErrorString "not all iterations produce same shape"])-           (srclocOf lam) rettype body+  body' <-+    localScope (scopeOfLParams params) $+      ensureResultShape+        (ErrorMsg [ErrorString "not all iterations produce same shape"])+        (srclocOf lam)+        rettype+        body   return $ I.Lambda params body' rettype -internaliseStreamMapLambda :: InternaliseLambda-                           -> E.Exp-                           -> [I.SubExp]-                           -> InternaliseM I.Lambda+internaliseStreamMapLambda ::+  InternaliseLambda ->+  E.Exp ->+  [I.SubExp] ->+  InternaliseM I.Lambda internaliseStreamMapLambda internaliseLambda lam args = do   chunk_size <- newVName "chunk_size"-  let chunk_param = I.Param chunk_size (I.Prim int32)+  let chunk_param = I.Param chunk_size (I.Prim int64)       outer = (`setOuterSize` I.Var chunk_size)   localScope (scopeOfLParams [chunk_param]) $ do     argtypes <- mapM I.subExpType args     (lam_params, orig_body, rettype) <--      internaliseLambda lam $ I.Prim int32 : map outer argtypes+      internaliseLambda lam $ I.Prim int64 : map outer argtypes     let orig_chunk_param : params = lam_params     body <- runBodyBinder $ do       letBindNames [paramName orig_chunk_param] $ I.BasicOp $ I.SubExp $ I.Var chunk_size       return orig_body-    body' <- localScope (scopeOfLParams params) $ insertStmsM $ do-      letBindNames [paramName orig_chunk_param] $ I.BasicOp $ I.SubExp $ I.Var chunk_size-      ensureResultShape-        (ErrorMsg [ErrorString "not all iterations produce same shape"])-        (srclocOf lam) (map outer rettype) body-    return $ I.Lambda (chunk_param:params) body' (map outer rettype)+    body' <- localScope (scopeOfLParams params) $+      insertStmsM $ do+        letBindNames [paramName orig_chunk_param] $ I.BasicOp $ I.SubExp $ I.Var chunk_size+        ensureResultShape+          (ErrorMsg [ErrorString "not all iterations produce same shape"])+          (srclocOf lam)+          (map outer rettype)+          body+    return $ I.Lambda (chunk_param : params) body' (map outer rettype) -internaliseFoldLambda :: InternaliseLambda-                      -> E.Exp-                      -> [I.Type] -> [I.Type]-                      -> InternaliseM I.Lambda+internaliseFoldLambda ::+  InternaliseLambda ->+  E.Exp ->+  [I.Type] ->+  [I.Type] ->+  InternaliseM I.Lambda internaliseFoldLambda internaliseLambda lam acctypes arrtypes = do   let rowtypes = map I.rowType arrtypes   (params, body, rettype) <- internaliseLambda lam $ acctypes ++ rowtypes-  let rettype' = [ t `I.setArrayShape` I.arrayShape shape-                   | (t,shape) <- zip rettype acctypes ]+  let rettype' =+        [ t `I.setArrayShape` I.arrayShape shape+          | (t, shape) <- zip rettype acctypes+        ]   -- The result of the body must have the exact same shape as the   -- initial accumulator.  We accomplish this with an assertion and   -- reshape().-  body' <- localScope (scopeOfLParams params) $-           ensureResultShape-           (ErrorMsg [ErrorString "shape of result does not match shape of initial value"])-           (srclocOf lam) rettype' body+  body' <-+    localScope (scopeOfLParams params) $+      ensureResultShape+        (ErrorMsg [ErrorString "shape of result does not match shape of initial value"])+        (srclocOf lam)+        rettype'+        body   return $ I.Lambda params body' rettype' -internaliseStreamLambda :: InternaliseLambda-                        -> E.Exp-                        -> [I.Type]-                        -> InternaliseM ([LParam], Body)+internaliseStreamLambda ::+  InternaliseLambda ->+  E.Exp ->+  [I.Type] ->+  InternaliseM ([LParam], Body) internaliseStreamLambda internaliseLambda lam rowts = do   chunk_size <- newVName "chunk_size"-  let chunk_param = I.Param chunk_size $ I.Prim int32+  let chunk_param = I.Param chunk_size $ I.Prim int64       chunktypes = map (`arrayOfRow` I.Var chunk_size) rowts   localScope (scopeOfLParams [chunk_param]) $ do     (lam_params, orig_body, _) <--      internaliseLambda lam $ I.Prim int32 : chunktypes+      internaliseLambda lam $ I.Prim int64 : chunktypes     let orig_chunk_param : params = lam_params     body <- runBodyBinder $ do       letBindNames [paramName orig_chunk_param] $ I.BasicOp $ I.SubExp $ I.Var chunk_size       return orig_body-    return (chunk_param:params, body)+    return (chunk_param : params, body)  -- Given @k@ lambdas, this will return a lambda that returns an -- (k+2)-element tuple of integers.  The first element is the -- equivalence class ID in the range [0,k].  The remaining are all zero -- except for possibly one element.-internalisePartitionLambda :: InternaliseLambda-                           -> Int-                           -> E.Exp-                           -> [I.SubExp]-                           -> InternaliseM I.Lambda+internalisePartitionLambda ::+  InternaliseLambda ->+  Int ->+  E.Exp ->+  [I.SubExp] ->+  InternaliseM I.Lambda internalisePartitionLambda internaliseLambda k lam args = do   argtypes <- mapM I.subExpType args   let rowtypes = map I.rowType argtypes   (params, body, _) <- internaliseLambda lam rowtypes-  body' <- localScope (scopeOfLParams params) $-           lambdaWithIncrement body+  body' <-+    localScope (scopeOfLParams params) $+      lambdaWithIncrement body   return $ I.Lambda params body' rettype-  where rettype = replicate (k+2) $ I.Prim int32-        result i = map constant $ (fromIntegral i :: Int32) :-                   (replicate i 0 ++ [1::Int32] ++ replicate (k-i) 0)+  where+    rettype = replicate (k + 2) $ I.Prim int64+    result i =+      map constant $+        fromIntegral i :+        (replicate i 0 ++ [1 :: Int64] ++ replicate (k - i) 0) -        mkResult _ i | i >= k = return $ result i-        mkResult eq_class i = do-          is_i <- letSubExp "is_i" $ BasicOp $ CmpOp (CmpEq int32) eq_class (constant i)-          fmap (map I.Var) . letTupExp "part_res" =<<-            eIf (eSubExp is_i) (pure $ resultBody $ result i)-                               (resultBody <$> mkResult eq_class (i+1))+    mkResult _ i | i >= k = return $ result i+    mkResult eq_class i = do+      is_i <-+        letSubExp "is_i" $+          BasicOp $+            CmpOp (CmpEq int64) eq_class $+              intConst Int64 $ toInteger i+      fmap (map I.Var) . letTupExp "part_res"+        =<< eIf+          (eSubExp is_i)+          (pure $ resultBody $ result i)+          (resultBody <$> mkResult eq_class (i + 1)) -        lambdaWithIncrement :: I.Body -> InternaliseM I.Body-        lambdaWithIncrement lam_body = runBodyBinder $ do-          eq_class <- head <$> bodyBind lam_body-          resultBody <$> mkResult eq_class 0+    lambdaWithIncrement :: I.Body -> InternaliseM I.Body+    lambdaWithIncrement lam_body = runBodyBinder $ do+      eq_class <- head <$> bodyBind lam_body+      resultBody <$> mkResult eq_class 0
src/Futhark/Internalise/Monad.hs view
@@ -1,46 +1,38 @@ {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Trustworthy #-}-module Futhark.Internalise.Monad-  ( InternaliseM-  , runInternaliseM-  , throwError-  , VarSubstitutions-  , InternaliseEnv (..)-  , Closure-  , FunInfo--  , substitutingVars-  , lookupSubst-  , addFunDef--  , lookupFunction-  , lookupFunction'-  , lookupConst-  , allConsts--  , bindFunction-  , bindConstant--  , localConstsScope+{-# LANGUAGE TypeFamilies #-} -  , assert+module Futhark.Internalise.Monad+  ( InternaliseM,+    runInternaliseM,+    throwError,+    VarSubstitutions,+    InternaliseEnv (..),+    Closure,+    FunInfo,+    substitutingVars,+    lookupSubst,+    addFunDef,+    lookupFunction,+    lookupFunction',+    lookupConst,+    allConsts,+    bindFunction,+    bindConstant,+    localConstsScope,+    assert,      -- * Convenient reexports-  , module Futhark.Tools+    module Futhark.Tools,   )-  where+where  import Control.Monad.Except-import Control.Monad.State-import Control.Monad.Reader-import Control.Monad.Writer import Control.Monad.RWS import qualified Data.Map.Strict as M- import Futhark.IR.SOACS import Futhark.MonadFreshNames import Futhark.Tools@@ -50,10 +42,14 @@ -- corresponds to the closure of a locally defined function. type Closure = [VName] -type FunInfo = (Name, Closure,-                [VName], [DeclType],-                [FParam],-                [(SubExp,Type)] -> Maybe [DeclExtType])+type FunInfo =+  ( Name,+    Closure,+    [VName],+    [DeclType],+    [FParam],+    [(SubExp, Type)] -> Maybe [DeclExtType]+  )  type FunTable = M.Map VName FunInfo @@ -61,46 +57,55 @@ -- internalised subexpressions. type VarSubstitutions = M.Map VName [SubExp] -data InternaliseEnv = InternaliseEnv {-    envSubsts :: VarSubstitutions-  , envDoBoundsChecks :: Bool-  , envSafe :: Bool-  , envAttrs :: Attrs+data InternaliseEnv = InternaliseEnv+  { envSubsts :: VarSubstitutions,+    envDoBoundsChecks :: Bool,+    envSafe :: Bool,+    envAttrs :: Attrs   } -data InternaliseState = InternaliseState {-    stateNameSource :: VNameSource-  , stateFunTable :: FunTable-  , stateConstSubsts :: VarSubstitutions-  , stateConstScope :: Scope SOACS-  , stateConsts :: Names+data InternaliseState = InternaliseState+  { stateNameSource :: VNameSource,+    stateFunTable :: FunTable,+    stateConstSubsts :: VarSubstitutions,+    stateConstScope :: Scope SOACS,+    stateConsts :: Names   }  data InternaliseResult = InternaliseResult (Stms SOACS) [FunDef SOACS]  instance Semigroup InternaliseResult where   InternaliseResult xs1 ys1 <> InternaliseResult xs2 ys2 =-    InternaliseResult (xs1<>xs2) (ys1<>ys2)+    InternaliseResult (xs1 <> xs2) (ys1 <> ys2)  instance Monoid InternaliseResult where   mempty = InternaliseResult mempty mempty -newtype InternaliseM  a = InternaliseM (BinderT SOACS-                                        (RWS-                                         InternaliseEnv-                                         InternaliseResult-                                         InternaliseState)-                                        a)-  deriving (Functor, Applicative, Monad,-            MonadReader InternaliseEnv,-            MonadState InternaliseState,-            MonadFreshNames,-            HasScope SOACS,-            LocalScope SOACS)+newtype InternaliseM a+  = InternaliseM+      ( BinderT+          SOACS+          ( RWS+              InternaliseEnv+              InternaliseResult+              InternaliseState+          )+          a+      )+  deriving+    ( Functor,+      Applicative,+      Monad,+      MonadReader InternaliseEnv,+      MonadState InternaliseState,+      MonadFreshNames,+      HasScope SOACS,+      LocalScope SOACS+    )  instance (Monoid w, Monad m) => MonadFreshNames (RWST r w InternaliseState m) where   getNameSource = gets stateNameSource-  putNameSource src = modify $ \s -> s { stateNameSource = src }+  putNameSource src = modify $ \s -> s {stateNameSource = src}  instance MonadBinder InternaliseM where   type Lore InternaliseM = SOACS@@ -111,30 +116,35 @@   addStms = InternaliseM . addStms   collectStms (InternaliseM m) = InternaliseM $ collectStms m -runInternaliseM :: MonadFreshNames m =>-                   Bool -> InternaliseM ()-                -> m (Stms SOACS, [FunDef SOACS])+runInternaliseM ::+  MonadFreshNames m =>+  Bool ->+  InternaliseM () ->+  m (Stms SOACS, [FunDef SOACS]) runInternaliseM safe (InternaliseM m) =   modifyNameSource $ \src ->-  let ((_, consts), s, InternaliseResult _ funs) =-        runRWS (runBinderT m mempty) newEnv (newState src)-  in ((consts, funs), stateNameSource s)-  where newEnv = InternaliseEnv {-                   envSubsts = mempty-                 , envDoBoundsChecks = True-                 , envSafe = safe-                 , envAttrs = mempty-                 }-        newState src =-          InternaliseState { stateNameSource = src-                           , stateFunTable = mempty-                           , stateConstSubsts = mempty-                           , stateConsts = mempty-                           , stateConstScope = mempty-                           }+    let ((_, consts), s, InternaliseResult _ funs) =+          runRWS (runBinderT m mempty) newEnv (newState src)+     in ((consts, funs), stateNameSource s)+  where+    newEnv =+      InternaliseEnv+        { envSubsts = mempty,+          envDoBoundsChecks = True,+          envSafe = safe,+          envAttrs = mempty+        }+    newState src =+      InternaliseState+        { stateNameSource = src,+          stateFunTable = mempty,+          stateConstSubsts = mempty,+          stateConsts = mempty,+          stateConstScope = mempty+        }  substitutingVars :: VarSubstitutions -> InternaliseM a -> InternaliseM a-substitutingVars substs = local $ \env -> env { envSubsts = substs <> envSubsts env }+substitutingVars substs = local $ \env -> env {envSubsts = substs <> envSubsts env}  lookupSubst :: VName -> InternaliseM (Maybe [SubExp]) lookupSubst v = do@@ -152,7 +162,8 @@  lookupFunction :: VName -> InternaliseM FunInfo lookupFunction fname = maybe bad return =<< lookupFunction' fname-  where bad = error $ "Internalise.lookupFunction: Function '" ++ pretty fname ++ "' not found."+  where+    bad = error $ "Internalise.lookupFunction: Function '" ++ pretty fname ++ "' not found."  lookupConst :: VName -> InternaliseM (Maybe [SubExp]) lookupConst fname = gets $ M.lookup fname . stateConstSubsts@@ -163,19 +174,21 @@ bindFunction :: VName -> FunDef SOACS -> FunInfo -> InternaliseM () bindFunction fname fd info = do   addFunDef fd-  modify $ \s -> s { stateFunTable = M.insert fname info $ stateFunTable s }+  modify $ \s -> s {stateFunTable = M.insert fname info $ stateFunTable s}  bindConstant :: VName -> FunDef SOACS -> InternaliseM () bindConstant cname fd = do   let stms = bodyStms $ funDefBody fd-      substs = takeLast (length (funDefRetType fd)) $-               bodyResult $ funDefBody fd+      substs =+        takeLast (length (funDefRetType fd)) $+          bodyResult $ funDefBody fd       const_names = namesFromList $ M.keys $ scopeOf stms   addStms stms   modify $ \s ->-    s { stateConstSubsts = M.insert cname substs $ stateConstSubsts s-      , stateConstScope = scopeOf stms <> stateConstScope s-      , stateConsts = const_names <> stateConsts s+    s+      { stateConstSubsts = M.insert cname substs $ stateConstSubsts s,+        stateConstScope = scopeOf stms <> stateConstScope s,+        stateConsts = const_names <> stateConsts s       }  localConstsScope :: InternaliseM a -> InternaliseM a@@ -186,25 +199,32 @@ -- | Construct an 'Assert' statement, but taking attributes into -- account.  Always use this function, and never construct 'Assert' -- directly in the internaliser!-assert :: String -> SubExp -> ErrorMsg SubExp -> SrcLoc-       -> InternaliseM Certificates+assert ::+  String ->+  SubExp ->+  ErrorMsg SubExp ->+  SrcLoc ->+  InternaliseM Certificates assert desc se msg loc = assertingOne $ do   attrs <- asks $ attrsForAssert . envAttrs-  attributing attrs $ letExp desc $-    BasicOp $ Assert se msg (loc, mempty)+  attributing attrs $+    letExp desc $+      BasicOp $ Assert se msg (loc, mempty)  -- | Execute the given action if 'envDoBoundsChecks' is true, otherwise -- just return an empty list.-asserting :: InternaliseM Certificates-          -> InternaliseM Certificates+asserting ::+  InternaliseM Certificates ->+  InternaliseM Certificates asserting m = do   doBoundsChecks <- asks envDoBoundsChecks   if doBoundsChecks-  then m-  else return mempty+    then m+    else return mempty  -- | Execute the given action if 'envDoBoundsChecks' is true, otherwise -- just return an empty list.-assertingOne :: InternaliseM VName-             -> InternaliseM Certificates+assertingOne ::+  InternaliseM VName ->+  InternaliseM Certificates assertingOne m = asserting $ Certificates . pure <$> m
src/Futhark/Internalise/Monomorphise.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE Trustworthy #-}+ -- | This monomorphization module converts a well-typed, polymorphic, -- module-free Futhark program into an equivalent monomorphic program. --@@ -23,30 +24,28 @@ -- -- Note that these changes are unfortunately not visible in the AST -- representation.-module Futhark.Internalise.Monomorphise-  ( transformProg ) where+module Futhark.Internalise.Monomorphise (transformProg) where -import           Control.Monad.Identity-import           Control.Monad.RWS hiding (Sum)-import           Control.Monad.State-import           Control.Monad.Writer hiding (Sum)-import           Data.Bitraversable-import           Data.Bifunctor-import           Data.List (partition)+import Control.Monad.Identity+import Control.Monad.RWS hiding (Sum)+import Control.Monad.State+import Control.Monad.Writer hiding (Sum)+import Data.Bifunctor+import Data.Bitraversable+import Data.Foldable+import Data.List (partition) import qualified Data.Map.Strict as M-import           Data.Maybe-import qualified Data.Set as S+import Data.Maybe import qualified Data.Sequence as Seq-import           Data.Foldable--import           Futhark.MonadFreshNames-import           Language.Futhark-import           Language.Futhark.Traversals-import           Language.Futhark.Semantic (TypeBinding(..))-import           Language.Futhark.TypeChecker.Types+import qualified Data.Set as S+import Futhark.MonadFreshNames+import Language.Futhark+import Language.Futhark.Semantic (TypeBinding (..))+import Language.Futhark.Traversals+import Language.Futhark.TypeChecker.Types -i32 :: TypeBase dim als-i32 = Scalar $ Prim $ Signed Int32+i64 :: TypeBase dim als+i64 = Scalar $ Prim $ Signed Int64  -- The monomorphization monad reads 'PolyBinding's and writes -- 'ValBind's.  The 'TypeParam's in the 'ValBind's can only be size@@ -55,10 +54,19 @@ -- Each 'Polybinding' is also connected with the 'RecordReplacements' -- that were active when the binding was defined.  This is used only -- in local functions.-data PolyBinding = PolyBinding RecordReplacements-                   (VName, [TypeParam], [Pattern],-                     Maybe (TypeExp VName), StructType, [VName], Exp,-                     [AttrInfo], SrcLoc)+data PolyBinding+  = PolyBinding+      RecordReplacements+      ( VName,+        [TypeParam],+        [Pattern],+        Maybe (TypeExp VName),+        StructType,+        [VName],+        Exp,+        [AttrInfo],+        SrcLoc+      )  -- Mapping from record names to the variable names that contain the -- fields.  This is used because the monomorphiser also expands all@@ -69,41 +77,55 @@  -- Monomorphization environment mapping names of polymorphic functions -- to a representation of their corresponding function bindings.-data Env = Env { envPolyBindings :: M.Map VName PolyBinding-               , envTypeBindings :: M.Map VName TypeBinding-               , envRecordReplacements :: RecordReplacements-               }+data Env = Env+  { envPolyBindings :: M.Map VName PolyBinding,+    envTypeBindings :: M.Map VName TypeBinding,+    envRecordReplacements :: RecordReplacements+  }  instance Semigroup Env where   Env tb1 pb1 rr1 <> Env tb2 pb2 rr2 = Env (tb1 <> tb2) (pb1 <> pb2) (rr1 <> rr2)  instance Monoid Env where-  mempty  = Env mempty mempty mempty+  mempty = Env mempty mempty mempty  localEnv :: Env -> MonoM a -> MonoM a localEnv env = local (env <>)  extendEnv :: VName -> PolyBinding -> MonoM a -> MonoM a-extendEnv vn binding = localEnv-  mempty { envPolyBindings = M.singleton vn binding }+extendEnv vn binding =+  localEnv+    mempty {envPolyBindings = M.singleton vn binding}  withRecordReplacements :: RecordReplacements -> MonoM a -> MonoM a-withRecordReplacements rr = localEnv mempty { envRecordReplacements = rr }+withRecordReplacements rr = localEnv mempty {envRecordReplacements = rr}  replaceRecordReplacements :: RecordReplacements -> MonoM a -> MonoM a-replaceRecordReplacements rr = local $ \env -> env { envRecordReplacements = rr }+replaceRecordReplacements rr = local $ \env -> env {envRecordReplacements = rr}  -- The monomorphization monad.-newtype MonoM a = MonoM (RWST Env (Seq.Seq (VName, ValBind)) VNameSource-                         (State Lifts) a)-  deriving (Functor, Applicative, Monad,-            MonadReader Env,-            MonadWriter (Seq.Seq (VName, ValBind)),-            MonadFreshNames)+newtype MonoM a+  = MonoM+      ( RWST+          Env+          (Seq.Seq (VName, ValBind))+          VNameSource+          (State Lifts)+          a+      )+  deriving+    ( Functor,+      Applicative,+      Monad,+      MonadReader Env,+      MonadWriter (Seq.Seq (VName, ValBind)),+      MonadFreshNames+    )  runMonoM :: VNameSource -> MonoM a -> ((a, Seq.Seq (VName, ValBind)), VNameSource) runMonoM src (MonoM m) = ((a, defs), src')-  where (a, src', defs) = evalState (runRWST m mempty src) mempty+  where+    (a, src', defs) = evalState (runRWST m mempty src) mempty  lookupFun :: VName -> MonoM (Maybe PolyBinding) lookupFun vn = do@@ -125,11 +147,12 @@  monoType :: TypeBase (DimDecl VName) als -> MonoType monoType = runIdentity . traverseDims onDim . toStruct-  where onDim bound _ (NamedDim d)-          -- A locally bound size.-          | qualLeaf d `S.member` bound = pure False-        onDim _ _ AnyDim = pure False-        onDim _ _ _      = pure True+  where+    onDim bound _ (NamedDim d)+      -- A locally bound size.+      | qualLeaf d `S.member` bound = pure False+    onDim _ _ AnyDim = pure False+    onDim _ _ _ = pure True  -- Mapping from function name and instance list to a new function name in case -- the function has already been instantiated with those concrete types.@@ -152,95 +175,109 @@ transformFName loc fname t   | baseTag (qualLeaf fname) <= maxIntrinsicTag = return $ var fname   | otherwise = do-      maybe_fname <- lookupLifted (qualLeaf fname) (monoType t)-      maybe_funbind <- lookupFun $ qualLeaf fname-      t' <- removeTypeVariablesInType t-      case (maybe_fname, maybe_funbind) of-        -- The function has already been monomorphised.-        (Just (fname', infer), _) ->-          return $ applySizeArgs fname' t' $ infer t'-        -- An intrinsic function.-        (Nothing, Nothing) -> return $ var fname-        -- A polymorphic function.-        (Nothing, Just funbind) -> do-          (fname', infer, funbind') <- monomorphiseBinding False funbind (monoType t')-          tell $ Seq.singleton (qualLeaf fname, funbind')-          addLifted (qualLeaf fname) (monoType t) (fname', infer)-          return $ applySizeArgs fname' t' $ infer t'--  where var fname' = Var fname' (Info (fromStruct t)) loc+    maybe_fname <- lookupLifted (qualLeaf fname) (monoType t)+    maybe_funbind <- lookupFun $ qualLeaf fname+    t' <- removeTypeVariablesInType t+    case (maybe_fname, maybe_funbind) of+      -- The function has already been monomorphised.+      (Just (fname', infer), _) ->+        return $ applySizeArgs fname' t' $ infer t'+      -- An intrinsic function.+      (Nothing, Nothing) -> return $ var fname+      -- A polymorphic function.+      (Nothing, Just funbind) -> do+        (fname', infer, funbind') <- monomorphiseBinding False funbind (monoType t')+        tell $ Seq.singleton (qualLeaf fname, funbind')+        addLifted (qualLeaf fname) (monoType t) (fname', infer)+        return $ applySizeArgs fname' t' $ infer t'+  where+    var fname' = Var fname' (Info (fromStruct t)) loc -        applySizeArg (i, f) size_arg =-          (i-1,-           Apply f size_arg (Info (Observe, Nothing))-           (Info (foldFunType (replicate i i32) (fromStruct t)), Info [])-           loc)+    applySizeArg (i, f) size_arg =+      ( i -1,+        Apply+          f+          size_arg+          (Info (Observe, Nothing))+          (Info (foldFunType (replicate i i64) (fromStruct t)), Info [])+          loc+      ) -        applySizeArgs fname' t' size_args =-          snd $ foldl' applySizeArg (length size_args - 1,-                                     Var (qualName fname')-                                     (Info (foldFunType (map (const i32) size_args)-                                            (fromStruct t')))-                                     loc)+    applySizeArgs fname' t' size_args =+      snd $+        foldl'+          applySizeArg+          ( length size_args - 1,+            Var+              (qualName fname')+              ( Info+                  ( foldFunType+                      (map (const i64) size_args)+                      (fromStruct t')+                  )+              )+              loc+          )           size_args  -- This carries out record replacements in the alias information of a type. transformType :: TypeBase dim Aliasing -> MonoM (TypeBase dim Aliasing) transformType t = do   rrs <- asks envRecordReplacements-  let replace (AliasBound v) | Just d <- M.lookup v rrs =-                                 S.fromList $ map (AliasBound . fst) $ M.elems d+  let replace (AliasBound v)+        | Just d <- M.lookup v rrs =+          S.fromList $ map (AliasBound . fst) $ M.elems d       replace x = S.singleton x   -- As an attempt at an optimisation, only transform the aliases if   -- they refer to a variable we have record-replaced.-  return $ if any ((`M.member` rrs) . aliasVar) $ aliases t-           then second (mconcat . map replace . S.toList) t-           else t+  return $+    if any ((`M.member` rrs) . aliasVar) $ aliases t+      then second (mconcat . map replace . S.toList) t+      else t  sizesForPat :: MonadFreshNames m => Pattern -> m ([VName], Pattern) sizesForPat pat = do   (params', sizes) <- runStateT (astMap tv pat) []   return (sizes, params')-  where tv = identityMapper { mapOnPatternType = bitraverse onDim pure }-        onDim AnyDim = do v <- lift $ newVName "size"-                          modify (v:)-                          pure $ NamedDim $ qualName v-        onDim d = pure d+  where+    tv = identityMapper {mapOnPatternType = bitraverse onDim pure}+    onDim AnyDim = do+      v <- lift $ newVName "size"+      modify (v :)+      pure $ NamedDim $ qualName v+    onDim d = pure d  -- Monomorphization of expressions. transformExp :: Exp -> MonoM Exp-transformExp e@Literal{} = return e-transformExp e@IntLit{} = return e-transformExp e@FloatLit{} = return e-transformExp e@StringLit{} = return e-+transformExp e@Literal {} = return e+transformExp e@IntLit {} = return e+transformExp e@FloatLit {} = return e+transformExp e@StringLit {} = return e transformExp (Parens e loc) =   Parens <$> transformExp e <*> pure loc- transformExp (QualParens qn e loc) =   QualParens qn <$> transformExp e <*> pure loc- transformExp (TupLit es loc) =   TupLit <$> mapM transformExp es <*> pure loc- transformExp (RecordLit fs loc) =   RecordLit <$> mapM transformField fs <*> pure loc-  where transformField (RecordFieldExplicit name e loc') =-          RecordFieldExplicit name <$> transformExp e <*> pure loc'-        transformField (RecordFieldImplicit v t _) = do-          t' <- traverse transformType t-          transformField $ RecordFieldExplicit (baseName v)-            (Var (qualName v) t' loc) loc-+  where+    transformField (RecordFieldExplicit name e loc') =+      RecordFieldExplicit name <$> transformExp e <*> pure loc'+    transformField (RecordFieldImplicit v t _) = do+      t' <- traverse transformType t+      transformField $+        RecordFieldExplicit+          (baseName v)+          (Var (qualName v) t' loc)+          loc transformExp (ArrayLit es t loc) =   ArrayLit <$> mapM transformExp es <*> traverse transformType t <*> pure loc- transformExp (Range e1 me incl tp loc) = do   e1' <- transformExp e1   me' <- mapM transformExp me   incl' <- mapM transformExp incl   return $ Range e1' me' incl' tp loc- transformExp (Var fname (Info t) loc) = do   maybe_fs <- lookupRecordReplacement $ qualLeaf fname   case maybe_fs of@@ -253,105 +290,121 @@     Nothing -> do       t' <- transformType t       transformFName loc fname (toStruct t')- transformExp (Ascript e tp loc) =   Ascript <$> transformExp e <*> pure tp <*> pure loc- transformExp (Coerce e tp (Info t, ext) loc) = do   noticeDims t-  Coerce <$> transformExp e <*> pure tp <*>-    ((,) <$> (Info <$> transformType t) <*> pure ext) <*> pure loc-+  Coerce <$> transformExp e <*> pure tp+    <*> ((,) <$> (Info <$> transformType t) <*> pure ext)+    <*> pure loc transformExp (LetPat pat e1 e2 (Info t, retext) loc) = do   (pat', rr) <- transformPattern pat   t' <- transformType t-  LetPat pat' <$> transformExp e1 <*>-    withRecordReplacements rr (transformExp e2) <*>-    pure (Info t', retext) <*> pure loc-+  LetPat pat' <$> transformExp e1+    <*> withRecordReplacements rr (transformExp e2)+    <*> pure (Info t', retext)+    <*> pure loc transformExp (LetFun fname (tparams, params, retdecl, Info ret, body) e e_t loc)   | any isTypeParam tparams = do-      -- Retrieve the lifted monomorphic function bindings that are produced,-      -- filter those that are monomorphic versions of the current let-bound-      -- function and insert them at this point, and propagate the rest.-      rr <- asks envRecordReplacements-      let funbind = PolyBinding rr (fname, tparams, params, retdecl, ret, [], body, mempty, loc)-      pass $ do-        (e', bs) <- listen $ extendEnv fname funbind $ transformExp e-        -- Do not remember this one for next time we monomorphise this-        -- function.-        modifyLifts $ filter ((/=fname) . fst . fst)-        let (bs_local, bs_prop) = Seq.partition ((== fname) . fst) bs-        return (unfoldLetFuns (map snd $ toList bs_local) e', const bs_prop)-+    -- Retrieve the lifted monomorphic function bindings that are produced,+    -- filter those that are monomorphic versions of the current let-bound+    -- function and insert them at this point, and propagate the rest.+    rr <- asks envRecordReplacements+    let funbind = PolyBinding rr (fname, tparams, params, retdecl, ret, [], body, mempty, loc)+    pass $ do+      (e', bs) <- listen $ extendEnv fname funbind $ transformExp e+      -- Do not remember this one for next time we monomorphise this+      -- function.+      modifyLifts $ filter ((/= fname) . fst . fst)+      let (bs_local, bs_prop) = Seq.partition ((== fname) . fst) bs+      return (unfoldLetFuns (map snd $ toList bs_local) e', const bs_prop)   | otherwise = do-      body' <- transformExp body-      LetFun fname (tparams, params, retdecl, Info ret, body') <$>-        transformExp e <*> traverse transformType e_t <*> pure loc-+    body' <- transformExp body+    LetFun fname (tparams, params, retdecl, Info ret, body')+      <$> transformExp e <*> traverse transformType e_t <*> pure loc transformExp (If e1 e2 e3 (tp, retext) loc) = do   e1' <- transformExp e1   e2' <- transformExp e2   e3' <- transformExp e3   tp' <- traverse transformType tp   return $ If e1' e2' e3' (tp', retext) loc- transformExp (Apply e1 e2 d (ret, ext) loc) = do   e1' <- transformExp e1   e2' <- transformExp e2   ret' <- traverse transformType ret   return $ Apply e1' e2' d (ret', ext) loc- transformExp (Negate e loc) =   Negate <$> transformExp e <*> pure loc- transformExp (Lambda params e0 decl tp loc) = do   e0' <- transformExp e0   return $ Lambda params e0' decl tp loc- transformExp (OpSection qn t loc) =   transformExp $ Var qn t loc--transformExp (OpSectionLeft fname (Info t) e-               (Info (xtype, xargext), Info ytype) (Info rettype, Info retext) loc) = do-  fname' <- transformFName loc fname $ toStruct t-  e' <- transformExp e-  desugarBinOpSection fname' (Just e') Nothing-    t (xtype, xargext) (ytype, Nothing) (rettype, retext) loc--transformExp (OpSectionRight fname (Info t) e-              (Info xtype, Info (ytype, yargext)) (Info rettype) loc) = do-  fname' <- transformFName loc fname $ toStruct t-  e' <- transformExp e-  desugarBinOpSection fname' Nothing (Just e')-    t (xtype, Nothing) (ytype, yargext) (rettype, []) loc-+transformExp+  ( OpSectionLeft+      fname+      (Info t)+      e+      (Info (xtype, xargext), Info ytype)+      (Info rettype, Info retext)+      loc+    ) = do+    fname' <- transformFName loc fname $ toStruct t+    e' <- transformExp e+    desugarBinOpSection+      fname'+      (Just e')+      Nothing+      t+      (xtype, xargext)+      (ytype, Nothing)+      (rettype, retext)+      loc+transformExp+  ( OpSectionRight+      fname+      (Info t)+      e+      (Info xtype, Info (ytype, yargext))+      (Info rettype)+      loc+    ) = do+    fname' <- transformFName loc fname $ toStruct t+    e' <- transformExp e+    desugarBinOpSection+      fname'+      Nothing+      (Just e')+      t+      (xtype, Nothing)+      (ytype, yargext)+      (rettype, [])+      loc transformExp (ProjectSection fields (Info t) loc) =   desugarProjectSection fields t loc- transformExp (IndexSection idxs (Info t) loc) =   desugarIndexSection idxs t loc- transformExp (DoLoop sparams pat e1 form e3 ret loc) = do   e1' <- transformExp e1   form' <- case form of-    For ident e2  -> For ident <$> transformExp e2+    For ident e2 -> For ident <$> transformExp e2     ForIn pat2 e2 -> ForIn pat2 <$> transformExp e2-    While e2      -> While <$> transformExp e2+    While e2 -> While <$> transformExp e2   e3' <- transformExp e3   -- Maybe monomorphisation introduced new arrays to the loop, and   -- maybe they have AnyDim sizes.  This is not allowed.  Invent some   -- sizes for them.   (pat_sizes, pat') <- sizesForPat pat-  return $ DoLoop (sparams++pat_sizes) pat' e1' form' e3' ret loc-+  return $ DoLoop (sparams ++ pat_sizes) pat' e1' form' e3' ret loc transformExp (BinOp (fname, oploc) (Info t) (e1, d1) (e2, d2) tp ext loc) = do   fname' <- transformFName loc fname $ toStruct t   e1' <- transformExp e1   e2' <- transformExp e2   case fname' of-    Var fname'' _ _ | orderZero (typeOf e1'), orderZero (typeOf e2') ->-      return $ BinOp (fname'', oploc) (Info t) (e1', d1) (e2', d2) tp ext loc+    Var fname'' _ _+      | orderZero (typeOf e1'),+        orderZero (typeOf e2') ->+        return $ BinOp (fname'', oploc) (Info t) (e1', d1) (e2', d2) tp ext loc     _ -> do       -- We have to flip the arguments to the function, because       -- operator application is left-to-right, while function@@ -361,62 +414,79 @@       -- involves existential sizes will necessarily go through here.       (x_param_e, x_param) <- makeVarParam e1'       (y_param_e, y_param) <- makeVarParam e2'-      return $ LetPat x_param e1'-        (LetPat y_param e2'-          (applyOp fname' x_param_e y_param_e) (tp, Info mempty) mempty)-        (tp, Info mempty) mempty-  where applyOp fname' x y =-          Apply (Apply fname' x (Info (Observe, snd (unInfo d1)))-                 (Info (foldFunType [fromStruct $ fst (unInfo d2)] (unInfo tp)),-                  Info mempty) loc)-          y (Info (Observe, snd (unInfo d2))) (tp, ext) loc--        makeVarParam arg = do-          let argtype = typeOf arg-          x <- newNameFromString "binop_p"-          return (Var (qualName x) (Info argtype) mempty,-                  Id x (Info $ fromStruct argtype) mempty)-+      return $+        LetPat+          x_param+          e1'+          ( LetPat+              y_param+              e2'+              (applyOp fname' x_param_e y_param_e)+              (tp, Info mempty)+              mempty+          )+          (tp, Info mempty)+          mempty+  where+    applyOp fname' x y =+      Apply+        ( Apply+            fname'+            x+            (Info (Observe, snd (unInfo d1)))+            ( Info (foldFunType [fromStruct $ fst (unInfo d2)] (unInfo tp)),+              Info mempty+            )+            loc+        )+        y+        (Info (Observe, snd (unInfo d2)))+        (tp, ext)+        loc +    makeVarParam arg = do+      let argtype = typeOf arg+      x <- newNameFromString "binop_p"+      return+        ( Var (qualName x) (Info argtype) mempty,+          Id x (Info $ fromStruct argtype) mempty+        ) transformExp (Project n e tp loc) = do   maybe_fs <- case e of     Var qn _ _ -> lookupRecordReplacement (qualLeaf qn)-    _          -> return Nothing+    _ -> return Nothing   case maybe_fs of-    Just m | Just (v, _) <- M.lookup n m ->-               return $ Var (qualName v) tp loc+    Just m+      | Just (v, _) <- M.lookup n m ->+        return $ Var (qualName v) tp loc     _ -> do       e' <- transformExp e       return $ Project n e' tp loc- transformExp (LetWith id1 id2 idxs e1 body (Info t) loc) = do   idxs' <- mapM transformDimIndex idxs   e1' <- transformExp e1   body' <- transformExp body   t' <- transformType t   return $ LetWith id1 id2 idxs' e1' body' (Info t') loc- transformExp (Index e0 idxs info loc) =   Index <$> transformExp e0 <*> mapM transformDimIndex idxs <*> pure info <*> pure loc- transformExp (Update e1 idxs e2 loc) =   Update <$> transformExp e1 <*> mapM transformDimIndex idxs-         <*> transformExp e2 <*> pure loc-+    <*> transformExp e2+    <*> pure loc transformExp (RecordUpdate e1 fs e2 t loc) =   RecordUpdate <$> transformExp e1 <*> pure fs-               <*> transformExp e2 <*> pure t <*> pure loc-+    <*> transformExp e2+    <*> pure t+    <*> pure loc transformExp (Assert e1 e2 desc loc) =   Assert <$> transformExp e1 <*> transformExp e2 <*> pure desc <*> pure loc- transformExp (Constr name all_es t loc) =   Constr name <$> mapM transformExp all_es <*> pure t <*> pure loc- transformExp (Match e cs (t, retext) loc) =-  Match <$> transformExp e <*> mapM transformCase cs <*>-  ((,) <$> traverse transformType t <*> pure retext) <*> pure loc-+  Match <$> transformExp e <*> mapM transformCase cs+    <*> ((,) <$> traverse transformType t <*> pure retext)+    <*> pure loc transformExp (Attr info e loc) =   Attr info <$> transformExp e <*> pure loc @@ -429,54 +499,78 @@ transformDimIndex (DimFix e) = DimFix <$> transformExp e transformDimIndex (DimSlice me1 me2 me3) =   DimSlice <$> trans me1 <*> trans me2 <*> trans me3-  where trans = mapM transformExp+  where+    trans = mapM transformExp  -- Transform an operator section into a lambda.-desugarBinOpSection :: Exp -> Maybe Exp -> Maybe Exp-                    -> PatternType-                    -> (StructType, Maybe VName) -> (StructType, Maybe VName)-                    -> (PatternType, [VName]) -> SrcLoc -> MonoM Exp+desugarBinOpSection ::+  Exp ->+  Maybe Exp ->+  Maybe Exp ->+  PatternType ->+  (StructType, Maybe VName) ->+  (StructType, Maybe VName) ->+  (PatternType, [VName]) ->+  SrcLoc ->+  MonoM Exp desugarBinOpSection op e_left e_right t (xtype, xext) (ytype, yext) (rettype, retext) loc = do   (e1, p1) <- makeVarParam e_left $ fromStruct xtype   (e2, p2) <- makeVarParam e_right $ fromStruct ytype-  let apply_left = Apply op e1 (Info (Observe, xext))-                   (Info $ foldFunType [fromStruct ytype] t, Info []) loc-      body = Apply apply_left e2 (Info (Observe, yext))-             (Info rettype, Info retext) loc+  let apply_left =+        Apply+          op+          e1+          (Info (Observe, xext))+          (Info $ foldFunType [fromStruct ytype] t, Info [])+          loc+      body =+        Apply+          apply_left+          e2+          (Info (Observe, yext))+          (Info rettype, Info retext)+          loc       rettype' = toStruct rettype   return $ Lambda (p1 ++ p2) body Nothing (Info (mempty, rettype')) loc--  where makeVarParam (Just e) _ = return (e, [])-        makeVarParam Nothing argtype = do-          x <- newNameFromString "x"-          return (Var (qualName x) (Info argtype) mempty,-                  [Id x (Info $ fromStruct argtype) mempty])+  where+    makeVarParam (Just e) _ = return (e, [])+    makeVarParam Nothing argtype = do+      x <- newNameFromString "x"+      return+        ( Var (qualName x) (Info argtype) mempty,+          [Id x (Info $ fromStruct argtype) mempty]+        )  desugarProjectSection :: [Name] -> PatternType -> SrcLoc -> MonoM Exp desugarProjectSection fields (Scalar (Arrow _ _ t1 t2)) loc = do   p <- newVName "project_p"   let body = foldl project (Var (qualName p) (Info t1) mempty) fields   return $ Lambda [Id p (Info t1) mempty] body Nothing (Info (mempty, toStruct t2)) loc-  where project e field =-          case typeOf e of-            Scalar (Record fs)-              | Just t <- M.lookup field fs ->-                  Project field e (Info t) mempty-            t -> error $ "desugarOpSection: type " ++ pretty t ++-                 " does not have field " ++ pretty field-desugarProjectSection  _ t _ = error $ "desugarOpSection: not a function type: " ++ pretty t+  where+    project e field =+      case typeOf e of+        Scalar (Record fs)+          | Just t <- M.lookup field fs ->+            Project field e (Info t) mempty+        t ->+          error $+            "desugarOpSection: type " ++ pretty t+              ++ " does not have field "+              ++ pretty field+desugarProjectSection _ t _ = error $ "desugarOpSection: not a function type: " ++ pretty t  desugarIndexSection :: [DimIndex] -> PatternType -> SrcLoc -> MonoM Exp desugarIndexSection idxs (Scalar (Arrow _ _ t1 t2)) loc = do   p <- newVName "index_i"   let body = Index (Var (qualName p) (Info t1) loc) idxs (Info t2, Info []) loc   return $ Lambda [Id p (Info t1) mempty] body Nothing (Info (mempty, toStruct t2)) loc-desugarIndexSection  _ t _ = error $ "desugarIndexSection: not a function type: " ++ pretty t+desugarIndexSection _ t _ = error $ "desugarIndexSection: not a function type: " ++ pretty t  noticeDims :: TypeBase (DimDecl VName) as -> MonoM () noticeDims = mapM_ notice . nestedDims-  where notice (NamedDim v) = void $ transformFName mempty v i32-        notice _            = return ()+  where+    notice (NamedDim v) = void $ transformFName mempty v i64+    notice _ = return ()  -- Convert a collection of 'ValBind's to a nested sequence of let-bound, -- monomorphic functions with the given expression at the bottom.@@ -484,18 +578,25 @@ unfoldLetFuns [] e = e unfoldLetFuns (ValBind _ fname _ (Info (rettype, _)) dim_params params body _ _ loc : rest) e =   LetFun fname (dim_params, params, Nothing, Info rettype, body) e' (Info e_t) loc-  where e' = unfoldLetFuns rest e-        e_t = typeOf e'+  where+    e' = unfoldLetFuns rest e+    e_t = typeOf e'  transformPattern :: Pattern -> MonoM (Pattern, RecordReplacements) transformPattern (Id v (Info (Scalar (Record fs))) loc) = do   let fs' = M.toList fs-  (fs_ks, fs_ts) <- fmap unzip $ forM fs' $ \(f, ft) ->-    (,) <$> newVName (nameToString f) <*> transformType ft-  return (RecordPattern (zip (map fst fs')-                             (zipWith3 Id fs_ks (map Info fs_ts) $ repeat loc))-                        loc,-          M.singleton v $ M.fromList $ zip (map fst fs') $ zip fs_ks fs_ts)+  (fs_ks, fs_ts) <- fmap unzip $+    forM fs' $ \(f, ft) ->+      (,) <$> newVName (nameToString f) <*> transformType ft+  return+    ( RecordPattern+        ( zip+            (map fst fs')+            (zipWith3 Id fs_ks (map Info fs_ts) $ repeat loc)+        )+        loc,+      M.singleton v $ M.fromList $ zip (map fst fs') $ zip fs_ks fs_ts+    ) transformPattern (Id v t loc) = return (Id v t loc, mempty) transformPattern (TuplePattern pats loc) = do   (pats', rrs) <- unzip <$> mapM transformPattern pats@@ -526,36 +627,40 @@  type DimInst = M.Map VName (DimDecl VName) -dimMapping :: Monoid a =>-              TypeBase (DimDecl VName) a-           -> TypeBase (DimDecl VName) a-           -> DimInst+dimMapping ::+  Monoid a =>+  TypeBase (DimDecl VName) a ->+  TypeBase (DimDecl VName) a ->+  DimInst dimMapping t1 t2 = execState (matchDims f t1 t2) mempty-  where f (NamedDim d1) d2 = do-          modify $ M.insert (qualLeaf d1) d2-          return $ NamedDim d1-        f d _ = return d+  where+    f (NamedDim d1) d2 = do+      modify $ M.insert (qualLeaf d1) d2+      return $ NamedDim d1+    f d _ = return d  inferSizeArgs :: [TypeParam] -> StructType -> StructType -> [Exp] inferSizeArgs tparams bind_t t =   mapMaybe (tparamArg (dimMapping bind_t t)) tparams-  where tparamArg dinst tp =-          case M.lookup (typeParamName tp) dinst of-            Just (NamedDim d) ->-              Just $ Var d (Info i32) mempty-            Just (ConstDim x) ->-              Just $ Literal (SignedValue $ Int32Value $ fromIntegral x) mempty-            _ ->-              Nothing+  where+    tparamArg dinst tp =+      case M.lookup (typeParamName tp) dinst of+        Just (NamedDim d) ->+          Just $ Var d (Info i64) mempty+        Just (ConstDim x) ->+          Just $ Literal (SignedValue $ Int64Value $ fromIntegral x) mempty+        _ ->+          Nothing  explicitSizes :: StructType -> MonoType -> S.Set VName explicitSizes t1 t2 =   execState (matchDims onDims t1 t2) mempty `S.intersection` mustBeExplicit t1-  where onDims d1 d2 = do-          case (d1, d2) of-            (NamedDim v, True) -> modify $ S.insert $ qualLeaf v-            _                  -> return ()-          return d1+  where+    onDims d1 d2 = do+      case (d1, d2) of+        (NamedDim v, True) -> modify $ S.insert $ qualLeaf v+        _ -> return ()+      return d1  -- Monomorphising higher-order functions can result in function types -- where the same named parameter occurs in multiple spots.  When@@ -566,128 +671,153 @@ -- anyway. noNamedParams :: MonoType -> MonoType noNamedParams = f-  where f (Array () u t shape) = Array () u (f' t) shape-        f (Scalar t) = Scalar $ f' t-        f' (Arrow () _ t1 t2) =-          Arrow () Unnamed (f t1) (f t2)-        f' (Record fs) =-          Record $ fmap f fs-        f' (Sum cs) =-          Sum $ fmap (map f) cs-        f' t = t+  where+    f (Array () u t shape) = Array () u (f' t) shape+    f (Scalar t) = Scalar $ f' t+    f' (Arrow () _ t1 t2) =+      Arrow () Unnamed (f t1) (f t2)+    f' (Record fs) =+      Record $ fmap f fs+    f' (Sum cs) =+      Sum $ fmap (map f) cs+    f' t = t  -- Monomorphise a polymorphic function at the types given in the instance -- list. Monomorphises the body of the function as well. Returns the fresh name -- of the generated monomorphic function and its 'ValBind' representation.-monomorphiseBinding :: Bool -> PolyBinding -> MonoType-                    -> MonoM (VName, InferSizeArgs, ValBind)+monomorphiseBinding ::+  Bool ->+  PolyBinding ->+  MonoType ->+  MonoM (VName, InferSizeArgs, ValBind) monomorphiseBinding entry (PolyBinding rr (name, tparams, params, retdecl, rettype, retext, body, attrs, loc)) t =   replaceRecordReplacements rr $ do-  let bind_t = foldFunType (map patternStructType params) rettype-  (substs, t_shape_params) <- typeSubstsM loc (noSizes bind_t) $ noNamedParams t-  let substs' = M.map Subst substs-      rettype' = substTypesAny (`M.lookup` substs') rettype-      substPatternType =-        substTypesAny (fmap (fmap fromStruct) . (`M.lookup` substs'))-      params' = map (substPattern entry substPatternType) params-      bind_t' = substTypesAny (`M.lookup` substs') bind_t-      (shape_params_explicit, shape_params_implicit) =-        partition ((`S.member` explicitSizes bind_t' t) . typeParamName) $-        shape_params ++ t_shape_params--  (params'', rrs) <- unzip <$> mapM transformPattern params'+    let bind_t = foldFunType (map patternStructType params) rettype+    (substs, t_shape_params) <- typeSubstsM loc (noSizes bind_t) $ noNamedParams t+    let substs' = M.map Subst substs+        rettype' = substTypesAny (`M.lookup` substs') rettype+        substPatternType =+          substTypesAny (fmap (fmap fromStruct) . (`M.lookup` substs'))+        params' = map (substPattern entry substPatternType) params+        bind_t' = substTypesAny (`M.lookup` substs') bind_t+        (shape_params_explicit, shape_params_implicit) =+          partition ((`S.member` explicitSizes bind_t' t) . typeParamName) $+            shape_params ++ t_shape_params -  mapM_ noticeDims $ rettype : map patternStructType params''+    (params'', rrs) <- unzip <$> mapM transformPattern params' -  body' <- updateExpTypes (`M.lookup` substs') body-  body'' <- withRecordReplacements (mconcat rrs) $ transformExp body'-  name' <- if null tparams && not entry then return name else newName name+    mapM_ noticeDims $ rettype : map patternStructType params'' -  return (name',-          inferSizeArgs shape_params_explicit bind_t',-          if entry-          then toValBinding name'-               (shape_params_explicit++shape_params_implicit) params''-               (rettype', retext) body''-          else toValBinding name' shape_params_implicit-               (map shapeParam shape_params_explicit ++ params'')-               (rettype', retext) body'')+    body' <- updateExpTypes (`M.lookup` substs') body+    body'' <- withRecordReplacements (mconcat rrs) $ transformExp body'+    name' <- if null tparams && not entry then return name else newName name -  where shape_params = filter (not . isTypeParam) tparams+    return+      ( name',+        inferSizeArgs shape_params_explicit bind_t',+        if entry+          then+            toValBinding+              name'+              (shape_params_explicit ++ shape_params_implicit)+              params''+              (rettype', retext)+              body''+          else+            toValBinding+              name'+              shape_params_implicit+              (map shapeParam shape_params_explicit ++ params'')+              (rettype', retext)+              body''+      )+  where+    shape_params = filter (not . isTypeParam) tparams -        updateExpTypes substs = astMap $ mapper substs-        mapper substs = ASTMapper { mapOnExp         = astMap $ mapper substs-                                  , mapOnName        = pure-                                  , mapOnQualName    = pure-                                  , mapOnStructType  = pure . applySubst substs-                                  , mapOnPatternType = pure . applySubst substs-                                  }+    updateExpTypes substs = astMap $ mapper substs+    mapper substs =+      ASTMapper+        { mapOnExp = astMap $ mapper substs,+          mapOnName = pure,+          mapOnQualName = pure,+          mapOnStructType = pure . applySubst substs,+          mapOnPatternType = pure . applySubst substs+        } -        shapeParam tp = Id (typeParamName tp) (Info i32) $ srclocOf tp+    shapeParam tp = Id (typeParamName tp) (Info i64) $ srclocOf tp -        toValBinding name' tparams' params'' rettype' body'' =-          ValBind { valBindEntryPoint = Nothing-                  , valBindName       = name'-                  , valBindRetDecl    = retdecl-                  , valBindRetType    = Info rettype'-                  , valBindTypeParams = tparams'-                  , valBindParams     = params''-                  , valBindBody       = body''-                  , valBindDoc        = Nothing-                  , valBindAttrs      = attrs-                  , valBindLocation   = loc-                  }+    toValBinding name' tparams' params'' rettype' body'' =+      ValBind+        { valBindEntryPoint = Nothing,+          valBindName = name',+          valBindRetDecl = retdecl,+          valBindRetType = Info rettype',+          valBindTypeParams = tparams',+          valBindParams = params'',+          valBindBody = body'',+          valBindDoc = Nothing,+          valBindAttrs = attrs,+          valBindLocation = loc+        } -typeSubstsM :: MonadFreshNames m =>-               SrcLoc -> TypeBase () () -> MonoType-            -> m (M.Map VName StructType, [TypeParam])+typeSubstsM ::+  MonadFreshNames m =>+  SrcLoc ->+  TypeBase () () ->+  MonoType ->+  m (M.Map VName StructType, [TypeParam]) typeSubstsM loc orig_t1 orig_t2 =   let m = sub orig_t1 orig_t2-  in runWriterT $ execStateT m mempty--  where sub t1@Array{} t2@Array{}-          | Just t1' <- peelArray (arrayRank t1) t1,-            Just t2' <- peelArray (arrayRank t1) t2 =-              sub t1' t2'-        sub (Scalar (TypeVar _ _ v _)) t = addSubst v t-        sub (Scalar (Record fields1)) (Scalar (Record fields2)) =-          zipWithM_ sub-          (map snd $ sortFields fields1) (map snd $ sortFields fields2)-        sub (Scalar Prim{}) (Scalar Prim{}) = return ()-        sub (Scalar (Arrow _ _ t1a t1b)) (Scalar (Arrow _ _ t2a t2b)) = do-          sub t1a t2a-          sub t1b t2b-        sub (Scalar (Sum cs1)) (Scalar (Sum cs2)) =-          zipWithM_ typeSubstClause (sortConstrs cs1) (sortConstrs cs2)-          where typeSubstClause (_, ts1) (_, ts2) = zipWithM sub ts1 ts2-        sub t1@(Scalar Sum{}) t2 = sub t1 t2-        sub t1 t2@(Scalar Sum{}) = sub t1 t2--        sub t1 t2 = error $ unlines ["typeSubstsM: mismatched types:", pretty t1, pretty t2]+   in runWriterT $ execStateT m mempty+  where+    sub t1@Array {} t2@Array {}+      | Just t1' <- peelArray (arrayRank t1) t1,+        Just t2' <- peelArray (arrayRank t1) t2 =+        sub t1' t2'+    sub (Scalar (TypeVar _ _ v _)) t = addSubst v t+    sub (Scalar (Record fields1)) (Scalar (Record fields2)) =+      zipWithM_+        sub+        (map snd $ sortFields fields1)+        (map snd $ sortFields fields2)+    sub (Scalar Prim {}) (Scalar Prim {}) = return ()+    sub (Scalar (Arrow _ _ t1a t1b)) (Scalar (Arrow _ _ t2a t2b)) = do+      sub t1a t2a+      sub t1b t2b+    sub (Scalar (Sum cs1)) (Scalar (Sum cs2)) =+      zipWithM_ typeSubstClause (sortConstrs cs1) (sortConstrs cs2)+      where+        typeSubstClause (_, ts1) (_, ts2) = zipWithM sub ts1 ts2+    sub t1@(Scalar Sum {}) t2 = sub t1 t2+    sub t1 t2@(Scalar Sum {}) = sub t1 t2+    sub t1 t2 = error $ unlines ["typeSubstsM: mismatched types:", pretty t1, pretty t2] -        addSubst (TypeName _ v) t = do-          exists <- gets $ M.member v-          unless exists $ do-            t' <- bitraverse onDim pure t-            modify $ M.insert v t'+    addSubst (TypeName _ v) t = do+      exists <- gets $ M.member v+      unless exists $ do+        t' <- bitraverse onDim pure t+        modify $ M.insert v t' -        onDim True = do d <- lift $ lift $ newVName "d"-                        tell [TypeParamDim d loc]-                        return $ NamedDim $ qualName d-        onDim False = return AnyDim+    onDim True = do+      d <- lift $ lift $ newVName "d"+      tell [TypeParamDim d loc]+      return $ NamedDim $ qualName d+    onDim False = return AnyDim  -- Perform a given substitution on the types in a pattern. substPattern :: Bool -> (PatternType -> PatternType) -> Pattern -> Pattern substPattern entry f pat = case pat of-  TuplePattern pats loc       -> TuplePattern (map (substPattern entry f) pats) loc-  RecordPattern fs loc        -> RecordPattern (map substField fs) loc-    where substField (n, p) = (n, substPattern entry f p)-  PatternParens p loc         -> PatternParens (substPattern entry f p) loc-  Id vn (Info tp) loc         -> Id vn (Info $ f tp) loc-  Wildcard (Info tp) loc      -> Wildcard (Info $ f tp) loc-  PatternAscription p td loc | entry     -> PatternAscription (substPattern False f p) td loc-                             | otherwise -> substPattern False f p-  PatternLit e (Info tp) loc  -> PatternLit e (Info $ f tp) loc+  TuplePattern pats loc -> TuplePattern (map (substPattern entry f) pats) loc+  RecordPattern fs loc -> RecordPattern (map substField fs) loc+    where+      substField (n, p) = (n, substPattern entry f p)+  PatternParens p loc -> PatternParens (substPattern entry f p) loc+  Id vn (Info tp) loc -> Id vn (Info $ f tp) loc+  Wildcard (Info tp) loc -> Wildcard (Info $ f tp) loc+  PatternAscription p td loc+    | entry -> PatternAscription (substPattern False f p) td loc+    | otherwise -> substPattern False f p+  PatternLit e (Info tp) loc -> PatternLit e (Info $ f tp) loc   PatternConstr n (Info tp) ps loc -> PatternConstr n (Info $ f tp) ps loc  toPolyBinding :: ValBind -> PolyBinding@@ -698,20 +828,23 @@ removeTypeVariables :: Bool -> ValBind -> MonoM ValBind removeTypeVariables entry valbind@(ValBind _ _ _ (Info (rettype, retext)) _ pats body _ _ _) = do   subs <- asks $ M.map TypeSub . envTypeBindings-  let mapper = ASTMapper {-          mapOnExp         = astMap mapper-        , mapOnName        = pure-        , mapOnQualName    = pure-        , mapOnStructType  = pure . substituteTypes subs-        , mapOnPatternType = pure . substituteTypes subs-        }+  let mapper =+        ASTMapper+          { mapOnExp = astMap mapper,+            mapOnName = pure,+            mapOnQualName = pure,+            mapOnStructType = pure . substituteTypes subs,+            mapOnPatternType = pure . substituteTypes subs+          }    body' <- astMap mapper body -  return valbind { valBindRetType = Info (substituteTypes subs rettype, retext)-                 , valBindParams  = map (substPattern entry $ substituteTypes subs) pats-                 , valBindBody    = body'-                 }+  return+    valbind+      { valBindRetType = Info (substituteTypes subs rettype, retext),+        valBindParams = map (substPattern entry $ substituteTypes subs) pats,+        valBindBody = body'+      }  removeTypeVariablesInType :: StructType -> MonoM StructType removeTypeVariablesInType t = do@@ -720,17 +853,20 @@  transformValBind :: ValBind -> MonoM Env transformValBind valbind = do-  valbind' <- toPolyBinding <$>-              removeTypeVariables (isJust (valBindEntryPoint valbind)) valbind+  valbind' <-+    toPolyBinding+      <$> removeTypeVariables (isJust (valBindEntryPoint valbind)) valbind    when (isJust $ valBindEntryPoint valbind) $ do-    t <- removeTypeVariablesInType $ foldFunType-         (map patternStructType (valBindParams valbind)) $-         fst $ unInfo $ valBindRetType valbind+    t <-+      removeTypeVariablesInType $+        foldFunType+          (map patternStructType (valBindParams valbind))+          $ fst $ unInfo $ valBindRetType valbind     (name, _, valbind'') <- monomorphiseBinding True valbind' $ monoType t-    tell $ Seq.singleton (name, valbind'' { valBindEntryPoint = valBindEntryPoint valbind})+    tell $ Seq.singleton (name, valbind'' {valBindEntryPoint = valBindEntryPoint valbind}) -  return mempty { envPolyBindings = M.singleton (valBindName valbind) valbind' }+  return mempty {envPolyBindings = M.singleton (valBindName valbind) valbind'}  transformTypeBind :: TypeBind -> MonoM Env transformTypeBind (TypeBind name l tparams tydecl _ _) = do@@ -738,25 +874,26 @@   noticeDims $ unInfo $ expandedType tydecl   let tp = substituteTypes subs . unInfo $ expandedType tydecl       tbinding = TypeAbbr l tparams tp-  return mempty { envTypeBindings = M.singleton name tbinding }+  return mempty {envTypeBindings = M.singleton name tbinding}  transformDecs :: [Dec] -> MonoM () transformDecs [] = return () transformDecs (ValDec valbind : ds) = do   env <- transformValBind valbind   localEnv env $ transformDecs ds- transformDecs (TypeDec typebind : ds) = do   env <- transformTypeBind typebind   localEnv env $ transformDecs ds- transformDecs (dec : _) =-  error $ "The monomorphization module expects a module-free " ++-  "input program, but received: " ++ pretty dec+  error $+    "The monomorphization module expects a module-free "+      ++ "input program, but received: "+      ++ pretty dec  -- | Monomorphise a list of top-level declarations. A module-free input program -- is expected, so only value declarations and type declaration are accepted. transformProg :: MonadFreshNames m => [Dec] -> m [ValBind] transformProg decs =-  fmap (toList . fmap snd . snd) $ modifyNameSource $ \namesrc ->-  runMonoM namesrc $ transformDecs decs+  fmap (toList . fmap snd . snd) $+    modifyNameSource $ \namesrc ->+      runMonoM namesrc $ transformDecs decs
src/Futhark/Internalise/TypesValues.hs view
@@ -1,32 +1,32 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE Trustworthy #-}+ module Futhark.Internalise.TypesValues-  (-   -- * Internalising types-    internaliseReturnType-  , internaliseLambdaReturnType-  , internaliseEntryReturnType-  , internaliseType-  , internaliseParamTypes-  , internaliseLoopParamType-  , internalisePrimType-  , internalisedTypeSize-  , internaliseSumType+  ( -- * Internalising types+    internaliseReturnType,+    internaliseLambdaReturnType,+    internaliseEntryReturnType,+    internaliseType,+    internaliseParamTypes,+    internaliseLoopParamType,+    internalisePrimType,+    internalisedTypeSize,+    internaliseSumType, -  -- * Internalising values-  , internalisePrimValue+    -- * Internalising values+    internalisePrimValue,   )-  where+where+ import Control.Monad.Reader import Control.Monad.State import Data.List (delete, find, foldl')-import Data.Maybe import qualified Data.Map.Strict as M--import qualified Language.Futhark as E+import Data.Maybe import Futhark.IR.SOACS as I import Futhark.Internalise.Monad+import qualified Language.Futhark as E  internaliseUniqueness :: E.Uniqueness -> I.Uniqueness internaliseUniqueness E.Nonunique = I.Nonunique@@ -34,79 +34,92 @@  type TypeState = Int -newtype InternaliseTypeM a =-  InternaliseTypeM (StateT TypeState InternaliseM a)+newtype InternaliseTypeM a+  = InternaliseTypeM (StateT TypeState InternaliseM a)   deriving (Functor, Applicative, Monad, MonadState TypeState)  liftInternaliseM :: InternaliseM a -> InternaliseTypeM a liftInternaliseM = InternaliseTypeM . lift -runInternaliseTypeM :: InternaliseTypeM a-                    -> InternaliseM a+runInternaliseTypeM ::+  InternaliseTypeM a ->+  InternaliseM a runInternaliseTypeM (InternaliseTypeM m) =   evalStateT m 0 -internaliseParamTypes :: [E.TypeBase (E.DimDecl VName) ()]-                      -> InternaliseM [[I.TypeBase Shape Uniqueness]]+internaliseParamTypes ::+  [E.TypeBase (E.DimDecl VName) ()] ->+  InternaliseM [[I.TypeBase Shape Uniqueness]] internaliseParamTypes ts =   runInternaliseTypeM $ mapM (fmap (map onType) . internaliseTypeM) ts-  where onType = fromMaybe bad . hasStaticShape-        bad = error $ "internaliseParamTypes: " ++ pretty ts+  where+    onType = fromMaybe bad . hasStaticShape+    bad = error $ "internaliseParamTypes: " ++ pretty ts -internaliseLoopParamType :: E.TypeBase (E.DimDecl VName) ()-                         -> InternaliseM [I.TypeBase Shape Uniqueness]+internaliseLoopParamType ::+  E.TypeBase (E.DimDecl VName) () ->+  InternaliseM [I.TypeBase Shape Uniqueness] internaliseLoopParamType et =   concat <$> internaliseParamTypes [et] -internaliseReturnType :: E.TypeBase (E.DimDecl VName) ()-                      -> InternaliseM [I.TypeBase ExtShape Uniqueness]+internaliseReturnType ::+  E.TypeBase (E.DimDecl VName) () ->+  InternaliseM [I.TypeBase ExtShape Uniqueness] internaliseReturnType et =   runInternaliseTypeM (internaliseTypeM et) -internaliseLambdaReturnType :: E.TypeBase (E.DimDecl VName) ()-                            -> InternaliseM [I.TypeBase Shape NoUniqueness]+internaliseLambdaReturnType ::+  E.TypeBase (E.DimDecl VName) () ->+  InternaliseM [I.TypeBase Shape NoUniqueness] internaliseLambdaReturnType = fmap (map fromDecl) . internaliseLoopParamType  -- | As 'internaliseReturnType', but returns components of a top-level -- tuple type piecemeal.-internaliseEntryReturnType :: E.TypeBase (E.DimDecl VName) ()-                           -> InternaliseM [[I.TypeBase ExtShape Uniqueness]]+internaliseEntryReturnType ::+  E.TypeBase (E.DimDecl VName) () ->+  InternaliseM [[I.TypeBase ExtShape Uniqueness]] internaliseEntryReturnType et =-  runInternaliseTypeM $ mapM internaliseTypeM $-  case E.isTupleRecord et of-    Just ets | not $ null ets -> ets-    _ -> [et]+  runInternaliseTypeM $+    mapM internaliseTypeM $+      case E.isTupleRecord et of+        Just ets | not $ null ets -> ets+        _ -> [et] -internaliseType :: E.TypeBase (E.DimDecl VName) ()-                -> InternaliseM [I.TypeBase I.ExtShape Uniqueness]+internaliseType ::+  E.TypeBase (E.DimDecl VName) () ->+  InternaliseM [I.TypeBase I.ExtShape Uniqueness] internaliseType = runInternaliseTypeM . internaliseTypeM  newId :: InternaliseTypeM Int-newId = do i <- get-           put $ i + 1-           return i+newId = do+  i <- get+  put $ i + 1+  return i -internaliseDim :: E.DimDecl VName-               -> InternaliseTypeM ExtSize+internaliseDim ::+  E.DimDecl VName ->+  InternaliseTypeM ExtSize internaliseDim d =   case d of     E.AnyDim -> Ext <$> newId-    E.ConstDim n -> return $ Free $ intConst I.Int32 $ toInteger n+    E.ConstDim n -> return $ Free $ intConst I.Int64 $ toInteger n     E.NamedDim name -> namedDim name-  where namedDim (E.QualName _ name) = do-          subst <- liftInternaliseM $ lookupSubst name-          case subst of-            Just [v] -> return $ I.Free v-            _ -> return $ I.Free $ I.Var name+  where+    namedDim (E.QualName _ name) = do+      subst <- liftInternaliseM $ lookupSubst name+      case subst of+        Just [v] -> return $ I.Free v+        _ -> return $ I.Free $ I.Var name -internaliseTypeM :: E.StructType-                 -> InternaliseTypeM [I.TypeBase ExtShape Uniqueness]+internaliseTypeM ::+  E.StructType ->+  InternaliseTypeM [I.TypeBase ExtShape Uniqueness] internaliseTypeM orig_t =   case orig_t of     E.Array _ u et shape -> do       dims <- internaliseShape shape       ets <- internaliseTypeM $ E.Scalar et-      return [I.arrayOf et' (Shape dims) $ internaliseUniqueness u | et' <- ets ]+      return [I.arrayOf et' (Shape dims) $ internaliseUniqueness u | et' <- ets]     E.Scalar (E.Prim bt) ->       return [I.Prim $ internalisePrimType bt]     E.Scalar (E.Record ets)@@ -114,43 +127,54 @@       -- arrays of unit will lose their sizes.       | null ets -> return [I.Prim I.Bool]       | otherwise ->-          concat <$> mapM (internaliseTypeM . snd) (E.sortFields ets)-    E.Scalar E.TypeVar{} ->+        concat <$> mapM (internaliseTypeM . snd) (E.sortFields ets)+    E.Scalar E.TypeVar {} ->       error "internaliseTypeM: cannot handle type variable."-    E.Scalar E.Arrow{} ->+    E.Scalar E.Arrow {} ->       error $ "internaliseTypeM: cannot handle function type: " ++ pretty orig_t     E.Scalar (E.Sum cs) -> do-      (ts, _) <- internaliseConstructors <$>-                 traverse (fmap concat . mapM internaliseTypeM) cs+      (ts, _) <-+        internaliseConstructors+          <$> traverse (fmap concat . mapM internaliseTypeM) cs       return $ I.Prim (I.IntType I.Int8) : ts--  where internaliseShape = mapM internaliseDim . E.shapeDims+  where+    internaliseShape = mapM internaliseDim . E.shapeDims -internaliseConstructors :: M.Map Name [I.TypeBase ExtShape Uniqueness]-                        -> ([I.TypeBase ExtShape Uniqueness],-                            M.Map Name (Int, [Int]))+internaliseConstructors ::+  M.Map Name [I.TypeBase ExtShape Uniqueness] ->+  ( [I.TypeBase ExtShape Uniqueness],+    M.Map Name (Int, [Int])+  ) internaliseConstructors cs =-  foldl' onConstructor mempty $ zip (E.sortConstrs cs) [0..]-  where onConstructor (ts, mapping) ((c, c_ts), i) =-          let (_, js, new_ts) =-                foldl' f (zip ts [0..], mempty, mempty) c_ts-          in (ts ++ new_ts, M.insert c (i, js) mapping)-          where f (ts', js, new_ts) t-                  | Just (_, j) <- find ((==t) . fst) ts' =-                      (delete (t, j) ts',-                       js ++ [j],-                       new_ts)-                  | otherwise =-                      (ts',-                       js ++ [length ts + length new_ts],-                       new_ts ++ [t])+  foldl' onConstructor mempty $ zip (E.sortConstrs cs) [0 ..]+  where+    onConstructor (ts, mapping) ((c, c_ts), i) =+      let (_, js, new_ts) =+            foldl' f (zip ts [0 ..], mempty, mempty) c_ts+       in (ts ++ new_ts, M.insert c (i, js) mapping)+      where+        f (ts', js, new_ts) t+          | Just (_, j) <- find ((== t) . fst) ts' =+            ( delete (t, j) ts',+              js ++ [j],+              new_ts+            )+          | otherwise =+            ( ts',+              js ++ [length ts + length new_ts],+              new_ts ++ [t]+            ) -internaliseSumType :: M.Map Name [E.StructType]-                   -> InternaliseM ([I.TypeBase ExtShape Uniqueness],-                                    M.Map Name (Int, [Int]))+internaliseSumType ::+  M.Map Name [E.StructType] ->+  InternaliseM+    ( [I.TypeBase ExtShape Uniqueness],+      M.Map Name (Int, [Int])+    ) internaliseSumType cs =-  runInternaliseTypeM $ internaliseConstructors <$>-  traverse (fmap concat . mapM internaliseTypeM) cs+  runInternaliseTypeM $+    internaliseConstructors+      <$> traverse (fmap concat . mapM internaliseTypeM) cs  -- | How many core language values are needed to represent one source -- language value of the given type?
src/Futhark/MonadFreshNames.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+ -- | This module provides a monadic facility similar (and built on top -- of) "Futhark.FreshNames".  The removes the need for a (small) amount of -- boilerplate, at the cost of using some GHC extensions.  The idea is@@ -6,30 +8,30 @@ -- 'MonadFreshNames', you can automatically use the name generation -- functions exported by this module. module Futhark.MonadFreshNames-  ( MonadFreshNames (..)-  , modifyNameSource-  , newName-  , newNameFromString-  , newVName-  , newIdent-  , newIdent'-  , newParam-  , module Futhark.FreshNames-  ) where+  ( MonadFreshNames (..),+    modifyNameSource,+    newName,+    newNameFromString,+    newVName,+    newIdent,+    newIdent',+    newParam,+    module Futhark.FreshNames,+  )+where  import Control.Monad.Except+import qualified Control.Monad.RWS.Lazy+import qualified Control.Monad.RWS.Strict+import Control.Monad.Reader import qualified Control.Monad.State.Lazy import qualified Control.Monad.State.Strict+import qualified Control.Monad.Trans.Maybe import qualified Control.Monad.Writer.Lazy import qualified Control.Monad.Writer.Strict-import qualified Control.Monad.RWS.Lazy-import qualified Control.Monad.RWS.Strict-import qualified Control.Monad.Trans.Maybe-import Control.Monad.Reader--import Futhark.IR.Syntax-import qualified Futhark.FreshNames as FreshNames import Futhark.FreshNames hiding (newName)+import qualified Futhark.FreshNames as FreshNames+import Futhark.IR.Syntax  -- | A monad that stores a name source.  The following is a good -- instance for a monad in which the only state is a @NameSource vn@:@@ -51,13 +53,17 @@   getNameSource = Control.Monad.State.Strict.get   putNameSource = Control.Monad.State.Strict.put -instance (Applicative im, Monad im, Monoid w) =>-         MonadFreshNames (Control.Monad.RWS.Lazy.RWST r w VNameSource im) where+instance+  (Applicative im, Monad im, Monoid w) =>+  MonadFreshNames (Control.Monad.RWS.Lazy.RWST r w VNameSource im)+  where   getNameSource = Control.Monad.RWS.Lazy.get   putNameSource = Control.Monad.RWS.Lazy.put -instance (Applicative im, Monad im, Monoid w) =>-         MonadFreshNames (Control.Monad.RWS.Strict.RWST r w VNameSource im) where+instance+  (Applicative im, Monad im, Monoid w) =>+  MonadFreshNames (Control.Monad.RWS.Strict.RWST r w VNameSource im)+  where   getNameSource = Control.Monad.RWS.Strict.get   putNameSource = Control.Monad.RWS.Strict.put @@ -65,10 +71,11 @@ -- one, using 'getNameSource' and 'putNameSource' before and after the -- computation. modifyNameSource :: MonadFreshNames m => (VNameSource -> (a, VNameSource)) -> m a-modifyNameSource m = do src <- getNameSource-                        let (x,src') = m src-                        putNameSource src'-                        return x+modifyNameSource m = do+  src <- getNameSource+  let (x, src') = m src+  putNameSource src'+  return x  -- | Produce a fresh name, using the given name as a template. newName :: MonadFreshNames m => VName -> m VName@@ -87,24 +94,33 @@ newVName = newID . nameFromString  -- | Produce a fresh 'Ident', using the given name as a template.-newIdent :: MonadFreshNames m =>-            String -> Type -> m Ident+newIdent ::+  MonadFreshNames m =>+  String ->+  Type ->+  m Ident newIdent s t = do   s' <- newID $ nameFromString s   return $ Ident s' t  -- | Produce a fresh 'Ident', using the given 'Ident' as a template, -- but possibly modifying the name.-newIdent' :: MonadFreshNames m =>-             (String -> String)-          -> Ident -> m Ident+newIdent' ::+  MonadFreshNames m =>+  (String -> String) ->+  Ident ->+  m Ident newIdent' f ident =-  newIdent (f $ nameToString $ baseName $ identName ident)-           (identType ident)+  newIdent+    (f $ nameToString $ baseName $ identName ident)+    (identType ident)  -- | Produce a fresh 'Param', using the given name as a template.-newParam :: MonadFreshNames m =>-            String -> dec -> m (Param dec)+newParam ::+  MonadFreshNames m =>+  String ->+  dec ->+  m (Param dec) newParam s t = do   s' <- newID $ nameFromString s   return $ Param s' t@@ -116,22 +132,30 @@   getNameSource = lift getNameSource   putNameSource = lift . putNameSource -instance (MonadFreshNames m, Monoid s) =>-         MonadFreshNames (Control.Monad.Writer.Lazy.WriterT s m) where+instance+  (MonadFreshNames m, Monoid s) =>+  MonadFreshNames (Control.Monad.Writer.Lazy.WriterT s m)+  where   getNameSource = lift getNameSource   putNameSource = lift . putNameSource -instance (MonadFreshNames m, Monoid s) =>-         MonadFreshNames (Control.Monad.Writer.Strict.WriterT s m) where+instance+  (MonadFreshNames m, Monoid s) =>+  MonadFreshNames (Control.Monad.Writer.Strict.WriterT s m)+  where   getNameSource = lift getNameSource   putNameSource = lift . putNameSource -instance MonadFreshNames m =>-         MonadFreshNames (Control.Monad.Trans.Maybe.MaybeT m) where+instance+  MonadFreshNames m =>+  MonadFreshNames (Control.Monad.Trans.Maybe.MaybeT m)+  where   getNameSource = lift getNameSource   putNameSource = lift . putNameSource -instance MonadFreshNames m =>-         MonadFreshNames (ExceptT e m) where+instance+  MonadFreshNames m =>+  MonadFreshNames (ExceptT e m)+  where   getNameSource = lift getNameSource   putNameSource = lift . putNameSource
src/Futhark/Optimise/CSE.hs view
@@ -1,7 +1,8 @@-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE UndecidableInstances #-}+ -- | This module implements common-subexpression elimination.  This -- module does not actually remove the duplicate, but only replaces -- one with a diference to the other.  E.g:@@ -27,27 +28,30 @@ -- equal to any other, since the variable names will be distinct. -- This affects SOACs in particular. module Futhark.Optimise.CSE-       ( performCSE-       , performCSEOnFunDef-       , performCSEOnStms-       , CSEInOp-       )-       where+  ( performCSE,+    performCSEOnFunDef,+    performCSEOnStms,+    CSEInOp,+  )+where  import Control.Monad.Reader import qualified Data.Map.Strict as M- import Futhark.Analysis.Alias import Futhark.IR-import Futhark.IR.Prop.Aliases import Futhark.IR.Aliases-  (removeProgAliases, removeFunDefAliases, removeStmAliases,-   Aliases, consumedInStms)+  ( Aliases,+    consumedInStms,+    removeFunDefAliases,+    removeProgAliases,+    removeStmAliases,+  ) import qualified Futhark.IR.Kernels.Kernel as Kernel-import qualified Futhark.IR.SOACS.SOAC as SOAC import qualified Futhark.IR.Mem as Memory-import Futhark.Transform.Substitute+import Futhark.IR.Prop.Aliases+import qualified Futhark.IR.SOACS.SOAC as SOAC import Futhark.Pass+import Futhark.Transform.Substitute  -- | Perform CSE on every function in a program. --@@ -55,19 +59,26 @@ -- expressions producing arrays. This should be disabled when the lore has -- memory information, since at that point arrays have identity beyond their -- value.-performCSE :: (ASTLore lore, CanBeAliased (Op lore),-               CSEInOp (OpWithAliases (Op lore))) =>-              Bool -> Pass lore lore+performCSE ::+  ( ASTLore lore,+    CanBeAliased (Op lore),+    CSEInOp (OpWithAliases (Op lore))+  ) =>+  Bool ->+  Pass lore lore performCSE cse_arrays =   Pass "CSE" "Combine common subexpressions." $-  fmap removeProgAliases .-  intraproceduralTransformationWithConsts onConsts onFun .-  aliasAnalysis-  where onConsts stms =-          pure $ fst $-          runReader (cseInStms (consumedInStms stms) (stmsToList stms) (return ()))-          (newCSEState cse_arrays)-        onFun _ = pure . cseInFunDef cse_arrays+    fmap removeProgAliases+      . intraproceduralTransformationWithConsts onConsts onFun+      . aliasAnalysis+  where+    onConsts stms =+      pure $+        fst $+          runReader+            (cseInStms (consumedInStms stms) (stmsToList stms) (return ()))+            (newCSEState cse_arrays)+    onFun _ = pure . cseInFunDef cse_arrays  -- | Perform CSE on a single function. --@@ -75,9 +86,14 @@ -- expressions producing arrays. This should be disabled when the lore has -- memory information, since at that point arrays have identity beyond their -- value.-performCSEOnFunDef :: (ASTLore lore, CanBeAliased (Op lore),-                       CSEInOp (OpWithAliases (Op lore))) =>-                      Bool -> FunDef lore -> FunDef lore+performCSEOnFunDef ::+  ( ASTLore lore,+    CanBeAliased (Op lore),+    CSEInOp (OpWithAliases (Op lore))+  ) =>+  Bool ->+  FunDef lore ->+  FunDef lore performCSEOnFunDef cse_arrays =   removeFunDefAliases . cseInFunDef cse_arrays . analyseFun @@ -87,106 +103,139 @@ -- expressions producing arrays. This should be disabled when the lore has -- memory information, since at that point arrays have identity beyond their -- value.-performCSEOnStms :: (ASTLore lore, CanBeAliased (Op lore),-                     CSEInOp (OpWithAliases (Op lore))) =>-                    Bool -> Stms lore -> Stms lore+performCSEOnStms ::+  ( ASTLore lore,+    CanBeAliased (Op lore),+    CSEInOp (OpWithAliases (Op lore))+  ) =>+  Bool ->+  Stms lore ->+  Stms lore performCSEOnStms cse_arrays =   fmap removeStmAliases . f . fst . analyseStms mempty-  where f stms =-          fst $ runReader (cseInStms (consumedInStms stms)-                           (stmsToList stms) (return ()))+  where+    f stms =+      fst $+        runReader+          ( cseInStms+              (consumedInStms stms)+              (stmsToList stms)+              (return ())+          )           (newCSEState cse_arrays) -cseInFunDef :: (ASTLore lore, Aliased lore, CSEInOp (Op lore)) =>-               Bool -> FunDef lore -> FunDef lore+cseInFunDef ::+  (ASTLore lore, Aliased lore, CSEInOp (Op lore)) =>+  Bool ->+  FunDef lore ->+  FunDef lore cseInFunDef cse_arrays fundec =-  fundec { funDefBody =-              runReader (cseInBody ds $ funDefBody fundec) $ newCSEState cse_arrays-         }-  where ds = map (diet . declExtTypeOf) $ funDefRetType fundec+  fundec+    { funDefBody =+        runReader (cseInBody ds $ funDefBody fundec) $ newCSEState cse_arrays+    }+  where+    ds = map (diet . declExtTypeOf) $ funDefRetType fundec  type CSEM lore = Reader (CSEState lore) -cseInBody :: (ASTLore lore, Aliased lore, CSEInOp (Op lore)) =>-             [Diet] -> Body lore -> CSEM lore (Body lore)+cseInBody ::+  (ASTLore lore, Aliased lore, CSEInOp (Op lore)) =>+  [Diet] ->+  Body lore ->+  CSEM lore (Body lore) cseInBody ds (Body bodydec bnds res) = do   (bnds', res') <-     cseInStms (res_cons <> consumedInStms bnds) (stmsToList bnds) $ do-    CSEState (_, nsubsts) _ <- ask-    return $ substituteNames nsubsts res+      CSEState (_, nsubsts) _ <- ask+      return $ substituteNames nsubsts res   return $ Body bodydec bnds' res'-  where res_cons = mconcat $ zipWith consumeResult ds res-        consumeResult Consume se = freeIn se-        consumeResult _ _ = mempty+  where+    res_cons = mconcat $ zipWith consumeResult ds res+    consumeResult Consume se = freeIn se+    consumeResult _ _ = mempty -cseInLambda :: (ASTLore lore, Aliased lore, CSEInOp (Op lore)) =>-               Lambda lore -> CSEM lore (Lambda lore)+cseInLambda ::+  (ASTLore lore, Aliased lore, CSEInOp (Op lore)) =>+  Lambda lore ->+  CSEM lore (Lambda lore) cseInLambda lam = do   body' <- cseInBody (map (const Observe) $ lambdaReturnType lam) $ lambdaBody lam-  return lam { lambdaBody = body' }+  return lam {lambdaBody = body'} -cseInStms :: (ASTLore lore, Aliased lore, CSEInOp (Op lore)) =>-             Names -> [Stm lore]-          -> CSEM lore a-          -> CSEM lore (Stms lore, a)-cseInStms _ [] m = do a <- m-                      return (mempty, a)-cseInStms consumed (bnd:bnds) m =+cseInStms ::+  (ASTLore lore, Aliased lore, CSEInOp (Op lore)) =>+  Names ->+  [Stm lore] ->+  CSEM lore a ->+  CSEM lore (Stms lore, a)+cseInStms _ [] m = do+  a <- m+  return (mempty, a)+cseInStms consumed (bnd : bnds) m =   cseInStm consumed bnd $ \bnd' -> do     (bnds', a) <- cseInStms consumed bnds m     bnd'' <- mapM nestedCSE bnd'-    return (stmsFromList bnd''<>bnds', a)-  where nestedCSE bnd' = do-          let ds = map patElemDiet $ patternValueElements $ stmPattern bnd'-          e <- mapExpM (cse ds) $ stmExp bnd'-          return bnd' { stmExp = e }+    return (stmsFromList bnd'' <> bnds', a)+  where+    nestedCSE bnd' = do+      let ds = map patElemDiet $ patternValueElements $ stmPattern bnd'+      e <- mapExpM (cse ds) $ stmExp bnd'+      return bnd' {stmExp = e} -        cse ds = identityMapper { mapOnBody = const $ cseInBody ds-                                , mapOnOp = cseInOp-                                }+    cse ds =+      identityMapper+        { mapOnBody = const $ cseInBody ds,+          mapOnOp = cseInOp+        } -        patElemDiet pe | patElemName pe `nameIn` consumed = Consume-                       | otherwise                        = Observe+    patElemDiet pe+      | patElemName pe `nameIn` consumed = Consume+      | otherwise = Observe -cseInStm :: ASTLore lore =>-            Names -> Stm lore-         -> ([Stm lore] -> CSEM lore a)-         -> CSEM lore a+cseInStm ::+  ASTLore lore =>+  Names ->+  Stm lore ->+  ([Stm lore] -> CSEM lore a) ->+  CSEM lore a cseInStm consumed (Let pat (StmAux cs attrs edec) e) m = do   CSEState (esubsts, nsubsts) cse_arrays <- ask   let e' = substituteNames nsubsts e       pat' = substituteNames nsubsts pat-  if any (bad cse_arrays) $ patternValueElements pat then-    m [Let pat' (StmAux cs attrs edec) e']-    else-    case M.lookup (edec, e') esubsts of+  if any (bad cse_arrays) $ patternValueElements pat+    then m [Let pat' (StmAux cs attrs edec) e']+    else case M.lookup (edec, e') esubsts of       Just subpat ->         local (addNameSubst pat' subpat) $ do           let lets =                 [ Let (Pattern [] [patElem']) (StmAux cs attrs edec) $                     BasicOp $ SubExp $ Var $ patElemName patElem-                | (name,patElem) <- zip (patternNames pat') $ patternElements subpat ,-                  let patElem' = patElem { patElemName = name }+                  | (name, patElem) <- zip (patternNames pat') $ patternElements subpat,+                    let patElem' = patElem {patElemName = name}                 ]           m lets-      _ -> local (addExpSubst pat' edec e') $-           m [Let pat' (StmAux cs attrs edec) e']+      _ ->+        local (addExpSubst pat' edec e') $+          m [Let pat' (StmAux cs attrs edec) e']+  where+    bad cse_arrays pe+      | Mem {} <- patElemType pe = True+      | Array {} <- patElemType pe, not cse_arrays = True+      | patElemName pe `nameIn` consumed = True+      | otherwise = False -  where bad cse_arrays pe-          | Mem{} <- patElemType pe = True-          | Array{} <- patElemType pe, not cse_arrays = True-          | patElemName pe `nameIn` consumed = True-          | otherwise = False+type ExpressionSubstitutions lore =+  M.Map+    (ExpDec lore, Exp lore)+    (Pattern lore) -type ExpressionSubstitutions lore = M.Map-                                    (ExpDec lore, Exp lore)-                                    (Pattern lore) type NameSubstitutions = M.Map VName VName  data CSEState lore = CSEState-                     { _cseSubstitutions :: (ExpressionSubstitutions lore, NameSubstitutions)-                     , _cseArrays :: Bool-                     }+  { _cseSubstitutions :: (ExpressionSubstitutions lore, NameSubstitutions),+    _cseArrays :: Bool+  }  newCSEState :: Bool -> CSEState lore newCSEState = CSEState (M.empty, M.empty)@@ -198,12 +247,15 @@ addNameSubst pat subpat (CSEState (esubsts, nsubsts) cse_arrays) =   CSEState (esubsts, mkSubsts pat subpat `M.union` nsubsts) cse_arrays -addExpSubst :: ASTLore lore =>-               Pattern lore -> ExpDec lore -> Exp lore-            -> CSEState lore-            -> CSEState lore+addExpSubst ::+  ASTLore lore =>+  Pattern lore ->+  ExpDec lore ->+  Exp lore ->+  CSEState lore ->+  CSEState lore addExpSubst pat edec e (CSEState (esubsts, nsubsts) cse_arrays) =-  CSEState (M.insert (edec,e) pat esubsts, nsubsts) cse_arrays+  CSEState (M.insert (edec, e) pat esubsts, nsubsts) cse_arrays  -- | The operations that permit CSE. class CSEInOp op where@@ -218,30 +270,44 @@   CSEState _ cse_arrays <- ask   return $ runReader m $ newCSEState cse_arrays -instance (ASTLore lore, Aliased lore,-          CSEInOp (Op lore), CSEInOp op) => CSEInOp (Kernel.HostOp lore op) where+instance+  ( ASTLore lore,+    Aliased lore,+    CSEInOp (Op lore),+    CSEInOp op+  ) =>+  CSEInOp (Kernel.HostOp lore op)+  where   cseInOp (Kernel.SegOp op) = Kernel.SegOp <$> cseInOp op   cseInOp (Kernel.OtherOp op) = Kernel.OtherOp <$> cseInOp op   cseInOp x = return x -instance (ASTLore lore, Aliased lore, CSEInOp (Op lore)) =>-         CSEInOp (Kernel.SegOp lvl lore) where-  cseInOp = subCSE .-            Kernel.mapSegOpM-            (Kernel.SegOpMapper return cseInLambda cseInKernelBody return return)+instance+  (ASTLore lore, Aliased lore, CSEInOp (Op lore)) =>+  CSEInOp (Kernel.SegOp lvl lore)+  where+  cseInOp =+    subCSE+      . Kernel.mapSegOpM+        (Kernel.SegOpMapper return cseInLambda cseInKernelBody return return) -cseInKernelBody :: (ASTLore lore, Aliased lore, CSEInOp (Op lore)) =>-                   Kernel.KernelBody lore -> CSEM lore (Kernel.KernelBody lore)+cseInKernelBody ::+  (ASTLore lore, Aliased lore, CSEInOp (Op lore)) =>+  Kernel.KernelBody lore ->+  CSEM lore (Kernel.KernelBody lore) cseInKernelBody (Kernel.KernelBody bodydec bnds res) = do   Body _ bnds' _ <- cseInBody (map (const Observe) res) $ Body bodydec bnds []   return $ Kernel.KernelBody bodydec bnds' res  instance CSEInOp op => CSEInOp (Memory.MemOp op) where-  cseInOp o@Memory.Alloc{} = return o+  cseInOp o@Memory.Alloc {} = return o   cseInOp (Memory.Inner k) = Memory.Inner <$> subCSE (cseInOp k) -instance (ASTLore lore,-          CanBeAliased (Op lore),-          CSEInOp (OpWithAliases (Op lore))) =>-         CSEInOp (SOAC.SOAC (Aliases lore)) where+instance+  ( ASTLore lore,+    CanBeAliased (Op lore),+    CSEInOp (OpWithAliases (Op lore))+  ) =>+  CSEInOp (SOAC.SOAC (Aliases lore))+  where   cseInOp = subCSE . SOAC.mapSOACM (SOAC.SOACMapper return cseInLambda return)
src/Futhark/Optimise/DoubleBuffer.hs view
@@ -1,10 +1,11 @@ {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+ -- | The simplification engine is only willing to hoist allocations -- out of loops if the memory block resulting from the allocation is -- dead at the end of the loop.  If it is not, we may cause data@@ -21,117 +22,132 @@ -- value.  This has the effect of making the memory block returned by -- the array non-existential, which is important for later memory -- expansion to work.-module Futhark.Optimise.DoubleBuffer-       ( doubleBuffer )-       where+module Futhark.Optimise.DoubleBuffer (doubleBuffer) where -import           Control.Monad.State-import           Control.Monad.Writer-import           Control.Monad.Reader+import Control.Monad.Reader+import Control.Monad.State+import Control.Monad.Writer+import Data.List (find) import qualified Data.Map.Strict as M-import           Data.Maybe-import           Data.List (find)--import           Futhark.Construct-import           Futhark.IR-import           Futhark.Pass.ExplicitAllocations (arraySizeInBytesExp)-import           Futhark.Pass.ExplicitAllocations.Kernels ()+import Data.Maybe+import Futhark.Construct+import Futhark.IR+import Futhark.IR.KernelsMem import qualified Futhark.IR.Mem.IxFun as IxFun-import           Futhark.IR.KernelsMem-import           Futhark.Pass-import           Futhark.Util (maybeHead)+import Futhark.Pass+import Futhark.Pass.ExplicitAllocations (arraySizeInBytesExp)+import Futhark.Pass.ExplicitAllocations.Kernels ()+import Futhark.Util (maybeHead)  -- | The double buffering pass definition. doubleBuffer :: Pass KernelsMem KernelsMem doubleBuffer =-  Pass { passName = "Double buffer"-       , passDescription = "Perform double buffering for merge parameters of sequential loops."-       , passFunction = intraproceduralTransformation optimise-       }-  where optimise scope stms = modifyNameSource $ \src ->-          let m = runDoubleBufferM $ localScope scope $-                  fmap stmsFromList $ optimiseStms $ stmsToList stms-          in runState (runReaderT m env) src+  Pass+    { passName = "Double buffer",+      passDescription = "Perform double buffering for merge parameters of sequential loops.",+      passFunction = intraproceduralTransformation optimise+    }+  where+    optimise scope stms = modifyNameSource $ \src ->+      let m =+            runDoubleBufferM $+              localScope scope $+                fmap stmsFromList $ optimiseStms $ stmsToList stms+       in runState (runReaderT m env) src -        env = Env mempty doNotTouchLoop-        doNotTouchLoop ctx val body = return (mempty, ctx, val, body)+    env = Env mempty doNotTouchLoop+    doNotTouchLoop ctx val body = return (mempty, ctx, val, body) -data Env = Env { envScope :: Scope KernelsMem-               , envOptimiseLoop :: OptimiseLoop-               }+data Env = Env+  { envScope :: Scope KernelsMem,+    envOptimiseLoop :: OptimiseLoop+  } -newtype DoubleBufferM a =-  DoubleBufferM { runDoubleBufferM :: ReaderT Env (State VNameSource) a }+newtype DoubleBufferM a = DoubleBufferM {runDoubleBufferM :: ReaderT Env (State VNameSource) a}   deriving (Functor, Applicative, Monad, MonadReader Env, MonadFreshNames)  instance HasScope KernelsMem DoubleBufferM where   askScope = asks envScope  instance LocalScope KernelsMem DoubleBufferM where-  localScope scope = local $ \env -> env { envScope = envScope env <> scope }+  localScope scope = local $ \env -> env {envScope = envScope env <> scope}  optimiseBody :: Body KernelsMem -> DoubleBufferM (Body KernelsMem) optimiseBody body = do   bnds' <- optimiseStms $ stmsToList $ bodyStms body-  return $ body { bodyStms = stmsFromList bnds' }+  return $ body {bodyStms = stmsFromList bnds'}  optimiseStms :: [Stm KernelsMem] -> DoubleBufferM [Stm KernelsMem] optimiseStms [] = return []-optimiseStms (e:es) = do+optimiseStms (e : es) = do   e_es <- optimiseStm e   es' <- localScope (castScope $ scopeOf e_es) $ optimiseStms es   return $ e_es ++ es'  optimiseStm :: Stm KernelsMem -> DoubleBufferM [Stm KernelsMem] optimiseStm (Let pat aux (DoLoop ctx val form body)) = do-  body' <- localScope (scopeOf form <> scopeOfFParams (map fst $ ctx++val)) $-           optimiseBody body+  body' <-+    localScope (scopeOf form <> scopeOfFParams (map fst $ ctx ++ val)) $+      optimiseBody body   opt_loop <- asks envOptimiseLoop   (bnds, ctx', val', body'') <- opt_loop ctx val body'   return $ bnds ++ [Let pat aux $ DoLoop ctx' val' form body''] optimiseStm (Let pat aux e) =   pure . Let pat aux <$> mapExpM optimise e-  where optimise = identityMapper { mapOnBody = \_ x ->-                                      optimiseBody x :: DoubleBufferM (Body KernelsMem)-                                  , mapOnOp = optimiseOp-                                  }+  where+    optimise =+      identityMapper+        { mapOnBody = \_ x ->+            optimiseBody x :: DoubleBufferM (Body KernelsMem),+          mapOnOp = optimiseOp+        } -optimiseOp :: Op KernelsMem-           -> DoubleBufferM (Op KernelsMem)+optimiseOp ::+  Op KernelsMem ->+  DoubleBufferM (Op KernelsMem) optimiseOp (Inner (SegOp op)) =   local inSegOp $ Inner . SegOp <$> mapSegOpM mapper op-  where mapper = identitySegOpMapper-                 { mapOnSegOpLambda = optimiseLambda-                 , mapOnSegOpBody = optimiseKernelBody-                 }-        inSegOp env = env { envOptimiseLoop = optimiseLoop }+  where+    mapper =+      identitySegOpMapper+        { mapOnSegOpLambda = optimiseLambda,+          mapOnSegOpBody = optimiseKernelBody+        }+    inSegOp env = env {envOptimiseLoop = optimiseLoop} optimiseOp op = return op -optimiseKernelBody :: KernelBody KernelsMem-                   -> DoubleBufferM (KernelBody KernelsMem)+optimiseKernelBody ::+  KernelBody KernelsMem ->+  DoubleBufferM (KernelBody KernelsMem) optimiseKernelBody kbody = do   stms' <- optimiseStms $ stmsToList $ kernelBodyStms kbody-  return $ kbody { kernelBodyStms = stmsFromList stms' }+  return $ kbody {kernelBodyStms = stmsFromList stms'}  optimiseLambda :: Lambda KernelsMem -> DoubleBufferM (Lambda KernelsMem) optimiseLambda lam = do   body <- localScope (castScope $ scopeOf lam) $ optimiseBody $ lambdaBody lam-  return lam { lambdaBody = body }+  return lam {lambdaBody = body}  type OptimiseLoop =-  [(FParam KernelsMem, SubExp)] -> [(FParam KernelsMem, SubExp)] -> Body KernelsMem-  -> DoubleBufferM ([Stm KernelsMem],-                    [(FParam KernelsMem, SubExp)],-                    [(FParam KernelsMem, SubExp)],-                    Body KernelsMem)+  [(FParam KernelsMem, SubExp)] ->+  [(FParam KernelsMem, SubExp)] ->+  Body KernelsMem ->+  DoubleBufferM+    ( [Stm KernelsMem],+      [(FParam KernelsMem, SubExp)],+      [(FParam KernelsMem, SubExp)],+      Body KernelsMem+    )  optimiseLoop :: OptimiseLoop optimiseLoop ctx val body = do   -- We start out by figuring out which of the merge variables should   -- be double-buffered.-  buffered <- doubleBufferMergeParams-              (zip (map fst ctx) (bodyResult body)) (map fst merge)-              (boundInBody body)+  buffered <-+    doubleBufferMergeParams+      (zip (map fst ctx) (bodyResult body))+      (map fst merge)+      (boundInBody body)   -- Then create the allocations of the buffers and copies of the   -- initial values.   (merge', allocs) <- allocStms merge buffered@@ -140,124 +156,144 @@       (ctx', val') = splitAt (length ctx) merge'   -- Modify the initial merge p   return (allocs, ctx', val', body')-  where merge = ctx ++ val+  where+    merge = ctx ++ val  -- | The booleans indicate whether we should also play with the -- initial merge values.-data DoubleBuffer = BufferAlloc VName (PrimExp VName) Space Bool-                  | BufferCopy VName IxFun VName Bool-                    -- ^ First name is the memory block to copy to,-                    -- second is the name of the array copy.-                  | NoBuffer-                    deriving (Show)+data DoubleBuffer+  = BufferAlloc VName (PrimExp VName) Space Bool+  | -- | First name is the memory block to copy to,+    -- second is the name of the array copy.+    BufferCopy VName IxFun VName Bool+  | NoBuffer+  deriving (Show) -doubleBufferMergeParams :: MonadFreshNames m =>-                           [(FParam KernelsMem, SubExp)]-                        -> [FParam KernelsMem] -> Names-                        -> m [DoubleBuffer]+doubleBufferMergeParams ::+  MonadFreshNames m =>+  [(FParam KernelsMem, SubExp)] ->+  [FParam KernelsMem] ->+  Names ->+  m [DoubleBuffer] doubleBufferMergeParams ctx_and_res val_params bound_in_loop =   evalStateT (mapM buffer val_params) M.empty-  where loopVariant v = v `nameIn` bound_in_loop ||-                        v `elem` map (paramName . fst) ctx_and_res+  where+    loopVariant v =+      v `nameIn` bound_in_loop+        || v `elem` map (paramName . fst) ctx_and_res -        loopInvariantSize (Constant v) =-          Just (Constant v, True)-        loopInvariantSize (Var v) =-          case find ((==v) . paramName . fst) ctx_and_res of-            Just (_, Constant val) ->-              Just (Constant val, False)-            Just (_, Var v') | not $ loopVariant v' ->-              Just (Var v', False)-            Just _ ->-              Nothing-            Nothing ->-              Just (Var v, True)+    loopInvariantSize (Constant v) =+      Just (Constant v, True)+    loopInvariantSize (Var v) =+      case find ((== v) . paramName . fst) ctx_and_res of+        Just (_, Constant val) ->+          Just (Constant val, False)+        Just (_, Var v')+          | not $ loopVariant v' ->+            Just (Var v', False)+        Just _ ->+          Nothing+        Nothing ->+          Just (Var v, True) -        sizeForMem mem = maybeHead $ mapMaybe (arrayInMem . paramDec) val_params-          where arrayInMem (MemArray pt shape _ (ArrayIn arraymem ixfun))-                  | IxFun.isDirect ixfun,-                    Just (dims, b) <--                      mapAndUnzipM loopInvariantSize $ shapeDims shape,-                    mem == arraymem =-                      Just (arraySizeInBytesExp $-                             Array pt (Shape dims) NoUniqueness,-                            or b)-                arrayInMem _ = Nothing+    sizeForMem mem = maybeHead $ mapMaybe (arrayInMem . paramDec) val_params+      where+        arrayInMem (MemArray pt shape _ (ArrayIn arraymem ixfun))+          | IxFun.isDirect ixfun,+            Just (dims, b) <-+              mapAndUnzipM loopInvariantSize $ shapeDims shape,+            mem == arraymem =+            Just+              ( arraySizeInBytesExp $+                  Array pt (Shape dims) NoUniqueness,+                or b+              )+        arrayInMem _ = Nothing -        buffer fparam = case paramType fparam of-          Mem space-            | Just (size, b) <- sizeForMem $ paramName fparam -> do-                -- Let us double buffer this!-                bufname <- lift $ newVName "double_buffer_mem"-                modify $ M.insert (paramName fparam) (bufname, b)-                return $ BufferAlloc bufname size space b-          Array {}-            | MemArray _ _ _ (ArrayIn mem ixfun) <- paramDec fparam -> do-                buffered <- gets $ M.lookup mem-                case buffered of-                  Just (bufname, b) -> do-                    copyname <- lift $ newVName "double_buffer_array"-                    return $ BufferCopy bufname ixfun copyname b-                  Nothing ->-                    return NoBuffer-          _ -> return NoBuffer+    buffer fparam = case paramType fparam of+      Mem space+        | Just (size, b) <- sizeForMem $ paramName fparam -> do+          -- Let us double buffer this!+          bufname <- lift $ newVName "double_buffer_mem"+          modify $ M.insert (paramName fparam) (bufname, b)+          return $ BufferAlloc bufname size space b+      Array {}+        | MemArray _ _ _ (ArrayIn mem ixfun) <- paramDec fparam -> do+          buffered <- gets $ M.lookup mem+          case buffered of+            Just (bufname, b) -> do+              copyname <- lift $ newVName "double_buffer_array"+              return $ BufferCopy bufname ixfun copyname b+            Nothing ->+              return NoBuffer+      _ -> return NoBuffer -allocStms :: [(FParam KernelsMem, SubExp)] -> [DoubleBuffer]-          -> DoubleBufferM ([(FParam KernelsMem, SubExp)], [Stm KernelsMem])+allocStms ::+  [(FParam KernelsMem, SubExp)] ->+  [DoubleBuffer] ->+  DoubleBufferM ([(FParam KernelsMem, SubExp)], [Stm KernelsMem]) allocStms merge = runWriterT . zipWithM allocation merge-  where allocation m@(Param pname _, _) (BufferAlloc name size space b) = do-          stms <- lift $ runBinder_ $ do-            size' <- toSubExp "double_buffer_size" size-            letBindNames [name] $ Op $ Alloc size' space-          tell $ stmsToList stms-          if b then return (Param pname $ MemMem space, Var name)-               else return m-        allocation (f, Var v) (BufferCopy mem _ _ b) | b = do-          v_copy <- lift $ newVName $ baseString v ++ "_double_buffer_copy"-          (_v_mem, v_ixfun) <- lift $ lookupArraySummary v-          let bt = elemType $ paramType f-              shape = arrayShape $ paramType f-              bound = MemArray bt shape NoUniqueness $ ArrayIn mem v_ixfun-          tell [Let (Pattern [] [PatElem v_copy bound]) (defAux ()) $-                BasicOp $ Copy v]-          -- It is important that we treat this as a consumption, to-          -- avoid the Copy from being hoisted out of any enclosing-          -- loops.  Since we re-use (=overwrite) memory in the loop,-          -- the copy is critical for initialisation.  See issue #816.-          let uniqueMemInfo (MemArray pt pshape _ ret) =-                MemArray pt pshape Unique ret-              uniqueMemInfo info = info-          return (uniqueMemInfo <$> f, Var v_copy)-        allocation (f, se) _ =-          return (f, se)+  where+    allocation m@(Param pname _, _) (BufferAlloc name size space b) = do+      stms <- lift $+        runBinder_ $ do+          size' <- toSubExp "double_buffer_size" size+          letBindNames [name] $ Op $ Alloc size' space+      tell $ stmsToList stms+      if b+        then return (Param pname $ MemMem space, Var name)+        else return m+    allocation (f, Var v) (BufferCopy mem _ _ b) | b = do+      v_copy <- lift $ newVName $ baseString v ++ "_double_buffer_copy"+      (_v_mem, v_ixfun) <- lift $ lookupArraySummary v+      let bt = elemType $ paramType f+          shape = arrayShape $ paramType f+          bound = MemArray bt shape NoUniqueness $ ArrayIn mem v_ixfun+      tell+        [ Let (Pattern [] [PatElem v_copy bound]) (defAux ()) $+            BasicOp $ Copy v+        ]+      -- It is important that we treat this as a consumption, to+      -- avoid the Copy from being hoisted out of any enclosing+      -- loops.  Since we re-use (=overwrite) memory in the loop,+      -- the copy is critical for initialisation.  See issue #816.+      let uniqueMemInfo (MemArray pt pshape _ ret) =+            MemArray pt pshape Unique ret+          uniqueMemInfo info = info+      return (uniqueMemInfo <$> f, Var v_copy)+    allocation (f, se) _ =+      return (f, se) -doubleBufferResult :: [FParam KernelsMem] -> [DoubleBuffer]-                   -> Body KernelsMem -> Body KernelsMem+doubleBufferResult ::+  [FParam KernelsMem] ->+  [DoubleBuffer] ->+  Body KernelsMem ->+  Body KernelsMem doubleBufferResult valparams buffered (Body () bnds res) =   let (ctx_res, val_res) = splitAt (length res - length valparams) res-      (copybnds,val_res') =+      (copybnds, val_res') =         unzip $ zipWith3 buffer valparams buffered val_res-  in Body () (bnds<>stmsFromList (catMaybes copybnds)) $ ctx_res ++ val_res'-  where buffer _ (BufferAlloc bufname _ _ _) _ =-          (Nothing, Var bufname)--        buffer fparam (BufferCopy bufname ixfun copyname _) (Var v) =-          -- To construct the copy we will need to figure out its type-          -- based on the type of the function parameter.-          let t = resultType $ paramType fparam-              summary = MemArray (elemType t) (arrayShape t) NoUniqueness $ ArrayIn bufname ixfun-              copybnd = Let (Pattern [] [PatElem copyname summary]) (defAux ()) $-                        BasicOp $ Copy v-          in (Just copybnd, Var copyname)--        buffer _ _ se =-          (Nothing, se)+   in Body () (bnds <> stmsFromList (catMaybes copybnds)) $ ctx_res ++ val_res'+  where+    buffer _ (BufferAlloc bufname _ _ _) _ =+      (Nothing, Var bufname)+    buffer fparam (BufferCopy bufname ixfun copyname _) (Var v) =+      -- To construct the copy we will need to figure out its type+      -- based on the type of the function parameter.+      let t = resultType $ paramType fparam+          summary = MemArray (elemType t) (arrayShape t) NoUniqueness $ ArrayIn bufname ixfun+          copybnd =+            Let (Pattern [] [PatElem copyname summary]) (defAux ()) $+              BasicOp $ Copy v+       in (Just copybnd, Var copyname)+    buffer _ _ se =+      (Nothing, se) -        parammap = M.fromList $ zip (map paramName valparams) res+    parammap = M.fromList $ zip (map paramName valparams) res -        resultType t = t `setArrayDims` map substitute (arrayDims t)+    resultType t = t `setArrayDims` map substitute (arrayDims t) -        substitute (Var v)-          | Just replacement <- M.lookup v parammap = replacement-        substitute se =-          se+    substitute (Var v)+      | Just replacement <- M.lookup v parammap = replacement+    substitute se =+      se
src/Futhark/Optimise/Fusion.hs view
@@ -1,980 +1,1125 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}--- | Perform horizontal and vertical fusion of SOACs.  See the paper--- /A T2 Graph-Reduction Approach To Fusion/ for the basic idea (some--- extensions discussed in /Design and GPGPU Performance of Futhark’s--- Redomap Construct/).-module Futhark.Optimise.Fusion ( fuseSOACs )-  where--import Control.Monad.State-import Control.Monad.Reader-import Control.Monad.Except-import Data.Maybe-import qualified Data.Map.Strict as M-import qualified Data.Set      as S-import qualified Data.List         as L--import Futhark.IR.Prop.Aliases-import Futhark.IR.SOACS hiding (SOAC(..))-import qualified Futhark.IR.Aliases as Aliases-import qualified Futhark.IR.SOACS as Futhark-import Futhark.MonadFreshNames-import Futhark.IR.SOACS.Simplify-import Futhark.Optimise.Fusion.LoopKernel-import Futhark.Construct-import qualified Futhark.Analysis.HORep.SOAC as SOAC-import qualified Futhark.Analysis.Alias as Alias-import Futhark.Transform.Rename-import Futhark.Transform.Substitute-import Futhark.Pass-import Futhark.Util (maxinum)--data VarEntry = IsArray VName (NameInfo SOACS) Names SOAC.Input-              | IsNotArray (NameInfo SOACS)--varEntryType :: VarEntry -> NameInfo SOACS-varEntryType (IsArray _ dec _ _) =-  dec-varEntryType (IsNotArray dec) =-  dec--varEntryAliases :: VarEntry -> Names-varEntryAliases (IsArray _ _ x _) = x-varEntryAliases _ = mempty--data FusionGEnv = FusionGEnv {-    soacs      :: M.Map VName [VName]-  -- ^ Mapping from variable name to its entire family.-  , varsInScope:: M.Map VName VarEntry-  , fusedRes   :: FusedRes-  }--lookupArr :: VName -> FusionGEnv -> Maybe SOAC.Input-lookupArr v env = asArray =<< M.lookup v (varsInScope env)-  where asArray (IsArray _ _ _ input) = Just input-        asArray IsNotArray{}          = Nothing--newtype Error = Error String--instance Show Error where-  show (Error msg) = "Fusion error:\n" ++ msg--newtype FusionGM a = FusionGM (ExceptT Error (StateT VNameSource (Reader FusionGEnv)) a)-  deriving (Monad, Applicative, Functor,-            MonadError Error,-            MonadState VNameSource,-            MonadReader FusionGEnv)--instance MonadFreshNames FusionGM where-  getNameSource = get-  putNameSource = put--instance HasScope SOACS FusionGM where-  askScope = asks $ toScope . varsInScope-    where toScope = M.map varEntryType------------------------------------------------------------------------------ Monadic Helpers: bind/new/runFusionGatherM, etc----------------------------------------------------------------------------- | Binds an array name to the set of used-array vars-bindVar :: FusionGEnv -> (Ident, Names) -> FusionGEnv-bindVar env (Ident name t, aliases) =-  env { varsInScope = M.insert name entry $ varsInScope env }-  where entry = case t of-          Array {} -> IsArray name (LetName t) aliases' $ SOAC.identInput $ Ident name t-          _        -> IsNotArray $ LetName t-        expand = maybe mempty varEntryAliases . flip M.lookup (varsInScope env)-        aliases' = aliases <> mconcat (map expand $ namesToList aliases)--bindVars :: FusionGEnv -> [(Ident, Names)] -> FusionGEnv-bindVars = foldl bindVar--binding :: [(Ident, Names)] -> FusionGM a -> FusionGM a-binding vs = local (`bindVars` vs)--gatherStmPattern :: Pattern -> Exp -> FusionGM FusedRes -> FusionGM FusedRes-gatherStmPattern pat e = binding $ zip idents aliases-  where idents = patternIdents pat-        aliases = replicate (length (patternContextNames pat)) mempty ++-                  expAliases (Alias.analyseExp mempty e)--bindingPat :: Pattern -> FusionGM a -> FusionGM a-bindingPat = binding . (`zip` repeat mempty) . patternIdents--bindingParams :: Typed t => [Param t] -> FusionGM a -> FusionGM a-bindingParams = binding . (`zip` repeat mempty) . map paramIdent---- | Binds an array name to the set of soac-produced vars-bindingFamilyVar :: [VName] -> FusionGEnv -> Ident -> FusionGEnv-bindingFamilyVar faml env (Ident nm t) =-  env { soacs       = M.insert nm faml $ soacs env-      , varsInScope = M.insert nm (IsArray nm (LetName t) mempty $-                                   SOAC.identInput $ Ident nm t) $-                      varsInScope env-      }--varAliases :: VName -> FusionGM Names-varAliases v = asks $ (oneName v<>) . maybe mempty varEntryAliases .-                      M.lookup v . varsInScope--varsAliases :: Names -> FusionGM Names-varsAliases = fmap mconcat . mapM varAliases . namesToList--checkForUpdates :: FusedRes -> Exp -> FusionGM FusedRes-checkForUpdates res (BasicOp (Update src is _)) = do-  res' <- foldM addVarToInfusible res $-          src : namesToList (mconcat $ map freeIn is)-  aliases <- varAliases src-  let inspectKer k = k { inplace = aliases <> inplace k }-  return res' { kernels = M.map inspectKer $ kernels res' }-checkForUpdates res _ = return res---- | Updates the environment: (i) the @soacs@ (map) by binding each pattern---   element identifier to all pattern elements (identifiers) and (ii) the---   variables in scope (map) by inserting each (pattern-array) name.---   Finally, if the binding is an in-place update, then the @inplace@ field---   of each (result) kernel is updated with the new in-place updates.-bindingFamily :: Pattern -> FusionGM FusedRes -> FusionGM FusedRes-bindingFamily pat = local bind-  where idents = patternIdents pat-        family = patternNames pat-        bind env = foldl (bindingFamilyVar family) env idents--bindingTransform :: PatElem -> VName -> SOAC.ArrayTransform -> FusionGM a -> FusionGM a-bindingTransform pe srcname trns = local $ \env ->-  case M.lookup srcname $ varsInScope env of-    Just (IsArray src' _ aliases input) ->-      env { varsInScope =-              M.insert vname-              (IsArray src' (LetName dec) (oneName srcname <> aliases) $-               trns `SOAC.addTransform` input) $-              varsInScope env-          }-    _ -> bindVar env (patElemIdent pe, oneName vname)-  where vname = patElemName pe-        dec = patElemDec pe---- | Binds the fusion result to the environment.-bindRes :: FusedRes -> FusionGM a -> FusionGM a-bindRes rrr = local (\x -> x { fusedRes = rrr })---- | The fusion transformation runs in this monad.  The mutable--- state refers to the fresh-names engine.--- The reader hides the vtable that associates ... to ... (fill in, please).--- The 'Either' monad is used for error handling.-runFusionGatherM :: MonadFreshNames m =>-                    FusionGM a -> FusionGEnv -> m (Either Error a)-runFusionGatherM (FusionGM a) env =-  modifyNameSource $ \src -> runReader (runStateT (runExceptT a) src) env------------------------------------------------------------------------------ Fusion Entry Points: gather the to-be-fused kernels@pgm level    -------    and fuse them in a second pass!                               -------------------------------------------------------------------------------- | The pass definition.-fuseSOACs :: Pass SOACS SOACS-fuseSOACs =-  Pass { passName = "Fuse SOACs"-       , passDescription = "Perform higher-order optimisation, i.e., fusion."-       , passFunction = \prog ->-           simplifySOACS =<< renameProg =<<-           intraproceduralTransformationWithConsts-           (fuseConsts (freeIn (progFuns prog))) fuseFun prog-       }--fuseConsts :: Names -> Stms SOACS -> PassM (Stms SOACS)-fuseConsts used_consts consts =-  fuseStms mempty consts $ map Var $ namesToList used_consts--fuseFun :: Stms SOACS -> FunDef SOACS -> PassM (FunDef SOACS)-fuseFun consts fun = do-  stms <- fuseStms (scopeOf consts <> scopeOfFParams (funDefParams fun))-          (bodyStms $ funDefBody fun)-          (bodyResult $ funDefBody fun)-  let body = (funDefBody fun) { bodyStms = stms }-  return fun { funDefBody = body }--fuseStms :: Scope SOACS -> Stms SOACS -> Result -> PassM (Stms SOACS)-fuseStms scope stms res = do-  let env  = FusionGEnv { soacs = M.empty-                        , varsInScope = mempty-                        , fusedRes = mempty-                        }-  k <- cleanFusionResult <$>-       liftEitherM (runFusionGatherM-                    (binding scope' $ fusionGatherStms mempty (stmsToList stms) res)-                    env)-  if not $ rsucc k-  then return stms-  else liftEitherM $ runFusionGatherM (binding scope' $ bindRes k $ fuseInStms stms) env-  where scope' = map toBind $ M.toList scope-        toBind (k, t) = (Ident k $ typeOf t, mempty)-------------------------------------------------------------------------------------------------------------- RESULT's Data Structure------------------------------------------------------------------------------------------------------------ | A type used for (hopefully) uniquely referring a producer SOAC.--- The uniquely identifying value is the name of the first array--- returned from the SOAC.-newtype KernName = KernName { unKernName :: VName }-  deriving (Eq, Ord, Show)--data FusedRes = FusedRes {-    rsucc :: Bool-  -- ^ Whether we have fused something anywhere.--  , outArr     :: M.Map VName KernName-  -- ^ Associates an array to the name of the-  -- SOAC kernel that has produced it.--  , inpArr     :: M.Map VName (S.Set KernName)-  -- ^ Associates an array to the names of the-  -- SOAC kernels that uses it. These sets include-  -- only the SOAC input arrays used as full variables, i.e., no `a[i]'.--  , infusible  :: Names-  -- ^ the (names of) arrays that are not fusible, i.e.,-  ---  --   1. they are either used other than input to SOAC kernels, or-  ---  --   2. are used as input to at least two different kernels that-  --      are not located on disjoint control-flow branches, or-  ---  --   3. are used in the lambda expression of SOACs--  , kernels    :: M.Map KernName FusedKer-  -- ^ The map recording the uses-  }--instance Semigroup FusedRes where-  res1 <> res2 =-    FusedRes (rsucc     res1       ||      rsucc     res2)-             (outArr    res1    `M.union`  outArr    res2)-             (M.unionWith S.union (inpArr res1) (inpArr res2) )-             (infusible res1    <>  infusible res2)-             (kernels   res1    `M.union`  kernels   res2)--instance Monoid FusedRes where-  mempty = FusedRes { rsucc     = False,   outArr = M.empty, inpArr  = M.empty,-                      infusible = mempty, kernels = M.empty }--isInpArrInResModKers :: FusedRes -> S.Set KernName -> VName -> Bool-isInpArrInResModKers ress kers nm =-  case M.lookup nm (inpArr ress) of-    Nothing -> False-    Just s  -> not $ S.null $ s `S.difference` kers--getKersWithInpArrs :: FusedRes -> [VName] -> S.Set KernName-getKersWithInpArrs ress =-  S.unions . mapMaybe (`M.lookup` inpArr ress)---- | extend the set of names to include all the names---     produced via SOACs (by querring the vtable's soac)-expandSoacInpArr :: [VName] -> FusionGM [VName]-expandSoacInpArr =-    foldM (\y nm -> do bnd <- asks $ M.lookup nm . soacs-                       case bnd of-                         Nothing  -> return (y++[nm])-                         Just nns -> return (y++nns )-          ) []-------------------------------------------------------------------------------------------------------------------------------------------------soacInputs :: SOAC -> FusionGM ([VName], [VName])-soacInputs soac = do-  let (inp_idds, other_idds) = getIdentArr $ SOAC.inputs soac-      (inp_nms0, other_nms0) = (inp_idds, other_idds)-  inp_nms   <- expandSoacInpArr   inp_nms0-  other_nms <- expandSoacInpArr other_nms0-  return (inp_nms, other_nms)--addNewKerWithInfusible :: FusedRes -> ([Ident], StmAux (), SOAC, Names) -> Names -> FusionGM FusedRes-addNewKerWithInfusible res (idd, aux, soac, consumed) ufs = do-  nm_ker <- KernName <$> newVName "ker"-  scope <- askScope-  let out_nms = map identName idd-      new_ker = newKernel aux soac consumed out_nms scope-      comb    = M.unionWith S.union-      os' = M.fromList [(arr,nm_ker) | arr <- out_nms]-            `M.union` outArr res-      is' = M.fromList [(arr,S.singleton nm_ker)-                         | arr <- map SOAC.inputArray $ SOAC.inputs soac]-            `comb` inpArr res-  return $ FusedRes (rsucc res) os' is' ufs-           (M.insert nm_ker new_ker (kernels res))--lookupInput :: VName -> FusionGM (Maybe SOAC.Input)-lookupInput name = asks $ lookupArr name--inlineSOACInput :: SOAC.Input -> FusionGM SOAC.Input-inlineSOACInput (SOAC.Input ts v t) = do-  maybe_inp <- lookupInput v-  case maybe_inp of-    Nothing ->-      return $ SOAC.Input ts v t-    Just (SOAC.Input ts2 v2 t2) ->-      return $ SOAC.Input (ts2<>ts) v2 t2--inlineSOACInputs :: SOAC -> FusionGM SOAC-inlineSOACInputs soac = do-  inputs' <- mapM inlineSOACInput $ SOAC.inputs soac-  return $ inputs' `SOAC.setInputs` soac----- | Attempts to fuse between SOACs. Input:---   @rem_bnds@ are the bindings remaining in the current body after @orig_soac@.---   @lam_used_nms@ the infusible names---   @res@ the fusion result (before processing the current soac)---   @orig_soac@ and @out_idds@ the current SOAC and its binding pattern---   @consumed@ is the set of names consumed by the SOAC.---   Output: a new Fusion Result (after processing the current SOAC binding)-greedyFuse :: [Stm] -> Names -> FusedRes -> (Pattern, StmAux (), SOAC, Names)-           -> FusionGM FusedRes-greedyFuse rem_bnds lam_used_nms res (out_idds, aux, orig_soac, consumed) = do-  soac <- inlineSOACInputs orig_soac-  (inp_nms, other_nms) <- soacInputs soac-  -- Assumption: the free vars in lambda are already in @infusible res@.-  let out_nms     = patternNames out_idds-      isInfusible = (`nameIn` infusible res)-      is_screma  = case orig_soac of-                       SOAC.Screma _ form _ ->-                         (isJust (isRedomapSOAC form) || isJust (isScanomapSOAC form)) &&-                         not (isJust (isReduceSOAC form) || isJust (isScanSOAC form))-                       _ -> False-  ---  -- Conditions for fusion:-  -- If current soac is a replicate OR (current soac a redomap/scanomap AND-  --    (i) none of @out_idds@ belongs to the infusible set)-  -- THEN try applying producer-consumer fusion-  -- ELSE try applying horizontal        fusion-  -- (without duplicating computation in both cases)--  (ok_kers_compat, fused_kers, fused_nms, old_kers, oldker_nms) <--        if   is_screma || any isInfusible out_nms-        then horizontGreedyFuse rem_bnds res (out_idds, aux, soac, consumed)-        else prodconsGreedyFuse          res (out_idds, aux, soac, consumed)-  ---  -- (ii) check whether fusing @soac@ will violate any in-place update-  --      restriction, e.g., would move an input array past its in-place update.-  let all_used_names = namesToList $ mconcat [lam_used_nms, namesFromList inp_nms, namesFromList other_nms]-      has_inplace ker = any (`nameIn` inplace ker) all_used_names-      ok_inplace = not $ any has_inplace old_kers-  ---  -- (iii)  there are some kernels that use some of `out_idds' as inputs-  -- (iv)   and producer-consumer or horizontal fusion succeeds with those.-  let fusible_ker = not (null old_kers) && ok_inplace && ok_kers_compat-  ---  -- Start constructing the fusion's result:-  --  (i) inparr ids other than vars will be added to infusible list,-  -- (ii) will also become part of the infusible set the inparr vars-  --         that also appear as inparr of another kernel,-  --         BUT which said kernel is not the one we are fusing with (now)!-  let mod_kerS  = if fusible_ker then S.fromList oldker_nms else mempty-  let used_inps = filter (isInpArrInResModKers res mod_kerS) inp_nms-  let ufs       = mconcat [infusible res, namesFromList used_inps,-                           namesFromList other_nms `namesSubtract`-                           namesFromList (map SOAC.inputArray $ SOAC.inputs soac)]-  let comb      = M.unionWith S.union--  if not fusible_ker then-    addNewKerWithInfusible res (patternIdents out_idds, aux, soac, consumed) ufs-  else do-     -- Need to suitably update `inpArr':-     --   (i) first remove the inpArr bindings of the old kernel-     let inpArr' =-            foldl (\inpa (kold, knm) ->-                    S.foldl'-                        (\inpp nm ->-                           case M.lookup nm inpp of-                             Nothing -> inpp-                             Just s  -> let new_set = S.delete knm s-                                        in if S.null new_set-                                           then M.delete nm         inpp-                                           else M.insert nm new_set inpp-                        )-                    inpa $ arrInputs kold-                 )-            (inpArr res) (zip old_kers oldker_nms)-     --  (ii) then add the inpArr bindings of the new kernel-     let fused_ker_nms = zip fused_nms fused_kers-         inpArr''= foldl (\inpa' (knm, knew) ->-                             M.fromList [ (k, S.singleton knm)-                                         | k <- S.toList $ arrInputs knew ]-                             `comb` inpa'-                         )-                   inpArr' fused_ker_nms-     -- Update the kernels map (why not delete the ones that have been fused?)-     let kernels' = M.fromList fused_ker_nms `M.union` kernels res-     -- nothing to do for `outArr' (since we have not added a new kernel)-     -- DO IMPROVEMENT: attempt to fuse the resulting kernel AGAIN until it fails,-     --                 but make sure NOT to add a new kernel!-     return $ FusedRes True (outArr res) inpArr'' ufs kernels'--prodconsGreedyFuse :: FusedRes -> (Pattern, StmAux (), SOAC, Names)-                   -> FusionGM (Bool, [FusedKer], [KernName], [FusedKer], [KernName])-prodconsGreedyFuse res (out_idds, aux, soac, consumed) = do-  let out_nms        = patternNames out_idds    -- Extract VNames from output patterns-      to_fuse_knmSet = getKersWithInpArrs res out_nms  -- Find kernels which consume outputs-      to_fuse_knms   = S.toList to_fuse_knmSet-      lookup_kern k  = case M.lookup k (kernels res) of-                         Nothing  -> throwError $ Error-                                     ("In Fusion.hs, greedyFuse, comp of to_fuse_kers: "-                                      ++ "kernel name not found in kernels field!")-                         Just ker -> return ker-  to_fuse_kers <- mapM lookup_kern to_fuse_knms -- Get all consumer kernels-  -- try producer-consumer fusion-  (ok_kers_compat, fused_kers) <- do-      kers <- forM to_fuse_kers $-              attemptFusion mempty (patternNames out_idds) soac consumed-      case sequence kers of-        Nothing    -> return (False, [])-        Just kers' -> return (True, map certifyKer kers')-  return (ok_kers_compat, fused_kers, to_fuse_knms, to_fuse_kers, to_fuse_knms)-  where certifyKer k = k { kerAux = kerAux k <> aux }--horizontGreedyFuse :: [Stm] -> FusedRes -> (Pattern, StmAux (), SOAC, Names)-                   -> FusionGM (Bool, [FusedKer], [KernName], [FusedKer], [KernName])-horizontGreedyFuse rem_bnds res (out_idds, aux, soac, consumed) = do-  (inp_nms, _) <- soacInputs soac-  let out_nms        = patternNames out_idds-      infusible_nms  = namesFromList $ filter (`nameIn` infusible res) out_nms-      out_arr_nms    = case soac of-                        -- the accumulator result cannot be fused!-                        SOAC.Screma _ (ScremaForm scans reds _) _ ->-                          drop (scanResults scans + redResults reds) out_nms-                        SOAC.Stream _ frm _ _ -> drop (length $ getStreamAccums frm) out_nms-                        _ -> out_nms-      to_fuse_knms1  = S.toList $ getKersWithInpArrs res (out_arr_nms++inp_nms)-      to_fuse_knms2  = getKersWithSameInpSize (SOAC.width soac) res-      to_fuse_knms   = S.toList $ S.fromList $ to_fuse_knms1 ++ to_fuse_knms2-      lookupKernel k  = case M.lookup k (kernels res) of-                          Nothing  -> throwError $ Error-                                      ("In Fusion.hs, greedyFuse, comp of to_fuse_kers: "-                                       ++ "kernel name not found in kernels field!")-                          Just ker -> return ker--  -- For each kernel get the index in the bindings where the kernel is-  -- located and sort based on the index so that partial fusion may-  -- succeed.  We use the last position where one of the kernel-  -- outputs occur.-  let bnd_nms = map (patternNames . stmPattern) rem_bnds-  kernminds <- forM to_fuse_knms $ \ker_nm -> do-    ker <- lookupKernel ker_nm-    case mapMaybe (\out_nm -> L.findIndex (elem out_nm) bnd_nms) (outNames ker) of-      [] -> return Nothing-      is -> return $ Just (ker,ker_nm,maxinum is)--  scope <- askScope-  let kernminds' = L.sortBy (\(_,_,i1) (_,_,i2)->compare i1 i2) $ catMaybes kernminds-      soac_kernel = newKernel aux soac consumed out_nms scope--  -- now try to fuse kernels one by one (in a fold); @ok_ind@ is the index of the-  -- kernel until which fusion succeded, and @fused_ker@ is the resulting kernel.-  use_scope <- (<>scopeOf rem_bnds) <$> askScope-  (_,ok_ind,_,fused_ker,_) <--      foldM (\(cur_ok,n,prev_ind,cur_ker,ufus_nms) (ker, _ker_nm, bnd_ind) -> do-                -- check that we still try fusion and that the intermediate-                -- bindings do not use the results of cur_ker-                let curker_outnms  = outNames cur_ker-                    curker_outset  = namesFromList curker_outnms-                    new_ufus_nms   = namesFromList $ outNames ker ++ namesToList ufus_nms-                    -- disable horizontal fusion in the case when an output array of-                    -- producer SOAC is a non-trivially transformed input of the consumer-                    out_transf_ok  = let ker_inp = SOAC.inputs $ fsoac ker-                                         unfuse1 = namesFromList (map SOAC.inputArray ker_inp) `namesSubtract`-                                                   namesFromList (mapMaybe SOAC.isVarInput ker_inp)-                                         unfuse2 = namesIntersection curker_outset ufus_nms-                                     in not $ unfuse1 `namesIntersect` unfuse2-                    -- Disable horizontal fusion if consumer has any-                    -- output transforms.-                    cons_no_out_transf = SOAC.nullTransforms $ outputTransform ker--                consumer_ok   <- do let consumer_bnd   = rem_bnds !! bnd_ind-                                    maybesoac <- runReaderT (SOAC.fromExp $ stmExp consumer_bnd) use_scope-                                    case maybesoac of-                                      -- check that consumer's lambda body does not use-                                      -- directly the produced arrays (e.g., see noFusion3.fut).-                                      Right conssoac -> return $ not $-                                                        curker_outset-                                                        `namesIntersect`-                                                        freeIn (lambdaBody $ SOAC.lambda conssoac)-                                      Left _         -> return True--                let interm_bnds_ok = cur_ok && consumer_ok && out_transf_ok && cons_no_out_transf &&-                      foldl (\ok bnd-> ok && -- hardwired to False after first fail-                                       -- (i) check that the in-between bindings do-                                       --     not use the result of current kernel OR-                                       not (curker_outset `namesIntersect` freeIn (stmExp bnd)) ||-                                       --(ii) that the pattern-binding corresponds to-                                       --     the result of the consumer kernel; in the-                                       --     latter case it means it corresponds to a-                                       --     kernel that has been fused in the consumer,-                                       --     hence it should be ignored-                                        not ( null $ curker_outnms `L.intersect`-                                              patternNames (stmPattern bnd))-                            ) True (drop (prev_ind+1) $ take bnd_ind rem_bnds)-                if not interm_bnds_ok then return (False,n,bnd_ind,cur_ker,mempty)-                else do new_ker <- attemptFusion ufus_nms (outNames cur_ker)-                                   (fsoac cur_ker) (fusedConsumed cur_ker) ker-                        case new_ker of-                          Nothing -> return (False, n,bnd_ind,cur_ker,mempty)-                          Just krn->-                            let krn' = krn { kerAux = aux <> kerAux krn }-                            in return (True,n+1,bnd_ind,krn',new_ufus_nms)-            ) (True,0,0,soac_kernel,infusible_nms) kernminds'--  -- Find the kernels we have fused into and the name of the last such-  -- kernel (if any).-  let (to_fuse_kers', to_fuse_knms',_) = unzip3 $ take ok_ind kernminds'-      new_kernms = drop (ok_ind-1) to_fuse_knms'--  return (ok_ind>0, [fused_ker], new_kernms, to_fuse_kers', to_fuse_knms')--  where getKersWithSameInpSize :: SubExp -> FusedRes -> [KernName]-        getKersWithSameInpSize sz ress =-            map fst $ filter (\ (_,ker) -> sz == SOAC.width (fsoac ker)) $ M.toList $ kernels ress-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Fusion Gather for EXPRESSIONS and BODIES,                        ------- i.e., where work is being done:                                  -------    i) bottom-up AbSyn traversal (backward analysis)              -------   ii) soacs are fused greedily iff does not duplicate computation------- E.g., (y1, y2, y3) = mapT(f, x1, x2[i])                          -------       (z1, z2)     = mapT(g1, y1, y2)                            -------       (q1, q2)     = mapT(g2, y3, z1, a, y3)                     -------       res          = reduce(op, ne, q1, q2, z2, y1, y3)          ------- can be fused if y1,y2,y3, z1,z2, q1,q2 are not used elsewhere:   -------       res = redomap(op, \(x1,x2i,a)->                            -------                             let (y1,y2,y3) = f (x1, x2i)       in-------                             let (z1,z2)    = g1(y1, y2)        in-------                             let (q1,q2)    = g2(y3, z1, a, y3) in-------                             (q1, q2, z2, y1, y3)                 -------                     x1, x2[i], a)                                --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------fusionGatherBody :: FusedRes -> Body -> FusionGM FusedRes-fusionGatherBody fres (Body _ stms res) =-  fusionGatherStms fres (stmsToList stms) res--fusionGatherStms :: FusedRes -> [Stm] -> Result -> FusionGM FusedRes---- Some forms of do-loops can profitably be considered streamSeqs.  We--- are careful to ensure that the generated nested loop cannot itself--- be considered a stream, to avoid infinite recursion.-fusionGatherStms fres (Let (Pattern [] pes) bndtp-                       (DoLoop [] merge (ForLoop i it w loop_vars) body) : bnds) res-  | not $ null loop_vars = do-  let (merge_params,merge_init) = unzip merge-      (loop_params,loop_arrs) = unzip loop_vars-  chunk_size <- newVName "chunk_size"-  offset <- newVName "offset"-  let chunk_param = Param chunk_size $ Prim int32-      offset_param = Param offset $ Prim $ IntType it--  acc_params <- forM merge_params $ \p ->-    Param <$> newVName (baseString (paramName p) ++ "_outer") <*>-    pure (paramType p)--  chunked_params <- forM loop_vars $ \(p,arr) ->-    Param <$> newVName (baseString arr ++ "_chunk") <*>-    pure (paramType p `arrayOfRow` Futhark.Var chunk_size)--  let lam_params = chunk_param : acc_params ++ [offset_param] ++ chunked_params--  lam_body <- runBodyBinder $ localScope (scopeOfLParams lam_params) $ do-    let merge' = zip merge_params $ map (Futhark.Var . paramName) acc_params-    j <- newVName "j"-    loop_body <- runBodyBinder $ do-      forM_ (zip loop_params chunked_params) $ \(p,a_p) ->-        letBindNames [paramName p] $ BasicOp $ Index (paramName a_p) $-        fullSlice (paramType a_p) [DimFix $ Futhark.Var j]-      letBindNames [i] $ BasicOp $ BinOp (Add it OverflowUndef) (Futhark.Var offset) (Futhark.Var j)-      return body-    eBody [pure $-           DoLoop [] merge' (ForLoop j it (Futhark.Var chunk_size) []) loop_body,-           pure $-           BasicOp $ BinOp (Add Int32 OverflowUndef) (Futhark.Var offset) (Futhark.Var chunk_size)]-  let lam = Lambda { lambdaParams = lam_params-                   , lambdaBody = lam_body-                   , lambdaReturnType = map paramType $ acc_params ++ [offset_param]-                   }-      stream = Futhark.Stream w (Sequential $ merge_init ++ [intConst it 0]) lam loop_arrs--  -- It is important that the (discarded) final-offset is not the-  -- first element in the pattern, as we use the first element to-  -- identify the SOAC in the second phase of fusion.-  discard <- newVName "discard"-  let discard_pe = PatElem discard $ Prim int32--  fusionGatherStms fres-    (Let (Pattern [] (pes<>[discard_pe])) bndtp (Op stream) : bnds) res--fusionGatherStms fres (bnd@(Let pat _ e):bnds) res = do-  maybesoac <- SOAC.fromExp e-  case maybesoac of-    Right soac@(SOAC.Scatter _len lam _ivs _as) -> do-      -- We put the variables produced by Scatter into the infusible-      -- set to force horizontal fusion.  It is not possible to-      -- producer/consumer-fuse Scatter anyway.-      fres' <- addNamesToInfusible fres $ namesFromList $ patternNames pat-      mapLike fres' soac lam--    Right soac@(SOAC.Hist _ _ lam _) -> do-      -- We put the variables produced by Hist into the infusible-      -- set to force horizontal fusion.  It is not possible to-      -- producer/consumer-fuse Hist anyway.-      fres' <- addNamesToInfusible fres $ namesFromList $ patternNames pat-      mapLike fres' soac lam--    Right soac@(SOAC.Screma _ (ScremaForm scans reds map_lam) _) ->-      reduceLike soac (map scanLambda scans <> map redLambda reds <> [map_lam]) $-      concatMap scanNeutral scans <> concatMap redNeutral reds--    Right soac@(SOAC.Stream _ form lam _) -> do-      -- a redomap does not neccessarily start a new kernel, e.g.,-      -- @let a= reduce(+,0,A) in ... bnds ... in let B = map(f,A)@-      -- can be fused into a redomap that replaces the @map@, if @a@-      -- and @B@ are defined in the same scope and @bnds@ does not uses @a@.-      -- a redomap always starts a new kernel-      let lambdas = case form of-                        Parallel _ _ lout _ -> [lout, lam]-                        _                   -> [lam]-      reduceLike soac lambdas $ getStreamAccums form--    _ | [pe] <- patternValueElements pat,-        Just (src,trns) <- SOAC.transformFromExp (stmCerts bnd) e ->-          bindingTransform pe src trns $ fusionGatherStms fres bnds res-      | otherwise -> do-          let pat_vars = map (BasicOp . SubExp . Var) $ patternNames pat-          bres <- gatherStmPattern pat e $ fusionGatherStms fres bnds res-          bres' <- checkForUpdates bres e-          foldM fusionGatherExp bres' (e:pat_vars)--  where aux = stmAux bnd-        rem_bnds = bnd : bnds-        consumed = consumedInExp $ Alias.analyseExp mempty e--        reduceLike soac lambdas nes = do-          (used_lam, lres)  <- foldM fusionGatherLam (mempty, fres) lambdas-          bres  <- bindingFamily pat $ fusionGatherStms lres bnds res-          bres' <- foldM fusionGatherSubExp bres nes-          consumed' <- varsAliases consumed-          greedyFuse rem_bnds used_lam bres' (pat, aux, soac, consumed')--        mapLike fres' soac lambda = do-          bres  <- bindingFamily pat $ fusionGatherStms fres' bnds res-          (used_lam, blres) <- fusionGatherLam (mempty, bres) lambda-          consumed' <- varsAliases consumed-          greedyFuse rem_bnds used_lam blres (pat, aux, soac, consumed')--fusionGatherStms fres [] res =-  foldM fusionGatherExp fres $ map (BasicOp . SubExp) res--fusionGatherExp :: FusedRes -> Exp -> FusionGM FusedRes------------------------------------------------ Index/If    ------------------------------------------------fusionGatherExp fres (DoLoop ctx val form loop_body) = do-  fres' <- addNamesToInfusible fres $ freeIn form <> freeIn ctx <> freeIn val-  let form_idents =-        case form of-          ForLoop i _ _ loopvars ->-            Ident i (Prim int32) : map (paramIdent . fst) loopvars-          WhileLoop{} -> []--  new_res <- binding (zip (form_idents ++ map (paramIdent . fst) (ctx<>val)) $-                      repeat mempty) $-    fusionGatherBody mempty loop_body-  -- make the inpArr infusible, so that they-  -- cannot be fused from outside the loop:-  let (inp_arrs, _) = unzip $ M.toList $ inpArr new_res-  let new_res' = new_res { infusible = infusible new_res <> mconcat (map oneName inp_arrs) }-  -- merge new_res with fres'-  return $ new_res' <> fres'--fusionGatherExp fres (If cond e_then e_else _) = do-    then_res <- fusionGatherBody mempty e_then-    else_res <- fusionGatherBody mempty e_else-    let both_res = then_res <> else_res-    fres'    <- fusionGatherSubExp fres cond-    mergeFusionRes fres' both_res----------------------------------------------------------------------------------------- Errors: all SOACs, (because normalization ensures they appear---- directly in let exp, i.e., let x = e)--------------------------------------------------------------------------------------fusionGatherExp _ (Op Futhark.Screma{}) = errorIllegal "screma"-fusionGatherExp _ (Op Futhark.Scatter{}) = errorIllegal "write"------------------------------------------ Generic Traversal         ------------------------------------------fusionGatherExp fres e = addNamesToInfusible fres $ freeIn e--fusionGatherSubExp :: FusedRes -> SubExp -> FusionGM FusedRes-fusionGatherSubExp fres (Var idd) = addVarToInfusible fres idd-fusionGatherSubExp fres _         = return fres--addNamesToInfusible :: FusedRes -> Names -> FusionGM FusedRes-addNamesToInfusible fres = foldM addVarToInfusible fres . namesToList--addVarToInfusible :: FusedRes -> VName -> FusionGM FusedRes-addVarToInfusible fres name = do-  trns <- asks $ lookupArr name-  let name' = case trns of-        Nothing         -> name-        Just (SOAC.Input _ orig _) -> orig-  return fres { infusible = oneName name' <> infusible fres }---- Lambdas create a new scope.  Disallow fusing from outside lambda by--- adding inp_arrs to the infusible set.-fusionGatherLam :: (Names, FusedRes) -> Lambda -> FusionGM (Names, FusedRes)-fusionGatherLam (u_set,fres) (Lambda idds body _) = do-    new_res <- bindingParams idds $ fusionGatherBody mempty body-    -- make the inpArr infusible, so that they-    -- cannot be fused from outside the lambda:-    let inp_arrs = namesFromList $ M.keys $ inpArr new_res-    let unfus = infusible new_res <> inp_arrs-    bnds <- asks $ M.keys . varsInScope-    let unfus'  = unfus `namesIntersection` namesFromList bnds-    -- merge fres with new_res'-    let new_res' = new_res { infusible = unfus' }-    -- merge new_res with fres'-    return (u_set <> unfus', new_res' <> fres)--------------------------------------------------------------------------------------------------------------------------------- FINALLY, Substitute the kernels in function------------------------------------------------------------------------------------------------------------------------------fuseInStms :: Stms SOACS -> FusionGM (Stms SOACS)-fuseInStms stms-  | Just (Let pat aux e, stms') <- stmsHead stms = do-      stms'' <- bindingPat pat $ fuseInStms stms'-      soac_bnds <- replaceSOAC pat aux e-      pure $ soac_bnds <> stms''-  | otherwise =-      pure mempty--fuseInBody :: Body -> FusionGM Body-fuseInBody (Body _ stms res) =-  Body () <$> fuseInStms stms <*> pure res--fuseInExp :: Exp -> FusionGM Exp---- Handle loop specially because we need to bind the types of the--- merge variables.-fuseInExp (DoLoop ctx val form loopbody) =-  binding (zip form_idents $ repeat mempty) $-  bindingParams (map fst $ ctx ++ val) $-  DoLoop ctx val form <$> fuseInBody loopbody-  where form_idents = case form of-          WhileLoop{} -> []-          ForLoop i it _ loopvars ->-            Ident i (Prim $ IntType it) :-            map (paramIdent . fst) loopvars--fuseInExp e = mapExpM fuseIn e--fuseIn :: Mapper SOACS SOACS FusionGM-fuseIn = identityMapper {-           mapOnBody = const fuseInBody-         , mapOnOp = mapSOACM identitySOACMapper { mapOnSOACLambda = fuseInLambda }-         }--fuseInLambda :: Lambda -> FusionGM Lambda-fuseInLambda (Lambda params body rtp) = do-  body' <- bindingParams params $ fuseInBody body-  return $ Lambda params body' rtp--replaceSOAC :: Pattern -> StmAux () -> Exp -> FusionGM (Stms SOACS)-replaceSOAC (Pattern _ []) _ _ = return mempty-replaceSOAC pat@(Pattern _ (patElem : _)) aux e = do-  fres  <- asks fusedRes-  let pat_nm = patElemName patElem-      names  = patternIdents pat-  case M.lookup pat_nm (outArr fres) of-    Nothing  ->-      oneStm . Let pat aux <$> fuseInExp e-    Just knm ->-      case M.lookup knm (kernels fres) of-        Nothing  -> throwError $ Error-                                   ("In Fusion.hs, replaceSOAC, outArr in ker_name "-                                    ++"which is not in Res: "++pretty (unKernName knm))-        Just ker -> do-          when (null $ fusedVars ker) $-            throwError $ Error-            ("In Fusion.hs, replaceSOAC, unfused kernel "-             ++"still in result: "++pretty names)-          insertKerSOAC aux (outNames ker) ker--insertKerSOAC :: StmAux () -> [VName] -> FusedKer -> FusionGM (Stms SOACS)-insertKerSOAC aux names ker = do-  new_soac' <- finaliseSOAC $ fsoac ker-  runBinder_ $ do-    f_soac <- SOAC.toSOAC new_soac'-    -- The fused kernel may consume more than the original SOACs (see-    -- issue #224).  We insert copy expressions to fix it.-    f_soac' <- copyNewlyConsumed (fusedConsumed ker) $ addOpAliases f_soac-    validents <- zipWithM newIdent (map baseString names) $ SOAC.typeOf new_soac'-    auxing (kerAux ker <> aux) $ letBind (basicPattern [] validents) $ Op f_soac'-    transformOutput (outputTransform ker) names validents---- | Perform simplification and fusion inside the lambda(s) of a SOAC.-finaliseSOAC :: SOAC.SOAC SOACS -> FusionGM (SOAC.SOAC SOACS)-finaliseSOAC new_soac =-  case new_soac of-    SOAC.Screma w (ScremaForm scans reds map_lam) arrs -> do-      scans' <- forM scans $ \(Scan scan_lam scan_nes) -> do-        scan_lam' <- simplifyAndFuseInLambda scan_lam-        return $ Scan scan_lam' scan_nes--      reds' <- forM reds $ \(Reduce comm red_lam red_nes) -> do-        red_lam' <- simplifyAndFuseInLambda red_lam-        return $ Reduce comm red_lam' red_nes--      map_lam' <- simplifyAndFuseInLambda map_lam--      return $ SOAC.Screma w (ScremaForm scans' reds' map_lam') arrs-    SOAC.Scatter w lam inps dests -> do-      lam' <- simplifyAndFuseInLambda lam-      return $ SOAC.Scatter w lam' inps dests-    SOAC.Hist w ops lam arrs -> do-      lam' <- simplifyAndFuseInLambda lam-      return $ SOAC.Hist w ops lam' arrs-    SOAC.Stream w form lam inps -> do-      lam' <- simplifyAndFuseInLambda lam-      return $ SOAC.Stream w form lam' inps--simplifyAndFuseInLambda :: Lambda -> FusionGM Lambda-simplifyAndFuseInLambda lam = do-  lam' <- simplifyLambda lam-  (_, nfres) <- fusionGatherLam (mempty, mkFreshFusionRes) lam'-  let nfres' =  cleanFusionResult nfres-  bindRes nfres' $ fuseInLambda lam'--copyNewlyConsumed :: Names-                  -> Futhark.SOAC (Aliases.Aliases SOACS)-                  -> Binder SOACS (Futhark.SOAC SOACS)-copyNewlyConsumed was_consumed soac =-  case soac of-    Futhark.Screma w (Futhark.ScremaForm scans reds map_lam) arrs -> do-      -- Copy any arrays that are consumed now, but were not in the-      -- constituents.-      arrs' <- mapM copyConsumedArr arrs-      -- Any consumed free variables will have to be copied inside the-      -- lambda, and we have to substitute the name of the copy for-      -- the original.-      map_lam' <- copyFreeInLambda map_lam--      let scans' = map (\scan -> scan { scanLambda =-                                          Aliases.removeLambdaAliases-                                          (scanLambda scan)})-                  scans--      let reds' = map (\red -> red { redLambda =-                                       Aliases.removeLambdaAliases-                                       (redLambda red)})-                  reds--      return $ Futhark.Screma w (Futhark.ScremaForm scans' reds' map_lam') arrs'--    _ -> return $ removeOpAliases soac-  where consumed = consumedInOp soac-        newly_consumed = consumed `namesSubtract` was_consumed--        copyConsumedArr a-          | a `nameIn` newly_consumed =-            letExp (baseString a <> "_copy") $ BasicOp $ Copy a-          | otherwise = return a--        copyFreeInLambda lam = do-          let free_consumed = consumedByLambda lam `namesSubtract`-                namesFromList (map paramName $ lambdaParams lam)-          (bnds, subst) <--            foldM copyFree (mempty, mempty) $ namesToList free_consumed-          let lam' = Aliases.removeLambdaAliases lam-          return $ if null bnds-                   then lam'-                   else lam' { lambdaBody =-                                 insertStms bnds $-                                 substituteNames subst $ lambdaBody lam'-                             }--        copyFree (bnds, subst) v = do-          v_copy <- newVName $ baseString v <> "_copy"-          copy <- mkLetNamesM [v_copy] $ BasicOp $ Copy v-          return (oneStm copy<>bnds, M.insert v v_copy subst)-------------------------------------------------------------------------------------------------------------- HELPERS------------------------------------------------------------------------------------------------------------ | Get a new fusion result, i.e., for when entering a new scope,---   e.g., a new lambda or a new loop.-mkFreshFusionRes :: FusedRes-mkFreshFusionRes =-    FusedRes { rsucc     = False,   outArr = M.empty, inpArr  = M.empty,-               infusible = mempty, kernels = M.empty }--mergeFusionRes :: FusedRes -> FusedRes -> FusionGM FusedRes-mergeFusionRes res1 res2 = do-    let ufus_mres = infusible res1 <> infusible res2-    inp_both <- expandSoacInpArr $ M.keys $ inpArr res1 `M.intersection` inpArr res2-    let m_unfus = ufus_mres <> mconcat (map oneName inp_both)-    return $ FusedRes  (rsucc     res1       ||      rsucc     res2)-                       (outArr    res1    `M.union`  outArr    res2)-                       (M.unionWith S.union (inpArr res1) (inpArr res2) )-                       m_unfus-                       (kernels   res1    `M.union`  kernels   res2)----- | The expression arguments are supposed to be array-type exps.---   Returns a tuple, in which the arrays that are vars are in the---   first element of the tuple, and the one which are indexed or---   transposes (or otherwise transformed) should be in the second.------   E.g., for expression `mapT(f, a, b[i])', the result should be---   `([a],[b])'-getIdentArr :: [SOAC.Input] -> ([VName], [VName])-getIdentArr = foldl comb ([],[])-  where comb (vs,os) (SOAC.Input ts idd _)-          | SOAC.nullTransforms ts = (idd:vs, os)-        comb (vs, os) inp =-          (vs, SOAC.inputArray inp : os)--cleanFusionResult :: FusedRes -> FusedRes-cleanFusionResult fres =-    let newks = M.filter (not . null . fusedVars)      (kernels fres)-        newoa = M.filter (`M.member` newks)            (outArr  fres)-        newia = M.map    (S.filter (`M.member` newks)) (inpArr fres)-    in fres { outArr = newoa, inpArr = newia, kernels = newks }-------------------- Errors --------------------errorIllegal :: String -> FusionGM FusedRes-errorIllegal soac_name =-    throwError $ Error-                  ("In Fusion.hs, soac "++soac_name++" appears illegally in pgm!")+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-}++-- | Perform horizontal and vertical fusion of SOACs.  See the paper+-- /A T2 Graph-Reduction Approach To Fusion/ for the basic idea (some+-- extensions discussed in /Design and GPGPU Performance of Futhark’s+-- Redomap Construct/).+module Futhark.Optimise.Fusion (fuseSOACs) where++import Control.Monad.Except+import Control.Monad.Reader+import Control.Monad.State+import qualified Data.List as L+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Data.Set as S+import qualified Futhark.Analysis.Alias as Alias+import qualified Futhark.Analysis.HORep.SOAC as SOAC+import Futhark.Construct+import qualified Futhark.IR.Aliases as Aliases+import Futhark.IR.Prop.Aliases+import Futhark.IR.SOACS hiding (SOAC (..))+import qualified Futhark.IR.SOACS as Futhark+import Futhark.IR.SOACS.Simplify+import Futhark.Optimise.Fusion.LoopKernel+import Futhark.Pass+import Futhark.Transform.Rename+import Futhark.Transform.Substitute+import Futhark.Util (maxinum)++data VarEntry+  = IsArray VName (NameInfo SOACS) Names SOAC.Input+  | IsNotArray (NameInfo SOACS)++varEntryType :: VarEntry -> NameInfo SOACS+varEntryType (IsArray _ dec _ _) =+  dec+varEntryType (IsNotArray dec) =+  dec++varEntryAliases :: VarEntry -> Names+varEntryAliases (IsArray _ _ x _) = x+varEntryAliases _ = mempty++data FusionGEnv = FusionGEnv+  { -- | Mapping from variable name to its entire family.+    soacs :: M.Map VName [VName],+    varsInScope :: M.Map VName VarEntry,+    fusedRes :: FusedRes+  }++lookupArr :: VName -> FusionGEnv -> Maybe SOAC.Input+lookupArr v env = asArray =<< M.lookup v (varsInScope env)+  where+    asArray (IsArray _ _ _ input) = Just input+    asArray IsNotArray {} = Nothing++newtype Error = Error String++instance Show Error where+  show (Error msg) = "Fusion error:\n" ++ msg++newtype FusionGM a = FusionGM (ExceptT Error (StateT VNameSource (Reader FusionGEnv)) a)+  deriving+    ( Monad,+      Applicative,+      Functor,+      MonadError Error,+      MonadState VNameSource,+      MonadReader FusionGEnv+    )++instance MonadFreshNames FusionGM where+  getNameSource = get+  putNameSource = put++instance HasScope SOACS FusionGM where+  askScope = asks $ toScope . varsInScope+    where+      toScope = M.map varEntryType++------------------------------------------------------------------------+--- Monadic Helpers: bind/new/runFusionGatherM, etc+------------------------------------------------------------------------++-- | Binds an array name to the set of used-array vars+bindVar :: FusionGEnv -> (Ident, Names) -> FusionGEnv+bindVar env (Ident name t, aliases) =+  env {varsInScope = M.insert name entry $ varsInScope env}+  where+    entry = case t of+      Array {} -> IsArray name (LetName t) aliases' $ SOAC.identInput $ Ident name t+      _ -> IsNotArray $ LetName t+    expand = maybe mempty varEntryAliases . flip M.lookup (varsInScope env)+    aliases' = aliases <> mconcat (map expand $ namesToList aliases)++bindVars :: FusionGEnv -> [(Ident, Names)] -> FusionGEnv+bindVars = foldl bindVar++binding :: [(Ident, Names)] -> FusionGM a -> FusionGM a+binding vs = local (`bindVars` vs)++gatherStmPattern :: Pattern -> Exp -> FusionGM FusedRes -> FusionGM FusedRes+gatherStmPattern pat e = binding $ zip idents aliases+  where+    idents = patternIdents pat+    aliases =+      replicate (length (patternContextNames pat)) mempty+        ++ expAliases (Alias.analyseExp mempty e)++bindingPat :: Pattern -> FusionGM a -> FusionGM a+bindingPat = binding . (`zip` repeat mempty) . patternIdents++bindingParams :: Typed t => [Param t] -> FusionGM a -> FusionGM a+bindingParams = binding . (`zip` repeat mempty) . map paramIdent++-- | Binds an array name to the set of soac-produced vars+bindingFamilyVar :: [VName] -> FusionGEnv -> Ident -> FusionGEnv+bindingFamilyVar faml env (Ident nm t) =+  env+    { soacs = M.insert nm faml $ soacs env,+      varsInScope =+        M.insert+          nm+          ( IsArray nm (LetName t) mempty $+              SOAC.identInput $ Ident nm t+          )+          $ varsInScope env+    }++varAliases :: VName -> FusionGM Names+varAliases v =+  asks $+    (oneName v <>) . maybe mempty varEntryAliases+      . M.lookup v+      . varsInScope++varsAliases :: Names -> FusionGM Names+varsAliases = fmap mconcat . mapM varAliases . namesToList++checkForUpdates :: FusedRes -> Exp -> FusionGM FusedRes+checkForUpdates res (BasicOp (Update src is _)) = do+  res' <-+    foldM addVarToInfusible res $+      src : namesToList (mconcat $ map freeIn is)+  aliases <- varAliases src+  let inspectKer k = k {inplace = aliases <> inplace k}+  return res' {kernels = M.map inspectKer $ kernels res'}+checkForUpdates res _ = return res++-- | Updates the environment: (i) the @soacs@ (map) by binding each pattern+--   element identifier to all pattern elements (identifiers) and (ii) the+--   variables in scope (map) by inserting each (pattern-array) name.+--   Finally, if the binding is an in-place update, then the @inplace@ field+--   of each (result) kernel is updated with the new in-place updates.+bindingFamily :: Pattern -> FusionGM FusedRes -> FusionGM FusedRes+bindingFamily pat = local bind+  where+    idents = patternIdents pat+    family = patternNames pat+    bind env = foldl (bindingFamilyVar family) env idents++bindingTransform :: PatElem -> VName -> SOAC.ArrayTransform -> FusionGM a -> FusionGM a+bindingTransform pe srcname trns = local $ \env ->+  case M.lookup srcname $ varsInScope env of+    Just (IsArray src' _ aliases input) ->+      env+        { varsInScope =+            M.insert+              vname+              ( IsArray src' (LetName dec) (oneName srcname <> aliases) $+                  trns `SOAC.addTransform` input+              )+              $ varsInScope env+        }+    _ -> bindVar env (patElemIdent pe, oneName vname)+  where+    vname = patElemName pe+    dec = patElemDec pe++-- | Binds the fusion result to the environment.+bindRes :: FusedRes -> FusionGM a -> FusionGM a+bindRes rrr = local (\x -> x {fusedRes = rrr})++-- | The fusion transformation runs in this monad.  The mutable+-- state refers to the fresh-names engine.+-- The reader hides the vtable that associates ... to ... (fill in, please).+-- The 'Either' monad is used for error handling.+runFusionGatherM ::+  MonadFreshNames m =>+  FusionGM a ->+  FusionGEnv ->+  m (Either Error a)+runFusionGatherM (FusionGM a) env =+  modifyNameSource $ \src -> runReader (runStateT (runExceptT a) src) env++------------------------------------------------------------------------+--- Fusion Entry Points: gather the to-be-fused kernels@pgm level    ---+---    and fuse them in a second pass!                               ---+------------------------------------------------------------------------++-- | The pass definition.+fuseSOACs :: Pass SOACS SOACS+fuseSOACs =+  Pass+    { passName = "Fuse SOACs",+      passDescription = "Perform higher-order optimisation, i.e., fusion.",+      passFunction = \prog ->+        simplifySOACS =<< renameProg+          =<< intraproceduralTransformationWithConsts+            (fuseConsts (freeIn (progFuns prog)))+            fuseFun+            prog+    }++fuseConsts :: Names -> Stms SOACS -> PassM (Stms SOACS)+fuseConsts used_consts consts =+  fuseStms mempty consts $ map Var $ namesToList used_consts++fuseFun :: Stms SOACS -> FunDef SOACS -> PassM (FunDef SOACS)+fuseFun consts fun = do+  stms <-+    fuseStms+      (scopeOf consts <> scopeOfFParams (funDefParams fun))+      (bodyStms $ funDefBody fun)+      (bodyResult $ funDefBody fun)+  let body = (funDefBody fun) {bodyStms = stms}+  return fun {funDefBody = body}++fuseStms :: Scope SOACS -> Stms SOACS -> Result -> PassM (Stms SOACS)+fuseStms scope stms res = do+  let env =+        FusionGEnv+          { soacs = M.empty,+            varsInScope = mempty,+            fusedRes = mempty+          }+  k <-+    cleanFusionResult+      <$> liftEitherM+        ( runFusionGatherM+            (binding scope' $ fusionGatherStms mempty (stmsToList stms) res)+            env+        )+  if not $ rsucc k+    then return stms+    else liftEitherM $ runFusionGatherM (binding scope' $ bindRes k $ fuseInStms stms) env+  where+    scope' = map toBind $ M.toList scope+    toBind (k, t) = (Ident k $ typeOf t, mempty)++---------------------------------------------------+---------------------------------------------------+---- RESULT's Data Structure+---------------------------------------------------+---------------------------------------------------++-- | A type used for (hopefully) uniquely referring a producer SOAC.+-- The uniquely identifying value is the name of the first array+-- returned from the SOAC.+newtype KernName = KernName {unKernName :: VName}+  deriving (Eq, Ord, Show)++data FusedRes = FusedRes+  { -- | Whether we have fused something anywhere.+    rsucc :: Bool,+    -- | Associates an array to the name of the+    -- SOAC kernel that has produced it.+    outArr :: M.Map VName KernName,+    -- | Associates an array to the names of the+    -- SOAC kernels that uses it. These sets include+    -- only the SOAC input arrays used as full variables, i.e., no `a[i]'.+    inpArr :: M.Map VName (S.Set KernName),+    -- | the (names of) arrays that are not fusible, i.e.,+    --+    --   1. they are either used other than input to SOAC kernels, or+    --+    --   2. are used as input to at least two different kernels that+    --      are not located on disjoint control-flow branches, or+    --+    --   3. are used in the lambda expression of SOACs+    infusible :: Names,+    -- | The map recording the uses+    kernels :: M.Map KernName FusedKer+  }++instance Semigroup FusedRes where+  res1 <> res2 =+    FusedRes+      (rsucc res1 || rsucc res2)+      (outArr res1 `M.union` outArr res2)+      (M.unionWith S.union (inpArr res1) (inpArr res2))+      (infusible res1 <> infusible res2)+      (kernels res1 `M.union` kernels res2)++instance Monoid FusedRes where+  mempty =+    FusedRes+      { rsucc = False,+        outArr = M.empty,+        inpArr = M.empty,+        infusible = mempty,+        kernels = M.empty+      }++isInpArrInResModKers :: FusedRes -> S.Set KernName -> VName -> Bool+isInpArrInResModKers ress kers nm =+  case M.lookup nm (inpArr ress) of+    Nothing -> False+    Just s -> not $ S.null $ s `S.difference` kers++getKersWithInpArrs :: FusedRes -> [VName] -> S.Set KernName+getKersWithInpArrs ress =+  S.unions . mapMaybe (`M.lookup` inpArr ress)++-- | extend the set of names to include all the names+--     produced via SOACs (by querring the vtable's soac)+expandSoacInpArr :: [VName] -> FusionGM [VName]+expandSoacInpArr =+  foldM+    ( \y nm -> do+        bnd <- asks $ M.lookup nm . soacs+        case bnd of+          Nothing -> return (y ++ [nm])+          Just nns -> return (y ++ nns)+    )+    []++----------------------------------------------------------------------+----------------------------------------------------------------------++soacInputs :: SOAC -> FusionGM ([VName], [VName])+soacInputs soac = do+  let (inp_idds, other_idds) = getIdentArr $ SOAC.inputs soac+      (inp_nms0, other_nms0) = (inp_idds, other_idds)+  inp_nms <- expandSoacInpArr inp_nms0+  other_nms <- expandSoacInpArr other_nms0+  return (inp_nms, other_nms)++addNewKerWithInfusible :: FusedRes -> ([Ident], StmAux (), SOAC, Names) -> Names -> FusionGM FusedRes+addNewKerWithInfusible res (idd, aux, soac, consumed) ufs = do+  nm_ker <- KernName <$> newVName "ker"+  scope <- askScope+  let out_nms = map identName idd+      new_ker = newKernel aux soac consumed out_nms scope+      comb = M.unionWith S.union+      os' =+        M.fromList [(arr, nm_ker) | arr <- out_nms]+          `M.union` outArr res+      is' =+        M.fromList+          [ (arr, S.singleton nm_ker)+            | arr <- map SOAC.inputArray $ SOAC.inputs soac+          ]+          `comb` inpArr res+  return $+    FusedRes+      (rsucc res)+      os'+      is'+      ufs+      (M.insert nm_ker new_ker (kernels res))++lookupInput :: VName -> FusionGM (Maybe SOAC.Input)+lookupInput name = asks $ lookupArr name++inlineSOACInput :: SOAC.Input -> FusionGM SOAC.Input+inlineSOACInput (SOAC.Input ts v t) = do+  maybe_inp <- lookupInput v+  case maybe_inp of+    Nothing ->+      return $ SOAC.Input ts v t+    Just (SOAC.Input ts2 v2 t2) ->+      return $ SOAC.Input (ts2 <> ts) v2 t2++inlineSOACInputs :: SOAC -> FusionGM SOAC+inlineSOACInputs soac = do+  inputs' <- mapM inlineSOACInput $ SOAC.inputs soac+  return $ inputs' `SOAC.setInputs` soac++-- | Attempts to fuse between SOACs. Input:+--   @rem_bnds@ are the bindings remaining in the current body after @orig_soac@.+--   @lam_used_nms@ the infusible names+--   @res@ the fusion result (before processing the current soac)+--   @orig_soac@ and @out_idds@ the current SOAC and its binding pattern+--   @consumed@ is the set of names consumed by the SOAC.+--   Output: a new Fusion Result (after processing the current SOAC binding)+greedyFuse ::+  [Stm] ->+  Names ->+  FusedRes ->+  (Pattern, StmAux (), SOAC, Names) ->+  FusionGM FusedRes+greedyFuse rem_bnds lam_used_nms res (out_idds, aux, orig_soac, consumed) = do+  soac <- inlineSOACInputs orig_soac+  (inp_nms, other_nms) <- soacInputs soac+  -- Assumption: the free vars in lambda are already in @infusible res@.+  let out_nms = patternNames out_idds+      isInfusible = (`nameIn` infusible res)+      is_screma = case orig_soac of+        SOAC.Screma _ form _ ->+          (isJust (isRedomapSOAC form) || isJust (isScanomapSOAC form))+            && not (isJust (isReduceSOAC form) || isJust (isScanSOAC form))+        _ -> False+  --+  -- Conditions for fusion:+  -- If current soac is a replicate OR (current soac a redomap/scanomap AND+  --    (i) none of @out_idds@ belongs to the infusible set)+  -- THEN try applying producer-consumer fusion+  -- ELSE try applying horizontal        fusion+  -- (without duplicating computation in both cases)++  (ok_kers_compat, fused_kers, fused_nms, old_kers, oldker_nms) <-+    if is_screma || any isInfusible out_nms+      then horizontGreedyFuse rem_bnds res (out_idds, aux, soac, consumed)+      else prodconsGreedyFuse res (out_idds, aux, soac, consumed)+  --+  -- (ii) check whether fusing @soac@ will violate any in-place update+  --      restriction, e.g., would move an input array past its in-place update.+  let all_used_names = namesToList $ mconcat [lam_used_nms, namesFromList inp_nms, namesFromList other_nms]+      has_inplace ker = any (`nameIn` inplace ker) all_used_names+      ok_inplace = not $ any has_inplace old_kers+  --+  -- (iii)  there are some kernels that use some of `out_idds' as inputs+  -- (iv)   and producer-consumer or horizontal fusion succeeds with those.+  let fusible_ker = not (null old_kers) && ok_inplace && ok_kers_compat+  --+  -- Start constructing the fusion's result:+  --  (i) inparr ids other than vars will be added to infusible list,+  -- (ii) will also become part of the infusible set the inparr vars+  --         that also appear as inparr of another kernel,+  --         BUT which said kernel is not the one we are fusing with (now)!+  let mod_kerS = if fusible_ker then S.fromList oldker_nms else mempty+  let used_inps = filter (isInpArrInResModKers res mod_kerS) inp_nms+  let ufs =+        mconcat+          [ infusible res,+            namesFromList used_inps,+            namesFromList other_nms+              `namesSubtract` namesFromList (map SOAC.inputArray $ SOAC.inputs soac)+          ]+  let comb = M.unionWith S.union++  if not fusible_ker+    then addNewKerWithInfusible res (patternIdents out_idds, aux, soac, consumed) ufs+    else do+      -- Need to suitably update `inpArr':+      --   (i) first remove the inpArr bindings of the old kernel+      let inpArr' =+            foldl+              ( \inpa (kold, knm) ->+                  S.foldl'+                    ( \inpp nm ->+                        case M.lookup nm inpp of+                          Nothing -> inpp+                          Just s ->+                            let new_set = S.delete knm s+                             in if S.null new_set+                                  then M.delete nm inpp+                                  else M.insert nm new_set inpp+                    )+                    inpa+                    $ arrInputs kold+              )+              (inpArr res)+              (zip old_kers oldker_nms)+      --  (ii) then add the inpArr bindings of the new kernel+      let fused_ker_nms = zip fused_nms fused_kers+          inpArr'' =+            foldl+              ( \inpa' (knm, knew) ->+                  M.fromList+                    [ (k, S.singleton knm)+                      | k <- S.toList $ arrInputs knew+                    ]+                    `comb` inpa'+              )+              inpArr'+              fused_ker_nms+      -- Update the kernels map (why not delete the ones that have been fused?)+      let kernels' = M.fromList fused_ker_nms `M.union` kernels res+      -- nothing to do for `outArr' (since we have not added a new kernel)+      -- DO IMPROVEMENT: attempt to fuse the resulting kernel AGAIN until it fails,+      --                 but make sure NOT to add a new kernel!+      return $ FusedRes True (outArr res) inpArr'' ufs kernels'++prodconsGreedyFuse ::+  FusedRes ->+  (Pattern, StmAux (), SOAC, Names) ->+  FusionGM (Bool, [FusedKer], [KernName], [FusedKer], [KernName])+prodconsGreedyFuse res (out_idds, aux, soac, consumed) = do+  let out_nms = patternNames out_idds -- Extract VNames from output patterns+      to_fuse_knmSet = getKersWithInpArrs res out_nms -- Find kernels which consume outputs+      to_fuse_knms = S.toList to_fuse_knmSet+      lookup_kern k = case M.lookup k (kernels res) of+        Nothing ->+          throwError $+            Error+              ( "In Fusion.hs, greedyFuse, comp of to_fuse_kers: "+                  ++ "kernel name not found in kernels field!"+              )+        Just ker -> return ker+  to_fuse_kers <- mapM lookup_kern to_fuse_knms -- Get all consumer kernels+  -- try producer-consumer fusion+  (ok_kers_compat, fused_kers) <- do+    kers <-+      forM to_fuse_kers $+        attemptFusion mempty (patternNames out_idds) soac consumed+    case sequence kers of+      Nothing -> return (False, [])+      Just kers' -> return (True, map certifyKer kers')+  return (ok_kers_compat, fused_kers, to_fuse_knms, to_fuse_kers, to_fuse_knms)+  where+    certifyKer k = k {kerAux = kerAux k <> aux}++horizontGreedyFuse ::+  [Stm] ->+  FusedRes ->+  (Pattern, StmAux (), SOAC, Names) ->+  FusionGM (Bool, [FusedKer], [KernName], [FusedKer], [KernName])+horizontGreedyFuse rem_bnds res (out_idds, aux, soac, consumed) = do+  (inp_nms, _) <- soacInputs soac+  let out_nms = patternNames out_idds+      infusible_nms = namesFromList $ filter (`nameIn` infusible res) out_nms+      out_arr_nms = case soac of+        -- the accumulator result cannot be fused!+        SOAC.Screma _ (ScremaForm scans reds _) _ ->+          drop (scanResults scans + redResults reds) out_nms+        SOAC.Stream _ frm _ _ -> drop (length $ getStreamAccums frm) out_nms+        _ -> out_nms+      to_fuse_knms1 = S.toList $ getKersWithInpArrs res (out_arr_nms ++ inp_nms)+      to_fuse_knms2 = getKersWithSameInpSize (SOAC.width soac) res+      to_fuse_knms = S.toList $ S.fromList $ to_fuse_knms1 ++ to_fuse_knms2+      lookupKernel k = case M.lookup k (kernels res) of+        Nothing ->+          throwError $+            Error+              ( "In Fusion.hs, greedyFuse, comp of to_fuse_kers: "+                  ++ "kernel name not found in kernels field!"+              )+        Just ker -> return ker++  -- For each kernel get the index in the bindings where the kernel is+  -- located and sort based on the index so that partial fusion may+  -- succeed.  We use the last position where one of the kernel+  -- outputs occur.+  let bnd_nms = map (patternNames . stmPattern) rem_bnds+  kernminds <- forM to_fuse_knms $ \ker_nm -> do+    ker <- lookupKernel ker_nm+    case mapMaybe (\out_nm -> L.findIndex (elem out_nm) bnd_nms) (outNames ker) of+      [] -> return Nothing+      is -> return $ Just (ker, ker_nm, maxinum is)++  scope <- askScope+  let kernminds' = L.sortBy (\(_, _, i1) (_, _, i2) -> compare i1 i2) $ catMaybes kernminds+      soac_kernel = newKernel aux soac consumed out_nms scope++  -- now try to fuse kernels one by one (in a fold); @ok_ind@ is the index of the+  -- kernel until which fusion succeded, and @fused_ker@ is the resulting kernel.+  use_scope <- (<> scopeOf rem_bnds) <$> askScope+  (_, ok_ind, _, fused_ker, _) <-+    foldM+      ( \(cur_ok, n, prev_ind, cur_ker, ufus_nms) (ker, _ker_nm, bnd_ind) -> do+          -- check that we still try fusion and that the intermediate+          -- bindings do not use the results of cur_ker+          let curker_outnms = outNames cur_ker+              curker_outset = namesFromList curker_outnms+              new_ufus_nms = namesFromList $ outNames ker ++ namesToList ufus_nms+              -- disable horizontal fusion in the case when an output array of+              -- producer SOAC is a non-trivially transformed input of the consumer+              out_transf_ok =+                let ker_inp = SOAC.inputs $ fsoac ker+                    unfuse1 =+                      namesFromList (map SOAC.inputArray ker_inp)+                        `namesSubtract` namesFromList (mapMaybe SOAC.isVarInput ker_inp)+                    unfuse2 = namesIntersection curker_outset ufus_nms+                 in not $ unfuse1 `namesIntersect` unfuse2+              -- Disable horizontal fusion if consumer has any+              -- output transforms.+              cons_no_out_transf = SOAC.nullTransforms $ outputTransform ker++          consumer_ok <- do+            let consumer_bnd = rem_bnds !! bnd_ind+            maybesoac <- runReaderT (SOAC.fromExp $ stmExp consumer_bnd) use_scope+            case maybesoac of+              -- check that consumer's lambda body does not use+              -- directly the produced arrays (e.g., see noFusion3.fut).+              Right conssoac ->+                return $+                  not $+                    curker_outset+                      `namesIntersect` freeIn (lambdaBody $ SOAC.lambda conssoac)+              Left _ -> return True++          let interm_bnds_ok =+                cur_ok && consumer_ok && out_transf_ok && cons_no_out_transf+                  && foldl+                    ( \ok bnd ->+                        ok+                          && not (curker_outset `namesIntersect` freeIn (stmExp bnd)) -- hardwired to False after first fail+                          -- (i) check that the in-between bindings do+                          --     not use the result of current kernel OR+                          ||+                          --(ii) that the pattern-binding corresponds to+                          --     the result of the consumer kernel; in the+                          --     latter case it means it corresponds to a+                          --     kernel that has been fused in the consumer,+                          --     hence it should be ignored+                          not+                            ( null $+                                curker_outnms+                                  `L.intersect` patternNames (stmPattern bnd)+                            )+                    )+                    True+                    (drop (prev_ind + 1) $ take bnd_ind rem_bnds)+          if not interm_bnds_ok+            then return (False, n, bnd_ind, cur_ker, mempty)+            else do+              new_ker <-+                attemptFusion+                  ufus_nms+                  (outNames cur_ker)+                  (fsoac cur_ker)+                  (fusedConsumed cur_ker)+                  ker+              case new_ker of+                Nothing -> return (False, n, bnd_ind, cur_ker, mempty)+                Just krn ->+                  let krn' = krn {kerAux = aux <> kerAux krn}+                   in return (True, n + 1, bnd_ind, krn', new_ufus_nms)+      )+      (True, 0, 0, soac_kernel, infusible_nms)+      kernminds'++  -- Find the kernels we have fused into and the name of the last such+  -- kernel (if any).+  let (to_fuse_kers', to_fuse_knms', _) = unzip3 $ take ok_ind kernminds'+      new_kernms = drop (ok_ind -1) to_fuse_knms'++  return (ok_ind > 0, [fused_ker], new_kernms, to_fuse_kers', to_fuse_knms')+  where+    getKersWithSameInpSize :: SubExp -> FusedRes -> [KernName]+    getKersWithSameInpSize sz ress =+      map fst $ filter (\(_, ker) -> sz == SOAC.width (fsoac ker)) $ M.toList $ kernels ress++------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------------+--- Fusion Gather for EXPRESSIONS and BODIES,                        ---+--- i.e., where work is being done:                                  ---+---    i) bottom-up AbSyn traversal (backward analysis)              ---+---   ii) soacs are fused greedily iff does not duplicate computation---+--- E.g., (y1, y2, y3) = mapT(f, x1, x2[i])                          ---+---       (z1, z2)     = mapT(g1, y1, y2)                            ---+---       (q1, q2)     = mapT(g2, y3, z1, a, y3)                     ---+---       res          = reduce(op, ne, q1, q2, z2, y1, y3)          ---+--- can be fused if y1,y2,y3, z1,z2, q1,q2 are not used elsewhere:   ---+---       res = redomap(op, \(x1,x2i,a)->                            ---+---                             let (y1,y2,y3) = f (x1, x2i)       in---+---                             let (z1,z2)    = g1(y1, y2)        in---+---                             let (q1,q2)    = g2(y3, z1, a, y3) in---+---                             (q1, q2, z2, y1, y3)                 ---+---                     x1, x2[i], a)                                ---+------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------------++fusionGatherBody :: FusedRes -> Body -> FusionGM FusedRes+fusionGatherBody fres (Body _ stms res) =+  fusionGatherStms fres (stmsToList stms) res++fusionGatherStms :: FusedRes -> [Stm] -> Result -> FusionGM FusedRes+-- Some forms of do-loops can profitably be considered streamSeqs.  We+-- are careful to ensure that the generated nested loop cannot itself+-- be considered a stream, to avoid infinite recursion.+fusionGatherStms+  fres+  ( Let+      (Pattern [] pes)+      bndtp+      (DoLoop [] merge (ForLoop i it w loop_vars) body)+      : bnds+    )+  res+    | not $ null loop_vars = do+      let (merge_params, merge_init) = unzip merge+          (loop_params, loop_arrs) = unzip loop_vars+      chunk_size <- newVName "chunk_size"+      offset <- newVName "offset"+      let chunk_param = Param chunk_size $ Prim int64+          offset_param = Param offset $ Prim $ IntType it++      acc_params <- forM merge_params $ \p ->+        Param <$> newVName (baseString (paramName p) ++ "_outer")+          <*> pure (paramType p)++      chunked_params <- forM loop_vars $ \(p, arr) ->+        Param <$> newVName (baseString arr ++ "_chunk")+          <*> pure (paramType p `arrayOfRow` Futhark.Var chunk_size)++      let lam_params = chunk_param : acc_params ++ [offset_param] ++ chunked_params++      lam_body <- runBodyBinder $+        localScope (scopeOfLParams lam_params) $ do+          let merge' = zip merge_params $ map (Futhark.Var . paramName) acc_params+          j <- newVName "j"+          loop_body <- runBodyBinder $ do+            forM_ (zip loop_params chunked_params) $ \(p, a_p) ->+              letBindNames [paramName p] $+                BasicOp $+                  Index (paramName a_p) $+                    fullSlice (paramType a_p) [DimFix $ Futhark.Var j]+            letBindNames [i] $ BasicOp $ BinOp (Add it OverflowUndef) (Futhark.Var offset) (Futhark.Var j)+            return body+          eBody+            [ pure $+                DoLoop [] merge' (ForLoop j it (Futhark.Var chunk_size) []) loop_body,+              pure $+                BasicOp $ BinOp (Add Int64 OverflowUndef) (Futhark.Var offset) (Futhark.Var chunk_size)+            ]+      let lam =+            Lambda+              { lambdaParams = lam_params,+                lambdaBody = lam_body,+                lambdaReturnType = map paramType $ acc_params ++ [offset_param]+              }+          stream = Futhark.Stream w (Sequential $ merge_init ++ [intConst it 0]) lam loop_arrs++      -- It is important that the (discarded) final-offset is not the+      -- first element in the pattern, as we use the first element to+      -- identify the SOAC in the second phase of fusion.+      discard <- newVName "discard"+      let discard_pe = PatElem discard $ Prim int64++      fusionGatherStms+        fres+        (Let (Pattern [] (pes <> [discard_pe])) bndtp (Op stream) : bnds)+        res+fusionGatherStms fres (bnd@(Let pat _ e) : bnds) res = do+  maybesoac <- SOAC.fromExp e+  case maybesoac of+    Right soac@(SOAC.Scatter _len lam _ivs _as) -> do+      -- We put the variables produced by Scatter into the infusible+      -- set to force horizontal fusion.  It is not possible to+      -- producer/consumer-fuse Scatter anyway.+      fres' <- addNamesToInfusible fres $ namesFromList $ patternNames pat+      mapLike fres' soac lam+    Right soac@(SOAC.Hist _ _ lam _) -> do+      -- We put the variables produced by Hist into the infusible+      -- set to force horizontal fusion.  It is not possible to+      -- producer/consumer-fuse Hist anyway.+      fres' <- addNamesToInfusible fres $ namesFromList $ patternNames pat+      mapLike fres' soac lam+    Right soac@(SOAC.Screma _ (ScremaForm scans reds map_lam) _) ->+      reduceLike soac (map scanLambda scans <> map redLambda reds <> [map_lam]) $+        concatMap scanNeutral scans <> concatMap redNeutral reds+    Right soac@(SOAC.Stream _ form lam _) -> do+      -- a redomap does not neccessarily start a new kernel, e.g.,+      -- @let a= reduce(+,0,A) in ... bnds ... in let B = map(f,A)@+      -- can be fused into a redomap that replaces the @map@, if @a@+      -- and @B@ are defined in the same scope and @bnds@ does not uses @a@.+      -- a redomap always starts a new kernel+      let lambdas = case form of+            Parallel _ _ lout _ -> [lout, lam]+            _ -> [lam]+      reduceLike soac lambdas $ getStreamAccums form+    _+      | [pe] <- patternValueElements pat,+        Just (src, trns) <- SOAC.transformFromExp (stmCerts bnd) e ->+        bindingTransform pe src trns $ fusionGatherStms fres bnds res+      | otherwise -> do+        let pat_vars = map (BasicOp . SubExp . Var) $ patternNames pat+        bres <- gatherStmPattern pat e $ fusionGatherStms fres bnds res+        bres' <- checkForUpdates bres e+        foldM fusionGatherExp bres' (e : pat_vars)+  where+    aux = stmAux bnd+    rem_bnds = bnd : bnds+    consumed = consumedInExp $ Alias.analyseExp mempty e++    reduceLike soac lambdas nes = do+      (used_lam, lres) <- foldM fusionGatherLam (mempty, fres) lambdas+      bres <- bindingFamily pat $ fusionGatherStms lres bnds res+      bres' <- foldM fusionGatherSubExp bres nes+      consumed' <- varsAliases consumed+      greedyFuse rem_bnds used_lam bres' (pat, aux, soac, consumed')++    mapLike fres' soac lambda = do+      bres <- bindingFamily pat $ fusionGatherStms fres' bnds res+      (used_lam, blres) <- fusionGatherLam (mempty, bres) lambda+      consumed' <- varsAliases consumed+      greedyFuse rem_bnds used_lam blres (pat, aux, soac, consumed')+fusionGatherStms fres [] res =+  foldM fusionGatherExp fres $ map (BasicOp . SubExp) res++fusionGatherExp :: FusedRes -> Exp -> FusionGM FusedRes+-----------------------------------------+---- Index/If    ----+-----------------------------------------++fusionGatherExp fres (DoLoop ctx val form loop_body) = do+  fres' <- addNamesToInfusible fres $ freeIn form <> freeIn ctx <> freeIn val+  let form_idents =+        case form of+          ForLoop i it _ loopvars ->+            Ident i (Prim (IntType it)) : map (paramIdent . fst) loopvars+          WhileLoop {} -> []++  new_res <-+    binding+      ( zip (form_idents ++ map (paramIdent . fst) (ctx <> val)) $+          repeat mempty+      )+      $ fusionGatherBody mempty loop_body+  -- make the inpArr infusible, so that they+  -- cannot be fused from outside the loop:+  let (inp_arrs, _) = unzip $ M.toList $ inpArr new_res+  let new_res' = new_res {infusible = infusible new_res <> mconcat (map oneName inp_arrs)}+  -- merge new_res with fres'+  return $ new_res' <> fres'+fusionGatherExp fres (If cond e_then e_else _) = do+  then_res <- fusionGatherBody mempty e_then+  else_res <- fusionGatherBody mempty e_else+  let both_res = then_res <> else_res+  fres' <- fusionGatherSubExp fres cond+  mergeFusionRes fres' both_res++-----------------------------------------------------------------------------------+--- Errors: all SOACs, (because normalization ensures they appear+--- directly in let exp, i.e., let x = e)+-----------------------------------------------------------------------------------++fusionGatherExp _ (Op Futhark.Screma {}) = errorIllegal "screma"+fusionGatherExp _ (Op Futhark.Scatter {}) = errorIllegal "write"+-----------------------------------+---- Generic Traversal         ----+-----------------------------------++fusionGatherExp fres e = addNamesToInfusible fres $ freeIn e++fusionGatherSubExp :: FusedRes -> SubExp -> FusionGM FusedRes+fusionGatherSubExp fres (Var idd) = addVarToInfusible fres idd+fusionGatherSubExp fres _ = return fres++addNamesToInfusible :: FusedRes -> Names -> FusionGM FusedRes+addNamesToInfusible fres = foldM addVarToInfusible fres . namesToList++addVarToInfusible :: FusedRes -> VName -> FusionGM FusedRes+addVarToInfusible fres name = do+  trns <- asks $ lookupArr name+  let name' = case trns of+        Nothing -> name+        Just (SOAC.Input _ orig _) -> orig+  return fres {infusible = oneName name' <> infusible fres}++-- Lambdas create a new scope.  Disallow fusing from outside lambda by+-- adding inp_arrs to the infusible set.+fusionGatherLam :: (Names, FusedRes) -> Lambda -> FusionGM (Names, FusedRes)+fusionGatherLam (u_set, fres) (Lambda idds body _) = do+  new_res <- bindingParams idds $ fusionGatherBody mempty body+  -- make the inpArr infusible, so that they+  -- cannot be fused from outside the lambda:+  let inp_arrs = namesFromList $ M.keys $ inpArr new_res+  let unfus = infusible new_res <> inp_arrs+  bnds <- asks $ M.keys . varsInScope+  let unfus' = unfus `namesIntersection` namesFromList bnds+  -- merge fres with new_res'+  let new_res' = new_res {infusible = unfus'}+  -- merge new_res with fres'+  return (u_set <> unfus', new_res' <> fres)++-------------------------------------------------------------+-------------------------------------------------------------+--- FINALLY, Substitute the kernels in function+-------------------------------------------------------------+-------------------------------------------------------------++fuseInStms :: Stms SOACS -> FusionGM (Stms SOACS)+fuseInStms stms+  | Just (Let pat aux e, stms') <- stmsHead stms = do+    stms'' <- bindingPat pat $ fuseInStms stms'+    soac_bnds <- replaceSOAC pat aux e+    pure $ soac_bnds <> stms''+  | otherwise =+    pure mempty++fuseInBody :: Body -> FusionGM Body+fuseInBody (Body _ stms res) =+  Body () <$> fuseInStms stms <*> pure res++fuseInExp :: Exp -> FusionGM Exp+-- Handle loop specially because we need to bind the types of the+-- merge variables.+fuseInExp (DoLoop ctx val form loopbody) =+  binding (zip form_idents $ repeat mempty) $+    bindingParams (map fst $ ctx ++ val) $+      DoLoop ctx val form <$> fuseInBody loopbody+  where+    form_idents = case form of+      WhileLoop {} -> []+      ForLoop i it _ loopvars ->+        Ident i (Prim $ IntType it) :+        map (paramIdent . fst) loopvars+fuseInExp e = mapExpM fuseIn e++fuseIn :: Mapper SOACS SOACS FusionGM+fuseIn =+  identityMapper+    { mapOnBody = const fuseInBody,+      mapOnOp = mapSOACM identitySOACMapper {mapOnSOACLambda = fuseInLambda}+    }++fuseInLambda :: Lambda -> FusionGM Lambda+fuseInLambda (Lambda params body rtp) = do+  body' <- bindingParams params $ fuseInBody body+  return $ Lambda params body' rtp++replaceSOAC :: Pattern -> StmAux () -> Exp -> FusionGM (Stms SOACS)+replaceSOAC (Pattern _ []) _ _ = return mempty+replaceSOAC pat@(Pattern _ (patElem : _)) aux e = do+  fres <- asks fusedRes+  let pat_nm = patElemName patElem+      names = patternIdents pat+  case M.lookup pat_nm (outArr fres) of+    Nothing ->+      oneStm . Let pat aux <$> fuseInExp e+    Just knm ->+      case M.lookup knm (kernels fres) of+        Nothing ->+          throwError $+            Error+              ( "In Fusion.hs, replaceSOAC, outArr in ker_name "+                  ++ "which is not in Res: "+                  ++ pretty (unKernName knm)+              )+        Just ker -> do+          when (null $ fusedVars ker) $+            throwError $+              Error+                ( "In Fusion.hs, replaceSOAC, unfused kernel "+                    ++ "still in result: "+                    ++ pretty names+                )+          insertKerSOAC aux (outNames ker) ker++insertKerSOAC :: StmAux () -> [VName] -> FusedKer -> FusionGM (Stms SOACS)+insertKerSOAC aux names ker = do+  new_soac' <- finaliseSOAC $ fsoac ker+  runBinder_ $ do+    f_soac <- SOAC.toSOAC new_soac'+    -- The fused kernel may consume more than the original SOACs (see+    -- issue #224).  We insert copy expressions to fix it.+    f_soac' <- copyNewlyConsumed (fusedConsumed ker) $ addOpAliases f_soac+    validents <- zipWithM newIdent (map baseString names) $ SOAC.typeOf new_soac'+    auxing (kerAux ker <> aux) $ letBind (basicPattern [] validents) $ Op f_soac'+    transformOutput (outputTransform ker) names validents++-- | Perform simplification and fusion inside the lambda(s) of a SOAC.+finaliseSOAC :: SOAC.SOAC SOACS -> FusionGM (SOAC.SOAC SOACS)+finaliseSOAC new_soac =+  case new_soac of+    SOAC.Screma w (ScremaForm scans reds map_lam) arrs -> do+      scans' <- forM scans $ \(Scan scan_lam scan_nes) -> do+        scan_lam' <- simplifyAndFuseInLambda scan_lam+        return $ Scan scan_lam' scan_nes++      reds' <- forM reds $ \(Reduce comm red_lam red_nes) -> do+        red_lam' <- simplifyAndFuseInLambda red_lam+        return $ Reduce comm red_lam' red_nes++      map_lam' <- simplifyAndFuseInLambda map_lam++      return $ SOAC.Screma w (ScremaForm scans' reds' map_lam') arrs+    SOAC.Scatter w lam inps dests -> do+      lam' <- simplifyAndFuseInLambda lam+      return $ SOAC.Scatter w lam' inps dests+    SOAC.Hist w ops lam arrs -> do+      lam' <- simplifyAndFuseInLambda lam+      return $ SOAC.Hist w ops lam' arrs+    SOAC.Stream w form lam inps -> do+      lam' <- simplifyAndFuseInLambda lam+      return $ SOAC.Stream w form lam' inps++simplifyAndFuseInLambda :: Lambda -> FusionGM Lambda+simplifyAndFuseInLambda lam = do+  lam' <- simplifyLambda lam+  (_, nfres) <- fusionGatherLam (mempty, mkFreshFusionRes) lam'+  let nfres' = cleanFusionResult nfres+  bindRes nfres' $ fuseInLambda lam'++copyNewlyConsumed ::+  Names ->+  Futhark.SOAC (Aliases.Aliases SOACS) ->+  Binder SOACS (Futhark.SOAC SOACS)+copyNewlyConsumed was_consumed soac =+  case soac of+    Futhark.Screma w (Futhark.ScremaForm scans reds map_lam) arrs -> do+      -- Copy any arrays that are consumed now, but were not in the+      -- constituents.+      arrs' <- mapM copyConsumedArr arrs+      -- Any consumed free variables will have to be copied inside the+      -- lambda, and we have to substitute the name of the copy for+      -- the original.+      map_lam' <- copyFreeInLambda map_lam++      let scans' =+            map+              ( \scan ->+                  scan+                    { scanLambda =+                        Aliases.removeLambdaAliases+                          (scanLambda scan)+                    }+              )+              scans++      let reds' =+            map+              ( \red ->+                  red+                    { redLambda =+                        Aliases.removeLambdaAliases+                          (redLambda red)+                    }+              )+              reds++      return $ Futhark.Screma w (Futhark.ScremaForm scans' reds' map_lam') arrs'+    _ -> return $ removeOpAliases soac+  where+    consumed = consumedInOp soac+    newly_consumed = consumed `namesSubtract` was_consumed++    copyConsumedArr a+      | a `nameIn` newly_consumed =+        letExp (baseString a <> "_copy") $ BasicOp $ Copy a+      | otherwise = return a++    copyFreeInLambda lam = do+      let free_consumed =+            consumedByLambda lam+              `namesSubtract` namesFromList (map paramName $ lambdaParams lam)+      (bnds, subst) <-+        foldM copyFree (mempty, mempty) $ namesToList free_consumed+      let lam' = Aliases.removeLambdaAliases lam+      return $+        if null bnds+          then lam'+          else+            lam'+              { lambdaBody =+                  insertStms bnds $+                    substituteNames subst $ lambdaBody lam'+              }++    copyFree (bnds, subst) v = do+      v_copy <- newVName $ baseString v <> "_copy"+      copy <- mkLetNamesM [v_copy] $ BasicOp $ Copy v+      return (oneStm copy <> bnds, M.insert v v_copy subst)++---------------------------------------------------+---------------------------------------------------+---- HELPERS+---------------------------------------------------+---------------------------------------------------++-- | Get a new fusion result, i.e., for when entering a new scope,+--   e.g., a new lambda or a new loop.+mkFreshFusionRes :: FusedRes+mkFreshFusionRes =+  FusedRes+    { rsucc = False,+      outArr = M.empty,+      inpArr = M.empty,+      infusible = mempty,+      kernels = M.empty+    }++mergeFusionRes :: FusedRes -> FusedRes -> FusionGM FusedRes+mergeFusionRes res1 res2 = do+  let ufus_mres = infusible res1 <> infusible res2+  inp_both <- expandSoacInpArr $ M.keys $ inpArr res1 `M.intersection` inpArr res2+  let m_unfus = ufus_mres <> mconcat (map oneName inp_both)+  return $+    FusedRes+      (rsucc res1 || rsucc res2)+      (outArr res1 `M.union` outArr res2)+      (M.unionWith S.union (inpArr res1) (inpArr res2))+      m_unfus+      (kernels res1 `M.union` kernels res2)++-- | The expression arguments are supposed to be array-type exps.+--   Returns a tuple, in which the arrays that are vars are in the+--   first element of the tuple, and the one which are indexed or+--   transposes (or otherwise transformed) should be in the second.+--+--   E.g., for expression `mapT(f, a, b[i])', the result should be+--   `([a],[b])'+getIdentArr :: [SOAC.Input] -> ([VName], [VName])+getIdentArr = foldl comb ([], [])+  where+    comb (vs, os) (SOAC.Input ts idd _)+      | SOAC.nullTransforms ts = (idd : vs, os)+    comb (vs, os) inp =+      (vs, SOAC.inputArray inp : os)++cleanFusionResult :: FusedRes -> FusedRes+cleanFusionResult fres =+  let newks = M.filter (not . null . fusedVars) (kernels fres)+      newoa = M.filter (`M.member` newks) (outArr fres)+      newia = M.map (S.filter (`M.member` newks)) (inpArr fres)+   in fres {outArr = newoa, inpArr = newia, kernels = newks}++--------------+--- Errors ---+--------------++errorIllegal :: String -> FusionGM FusedRes+errorIllegal soac_name =+  throwError $+    Error+      ("In Fusion.hs, soac " ++ soac_name ++ " appears illegally in pgm!")
src/Futhark/Optimise/Fusion/Composing.hs view
@@ -11,22 +11,20 @@ -- -- The module will, however, remove duplicate inputs after fusion. module Futhark.Optimise.Fusion.Composing-  ( fuseMaps-  , fuseRedomap-  , mergeReduceOps+  ( fuseMaps,+    fuseRedomap,+    mergeReduceOps,   )-  where+where  import Data.List (mapAccumL) import qualified Data.Map.Strict as M import Data.Maybe- import qualified Futhark.Analysis.HORep.SOAC as SOAC--import Futhark.IR-import Futhark.Binder (Bindable(..), insertStm, insertStms, mkLet)+import Futhark.Binder (Bindable (..), insertStm, insertStms, mkLet) import Futhark.Construct (mapResult)-import Futhark.Util (splitAt3, takeLast, dropLast)+import Futhark.IR+import Futhark.Util (dropLast, splitAt3, takeLast)  -- | @fuseMaps lam1 inp1 out1 lam2 inp2@ fuses the function @lam1@ into -- @lam2@.  Both functions must be mapping functions, although @lam2@@@ -45,167 +43,252 @@ -- -- The result is the fused function, and a list of the array inputs -- expected by the SOAC containing the fused function.-fuseMaps :: Bindable lore =>-            Names     -- ^ The producer var names that still need to be returned-         -> Lambda lore -- ^ Function of SOAC to be fused.-         -> [SOAC.Input] -- ^ Input of SOAC to be fused.-         -> [(VName,Ident)] -- ^ Output of SOAC to be fused.  The-                            -- first identifier is the name of the-                            -- actual output, where the second output-                            -- is an identifier that can be used to-                            -- bind a single element of that output.-         -> Lambda lore -- ^ Function to be fused with.-         -> [SOAC.Input] -- ^ Input of SOAC to be fused with.-         -> (Lambda lore, [SOAC.Input]) -- ^ The fused lambda and the inputs of-                                   -- the resulting SOAC.+fuseMaps ::+  Bindable lore =>+  -- | The producer var names that still need to be returned+  Names ->+  -- | Function of SOAC to be fused.+  Lambda lore ->+  -- | Input of SOAC to be fused.+  [SOAC.Input] ->+  -- | Output of SOAC to be fused.  The+  -- first identifier is the name of the+  -- actual output, where the second output+  -- is an identifier that can be used to+  -- bind a single element of that output.+  [(VName, Ident)] ->+  -- | Function to be fused with.+  Lambda lore ->+  -- | Input of SOAC to be fused with.+  [SOAC.Input] ->+  -- | The fused lambda and the inputs of+  -- the resulting SOAC.+  (Lambda lore, [SOAC.Input]) fuseMaps unfus_nms lam1 inp1 out1 lam2 inp2 = (lam2', M.elems inputmap)-  where lam2' =-          lam2 { lambdaParams = [ Param name t-                                | Ident name t <- lam2redparams ++ M.keys inputmap ]-               , lambdaBody   = new_body2'-               }-        new_body2 = let bnds res = [ mkLet [] [p] $ BasicOp $ SubExp e-                                   | (p,e) <- zip pat res]-                        bindLambda res =-                            stmsFromList (bnds res) `insertStms` makeCopiesInner (lambdaBody lam2)-                    in makeCopies $ mapResult bindLambda (lambdaBody lam1)-        new_body2_rses = bodyResult new_body2-        new_body2'= new_body2 { bodyResult = new_body2_rses ++-                                             map (Var . identName) unfus_pat  }-        -- infusible variables are added at the end of the result/pattern/type-        (lam2redparams, unfus_pat, pat, inputmap, makeCopies, makeCopiesInner) =-          fuseInputs unfus_nms lam1 inp1 out1 lam2 inp2-        --(unfus_accpat, unfus_arrpat) = splitAt (length unfus_accs) unfus_pat+  where+    lam2' =+      lam2+        { lambdaParams =+            [ Param name t+              | Ident name t <- lam2redparams ++ M.keys inputmap+            ],+          lambdaBody = new_body2'+        }+    new_body2 =+      let bnds res =+            [ mkLet [] [p] $ BasicOp $ SubExp e+              | (p, e) <- zip pat res+            ]+          bindLambda res =+            stmsFromList (bnds res) `insertStms` makeCopiesInner (lambdaBody lam2)+       in makeCopies $ mapResult bindLambda (lambdaBody lam1)+    new_body2_rses = bodyResult new_body2+    new_body2' =+      new_body2+        { bodyResult =+            new_body2_rses+              ++ map (Var . identName) unfus_pat+        }+    -- infusible variables are added at the end of the result/pattern/type+    (lam2redparams, unfus_pat, pat, inputmap, makeCopies, makeCopiesInner) =+      fuseInputs unfus_nms lam1 inp1 out1 lam2 inp2 -fuseInputs :: Bindable lore =>-              Names-           -> Lambda lore -> [SOAC.Input] -> [(VName,Ident)]-           -> Lambda lore -> [SOAC.Input]-           -> ([Ident], [Ident], [Ident],-               M.Map Ident SOAC.Input,-               Body lore -> Body lore, Body lore -> Body lore)+--(unfus_accpat, unfus_arrpat) = splitAt (length unfus_accs) unfus_pat++fuseInputs ::+  Bindable lore =>+  Names ->+  Lambda lore ->+  [SOAC.Input] ->+  [(VName, Ident)] ->+  Lambda lore ->+  [SOAC.Input] ->+  ( [Ident],+    [Ident],+    [Ident],+    M.Map Ident SOAC.Input,+    Body lore -> Body lore,+    Body lore -> Body lore+  ) fuseInputs unfus_nms lam1 inp1 out1 lam2 inp2 =   (lam2redparams, unfus_vars, outbnds, inputmap, makeCopies, makeCopiesInner)-  where (lam2redparams, lam2arrparams) =-          splitAt (length lam2params - length inp2) lam2params-        lam1params = map paramIdent $ lambdaParams lam1-        lam2params = map paramIdent $ lambdaParams lam2-        lam1inputmap = M.fromList $ zip lam1params inp1-        lam2inputmap = M.fromList $ zip lam2arrparams            inp2-        (lam2inputmap', makeCopiesInner) = removeDuplicateInputs lam2inputmap-        originputmap = lam1inputmap `M.union` lam2inputmap'-        outins = uncurry (outParams $ map fst out1) $-                 unzip $ M.toList lam2inputmap'-        outbnds= filterOutParams out1 outins-        (inputmap, makeCopies) =-          removeDuplicateInputs $ originputmap `M.difference` outins-        -- Cosmin: @unfus_vars@ is supposed to be the lam2 vars corresponding to unfus_nms (?)-        getVarParPair x = case SOAC.isVarInput (snd x) of-                            Just nm -> Just (nm, fst x)-                            Nothing -> Nothing --should not be reached!-        outinsrev = M.fromList $ mapMaybe getVarParPair $ M.toList outins-        unfusible outname-          | outname `nameIn` unfus_nms =-            outname `M.lookup` M.union outinsrev (M.fromList out1)-        unfusible _ = Nothing-        unfus_vars= mapMaybe (unfusible . fst) out1+  where+    (lam2redparams, lam2arrparams) =+      splitAt (length lam2params - length inp2) lam2params+    lam1params = map paramIdent $ lambdaParams lam1+    lam2params = map paramIdent $ lambdaParams lam2+    lam1inputmap = M.fromList $ zip lam1params inp1+    lam2inputmap = M.fromList $ zip lam2arrparams inp2+    (lam2inputmap', makeCopiesInner) = removeDuplicateInputs lam2inputmap+    originputmap = lam1inputmap `M.union` lam2inputmap'+    outins =+      uncurry (outParams $ map fst out1) $+        unzip $ M.toList lam2inputmap'+    outbnds = filterOutParams out1 outins+    (inputmap, makeCopies) =+      removeDuplicateInputs $ originputmap `M.difference` outins+    -- Cosmin: @unfus_vars@ is supposed to be the lam2 vars corresponding to unfus_nms (?)+    getVarParPair x = case SOAC.isVarInput (snd x) of+      Just nm -> Just (nm, fst x)+      Nothing -> Nothing --should not be reached!+    outinsrev = M.fromList $ mapMaybe getVarParPair $ M.toList outins+    unfusible outname+      | outname `nameIn` unfus_nms =+        outname `M.lookup` M.union outinsrev (M.fromList out1)+    unfusible _ = Nothing+    unfus_vars = mapMaybe (unfusible . fst) out1 -outParams :: [VName] -> [Ident] -> [SOAC.Input]-          -> M.Map Ident SOAC.Input+outParams ::+  [VName] ->+  [Ident] ->+  [SOAC.Input] ->+  M.Map Ident SOAC.Input outParams out1 lam2arrparams inp2 =   M.fromList $ mapMaybe isOutParam $ zip lam2arrparams inp2-  where isOutParam (p, inp)-          | Just a <- SOAC.isVarInput inp,-            a `elem` out1 = Just (p, inp)-        isOutParam _      = Nothing+  where+    isOutParam (p, inp)+      | Just a <- SOAC.isVarInput inp,+        a `elem` out1 =+        Just (p, inp)+    isOutParam _ = Nothing -filterOutParams :: [(VName,Ident)]-                -> M.Map Ident SOAC.Input-                -> [Ident]+filterOutParams ::+  [(VName, Ident)] ->+  M.Map Ident SOAC.Input ->+  [Ident] filterOutParams out1 outins =   snd $ mapAccumL checkUsed outUsage out1-  where outUsage = M.foldlWithKey' add M.empty outins-          where add m p inp =-                  case SOAC.isVarInput inp of-                    Just v  -> M.insertWith (++) v [p] m-                    Nothing -> m+  where+    outUsage = M.foldlWithKey' add M.empty outins+      where+        add m p inp =+          case SOAC.isVarInput inp of+            Just v -> M.insertWith (++) v [p] m+            Nothing -> m -        checkUsed m (a,ra) =-          case M.lookup a m of-            Just (p:ps) -> (M.insert a ps m, p)-            _           -> (m, ra)+    checkUsed m (a, ra) =+      case M.lookup a m of+        Just (p : ps) -> (M.insert a ps m, p)+        _ -> (m, ra) -removeDuplicateInputs :: Bindable lore =>-                         M.Map Ident SOAC.Input-                      -> (M.Map Ident SOAC.Input, Body lore -> Body lore)+removeDuplicateInputs ::+  Bindable lore =>+  M.Map Ident SOAC.Input ->+  (M.Map Ident SOAC.Input, Body lore -> Body lore) removeDuplicateInputs = fst . M.foldlWithKey' comb ((M.empty, id), M.empty)-  where comb ((parmap, inner), arrmap) par arr =-          case M.lookup arr arrmap of-            Nothing -> ((M.insert par arr parmap, inner),-                        M.insert arr (identName par) arrmap)-            Just par' -> ((parmap, inner . forward par par'),-                          arrmap)-        forward to from b =-          mkLet [] [to] (BasicOp $ SubExp $ Var from)-          `insertStm` b--fuseRedomap :: Bindable lore =>-               Names -> [VName]-            -> Lambda lore -> [SubExp] -> [SubExp] -> [SOAC.Input]-            -> [(VName,Ident)]-            -> Lambda lore -> [SubExp] -> [SubExp] -> [SOAC.Input]-            -> (Lambda lore, [SOAC.Input])-fuseRedomap unfus_nms outVars p_lam p_scan_nes p_red_nes p_inparr outPairs-                              c_lam c_scan_nes c_red_nes c_inparr =-  -- We hack the implementation of map o redomap to handle this case:-  --   (i) we remove the accumulator formal paramter and corresponding-  --       (body) result from from redomap's fold-lambda body-  let p_num_nes   = length p_scan_nes + length p_red_nes-      unfus_arrs  = filter (`nameIn` unfus_nms) outVars-      p_lam_body   = lambdaBody p_lam-      (p_lam_scan_ts, p_lam_red_ts, p_lam_map_ts) =-        splitAt3 (length p_scan_nes) (length p_red_nes) $ lambdaReturnType p_lam-      (p_lam_scan_res, p_lam_red_res, p_lam_map_res) =-        splitAt3 (length p_scan_nes) (length p_red_nes) $ bodyResult p_lam_body-      p_lam_hacked = p_lam { lambdaParams = takeLast (length p_inparr) $ lambdaParams p_lam-                           , lambdaBody   = p_lam_body { bodyResult = p_lam_map_res }-                           , lambdaReturnType = p_lam_map_ts }+  where+    comb ((parmap, inner), arrmap) par arr =+      case M.lookup arr arrmap of+        Nothing ->+          ( (M.insert par arr parmap, inner),+            M.insert arr (identName par) arrmap+          )+        Just par' ->+          ( (parmap, inner . forward par par'),+            arrmap+          )+    forward to from b =+      mkLet [] [to] (BasicOp $ SubExp $ Var from)+        `insertStm` b -  --  (ii) we remove the accumulator's (global) output result from-  --       @outPairs@, then ``map o redomap'' fuse the two lambdas-  --       (in the usual way), and construct the extra return types-  --       for the arrays that fall through.-      (res_lam, new_inp) = fuseMaps (namesFromList unfus_arrs) p_lam_hacked p_inparr-                                    (drop p_num_nes outPairs) c_lam c_inparr-      (res_lam_scan_ts, res_lam_red_ts, res_lam_map_ts) =-        splitAt3 (length c_scan_nes) (length c_red_nes) $ lambdaReturnType res_lam-      (_,extra_map_ts) = unzip $ filter (\(nm,_)-> nm `elem` unfus_arrs) $-                         zip (drop p_num_nes outVars) $ drop p_num_nes $-                         lambdaReturnType p_lam+fuseRedomap ::+  Bindable lore =>+  Names ->+  [VName] ->+  Lambda lore ->+  [SubExp] ->+  [SubExp] ->+  [SOAC.Input] ->+  [(VName, Ident)] ->+  Lambda lore ->+  [SubExp] ->+  [SubExp] ->+  [SOAC.Input] ->+  (Lambda lore, [SOAC.Input])+fuseRedomap+  unfus_nms+  outVars+  p_lam+  p_scan_nes+  p_red_nes+  p_inparr+  outPairs+  c_lam+  c_scan_nes+  c_red_nes+  c_inparr =+    -- We hack the implementation of map o redomap to handle this case:+    --   (i) we remove the accumulator formal paramter and corresponding+    --       (body) result from from redomap's fold-lambda body+    let p_num_nes = length p_scan_nes + length p_red_nes+        unfus_arrs = filter (`nameIn` unfus_nms) outVars+        p_lam_body = lambdaBody p_lam+        (p_lam_scan_ts, p_lam_red_ts, p_lam_map_ts) =+          splitAt3 (length p_scan_nes) (length p_red_nes) $ lambdaReturnType p_lam+        (p_lam_scan_res, p_lam_red_res, p_lam_map_res) =+          splitAt3 (length p_scan_nes) (length p_red_nes) $ bodyResult p_lam_body+        p_lam_hacked =+          p_lam+            { lambdaParams = takeLast (length p_inparr) $ lambdaParams p_lam,+              lambdaBody = p_lam_body {bodyResult = p_lam_map_res},+              lambdaReturnType = p_lam_map_ts+            } -  -- (iii) Finally, we put back the accumulator's formal parameter and-  --       (body) result in the first position of the obtained lambda.-      accpars  = dropLast (length p_inparr) $ lambdaParams p_lam-      res_body = lambdaBody res_lam-      (res_lam_scan_res, res_lam_red_res, res_lam_map_res) =-        splitAt3 (length c_scan_nes) (length c_red_nes) $ bodyResult res_body-      res_body'= res_body { bodyResult = p_lam_scan_res ++ res_lam_scan_res ++-                                         p_lam_red_res ++ res_lam_red_res ++-                                         res_lam_map_res }-      res_lam' = res_lam { lambdaParams     = accpars ++ lambdaParams res_lam-                         , lambdaBody       = res_body'-                         , lambdaReturnType = p_lam_scan_ts ++ res_lam_scan_ts ++-                                              p_lam_red_ts ++ res_lam_red_ts ++-                                              res_lam_map_ts ++ extra_map_ts-                         }-  in  (res_lam', new_inp)+        --  (ii) we remove the accumulator's (global) output result from+        --       @outPairs@, then ``map o redomap'' fuse the two lambdas+        --       (in the usual way), and construct the extra return types+        --       for the arrays that fall through.+        (res_lam, new_inp) =+          fuseMaps+            (namesFromList unfus_arrs)+            p_lam_hacked+            p_inparr+            (drop p_num_nes outPairs)+            c_lam+            c_inparr+        (res_lam_scan_ts, res_lam_red_ts, res_lam_map_ts) =+          splitAt3 (length c_scan_nes) (length c_red_nes) $ lambdaReturnType res_lam+        (_, extra_map_ts) =+          unzip $+            filter (\(nm, _) -> nm `elem` unfus_arrs) $+              zip (drop p_num_nes outVars) $+                drop p_num_nes $+                  lambdaReturnType p_lam +        -- (iii) Finally, we put back the accumulator's formal parameter and+        --       (body) result in the first position of the obtained lambda.+        accpars = dropLast (length p_inparr) $ lambdaParams p_lam+        res_body = lambdaBody res_lam+        (res_lam_scan_res, res_lam_red_res, res_lam_map_res) =+          splitAt3 (length c_scan_nes) (length c_red_nes) $ bodyResult res_body+        res_body' =+          res_body+            { bodyResult =+                p_lam_scan_res ++ res_lam_scan_res+                  ++ p_lam_red_res+                  ++ res_lam_red_res+                  ++ res_lam_map_res+            }+        res_lam' =+          res_lam+            { lambdaParams = accpars ++ lambdaParams res_lam,+              lambdaBody = res_body',+              lambdaReturnType =+                p_lam_scan_ts ++ res_lam_scan_ts+                  ++ p_lam_red_ts+                  ++ res_lam_red_ts+                  ++ res_lam_map_ts+                  ++ extra_map_ts+            }+     in (res_lam', new_inp)  mergeReduceOps :: Lambda lore -> Lambda lore -> Lambda lore mergeReduceOps (Lambda par1 bdy1 rtp1) (Lambda par2 bdy2 rtp2) =-  let body' = Body (bodyDec bdy1)-                   (bodyStms bdy1 <> bodyStms bdy2)-                   (bodyResult bdy1 ++ bodyResult   bdy2)+  let body' =+        Body+          (bodyDec bdy1)+          (bodyStms bdy1 <> bodyStms bdy2)+          (bodyResult bdy1 ++ bodyResult bdy2)       (len1, len2) = (length rtp1, length rtp2)-      par'  = take len1 par1 ++ take len2 par2 ++ drop len1 par1 ++ drop len2 par2-  in  Lambda par' body' (rtp1++rtp2)+      par' = take len1 par1 ++ take len2 par2 ++ drop len1 par1 ++ drop len2 par2+   in Lambda par' body' (rtp1 ++ rtp2)
src/Futhark/Optimise/Fusion/LoopKernel.hs view
@@ -1,785 +1,942 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-module Futhark.Optimise.Fusion.LoopKernel-  ( FusedKer(..)-  , newKernel-  , inputs-  , setInputs-  , arrInputs-  , transformOutput-  , attemptFusion-  , SOAC-  , MapNest-  )-  where--import Control.Applicative-import Control.Arrow (first)-import Control.Monad-import Control.Monad.Reader-import Control.Monad.State-import qualified Data.Set as S-import qualified Data.Map.Strict as M-import Data.Maybe-import Data.List (find, (\\), tails)--import Futhark.IR.SOACS hiding (SOAC(..))-import qualified Futhark.IR.SOACS as Futhark-import Futhark.Transform.Rename (renameLambda)-import Futhark.Transform.Substitute-import Futhark.MonadFreshNames-import qualified Futhark.Analysis.HORep.SOAC as SOAC-import qualified Futhark.Analysis.HORep.MapNest as MapNest-import Futhark.Pass.ExtractKernels.ISRWIM (rwimPossible)-import Futhark.Optimise.Fusion.Composing-import Futhark.Construct-import Futhark.Util (splitAt3)--newtype TryFusion a = TryFusion (ReaderT (Scope SOACS)-                                 (StateT VNameSource Maybe)-                                 a)-  deriving (Functor, Applicative, Alternative, Monad, MonadFail,-            MonadFreshNames,-            HasScope SOACS,-            LocalScope SOACS)--tryFusion :: MonadFreshNames m =>-             TryFusion a -> Scope SOACS -> m (Maybe a)-tryFusion (TryFusion m) types = modifyNameSource $ \src ->-  case runStateT (runReaderT m types) src of-    Just (x, src') -> (Just x, src')-    Nothing        -> (Nothing, src)--liftMaybe :: Maybe a -> TryFusion a-liftMaybe Nothing = fail "Nothing"-liftMaybe (Just x) = return x--type SOAC = SOAC.SOAC SOACS-type MapNest = MapNest.MapNest SOACS---- XXX: This function is very gross.-transformOutput :: SOAC.ArrayTransforms -> [VName] -> [Ident]-                -> Binder SOACS ()-transformOutput ts names = descend ts-  where descend ts' validents =-          case SOAC.viewf ts' of-            SOAC.EmptyF ->-              forM_ (zip names validents) $ \(k, valident) ->-              letBindNames [k] $ BasicOp $ SubExp $ Var $ identName valident-            t SOAC.:< ts'' -> do-              let (es,css) = unzip $ map (applyTransform t) validents-                  mkPat (Ident nm tp) = Pattern [] [PatElem nm tp]-              opts <- concat <$> mapM primOpType es-              newIds <- forM (zip names opts) $ \(k, opt) ->-                newIdent (baseString k) opt-              forM_ (zip3 css newIds es) $ \(cs,ids,e) ->-                certifying cs $ letBind (mkPat ids) (BasicOp e)-              descend ts'' newIds--applyTransform :: SOAC.ArrayTransform -> Ident -> (BasicOp, Certificates)-applyTransform (SOAC.Rearrange cs perm) v =-  (Rearrange perm' $ identName v, cs)-  where perm' = perm ++ drop (length perm) [0..arrayRank (identType v)-1]-applyTransform (SOAC.Reshape cs shape) v =-  (Reshape shape $ identName v, cs)-applyTransform (SOAC.ReshapeOuter cs shape) v =-  let shapes = reshapeOuter shape 1 $ arrayShape $ identType v-  in (Reshape shapes $ identName v, cs)-applyTransform (SOAC.ReshapeInner cs shape) v =-  let shapes = reshapeInner shape 1 $ arrayShape $ identType v-  in (Reshape shapes $ identName v, cs)-applyTransform (SOAC.Replicate cs n) v =-  (Replicate n $ Var $ identName v, cs)--inputToOutput :: SOAC.Input -> Maybe (SOAC.ArrayTransform, SOAC.Input)-inputToOutput (SOAC.Input ts ia iat) =-  case SOAC.viewf ts of-    t SOAC.:< ts' -> Just (t, SOAC.Input ts' ia iat)-    SOAC.EmptyF   -> Nothing--data FusedKer = FusedKer {-    fsoac      :: SOAC-  -- ^ the SOAC expression, e.g., mapT( f(a,b), x, y )--  , inplace    :: Names-  -- ^ Variables used in in-place updates in the kernel itself, as-  -- well as on the path to the kernel from the current position.-  -- This is used to avoid fusion that would violate in-place-  -- restrictions.--  , fusedVars :: [VName]-  -- ^ whether at least a fusion has been performed.--  , fusedConsumed :: Names-  -- ^ The set of variables that were consumed by the SOACs-  -- contributing to this kernel.  Note that, by the type rules, the-  -- final SOAC may actually consume _more_ than its original-  -- contributors, which implies the need for 'Copy' expressions.--  , kernelScope :: Scope SOACS-  -- ^ The names in scope at the kernel.--  , outputTransform :: SOAC.ArrayTransforms-  , outNames :: [VName]-  , kerAux :: StmAux ()-  } deriving (Show)--newKernel :: StmAux () -> SOAC -> Names -> [VName] -> Scope SOACS -> FusedKer-newKernel aux soac consumed out_nms scope =-  FusedKer { fsoac = soac-           , inplace = consumed-           , fusedVars = []-           , fusedConsumed = consumed-           , outputTransform = SOAC.noTransforms-           , outNames = out_nms-           , kernelScope = scope-           , kerAux = aux-           }--arrInputs :: FusedKer -> S.Set VName-arrInputs = S.fromList . map SOAC.inputArray . inputs--inputs :: FusedKer -> [SOAC.Input]-inputs = SOAC.inputs . fsoac--setInputs :: [SOAC.Input] -> FusedKer -> FusedKer-setInputs inps ker = ker { fsoac = inps `SOAC.setInputs` fsoac ker }--tryOptimizeSOAC :: Names -> [VName] -> SOAC -> Names -> FusedKer-                -> TryFusion FusedKer-tryOptimizeSOAC unfus_nms outVars soac consumed ker = do-  (soac', ots) <- optimizeSOAC Nothing soac mempty-  let ker' = map (addInitialTransformIfRelevant ots) (inputs ker) `setInputs` ker-      outIdents = zipWith Ident outVars $ SOAC.typeOf soac'-      ker'' = fixInputTypes outIdents ker'-  applyFusionRules unfus_nms outVars soac' consumed ker''-  where addInitialTransformIfRelevant ots inp-          | SOAC.inputArray inp `elem` outVars =-              SOAC.addInitialTransforms ots inp-          | otherwise =-              inp--tryOptimizeKernel :: Names -> [VName] -> SOAC -> Names -> FusedKer-                  -> TryFusion FusedKer-tryOptimizeKernel unfus_nms outVars soac consumed ker = do-  ker' <- optimizeKernel (Just outVars) ker-  applyFusionRules unfus_nms outVars soac consumed ker'--tryExposeInputs :: Names -> [VName] -> SOAC -> Names -> FusedKer-                -> TryFusion FusedKer-tryExposeInputs unfus_nms outVars soac consumed ker = do-  (ker', ots) <- exposeInputs outVars ker-  if SOAC.nullTransforms ots-  then fuseSOACwithKer unfus_nms outVars soac consumed ker'-  else do-    (soac', ots') <- pullOutputTransforms soac ots-    let outIdents = zipWith Ident outVars $ SOAC.typeOf soac'-        ker'' = fixInputTypes outIdents ker'-    if SOAC.nullTransforms ots'-    then applyFusionRules unfus_nms outVars soac' consumed ker''-    else fail "tryExposeInputs could not pull SOAC transforms"--fixInputTypes :: [Ident] -> FusedKer -> FusedKer-fixInputTypes outIdents ker =-  ker { fsoac = fixInputTypes' $ fsoac ker }-  where fixInputTypes' soac =-          map fixInputType (SOAC.inputs soac) `SOAC.setInputs` soac-        fixInputType (SOAC.Input ts v _)-          | Just v' <- find ((==v) . identName) outIdents =-            SOAC.Input ts v $ identType v'-        fixInputType inp = inp--applyFusionRules :: Names -> [VName] -> SOAC -> Names -> FusedKer-                 -> TryFusion FusedKer-applyFusionRules    unfus_nms outVars soac consumed ker =-  tryOptimizeSOAC   unfus_nms outVars soac consumed ker <|>-  tryOptimizeKernel unfus_nms outVars soac consumed ker <|>-  fuseSOACwithKer   unfus_nms outVars soac consumed ker <|>-  tryExposeInputs   unfus_nms outVars soac consumed ker--attemptFusion :: MonadFreshNames m =>-                 Names -> [VName] -> SOAC -> Names -> FusedKer-              -> m (Maybe FusedKer)-attemptFusion unfus_nms outVars soac consumed ker =-  fmap removeUnusedParamsFromKer <$>-    tryFusion (applyFusionRules unfus_nms outVars soac consumed ker)-    (kernelScope ker)--removeUnusedParamsFromKer :: FusedKer -> FusedKer-removeUnusedParamsFromKer ker =-  case soac of SOAC.Screma {} -> ker { fsoac = soac' }-               _                -> ker-  where soac = fsoac ker-        l = SOAC.lambda soac-        inps = SOAC.inputs soac-        (l', inps') = removeUnusedParams l inps-        soac' = l' `SOAC.setLambda`-                (inps' `SOAC.setInputs` soac)--removeUnusedParams :: Lambda -> [SOAC.Input] -> (Lambda, [SOAC.Input])-removeUnusedParams l inps =-  (l { lambdaParams = ps' }, inps')-  where pInps = zip (lambdaParams l) inps-        (ps', inps') = case (unzip $ filter (used . fst) pInps, pInps) of-                         (([], []), (p,inp):_) -> ([p], [inp])-                         ((ps_, inps_), _)     -> (ps_, inps_)-        used p = paramName p `nameIn` freeVars-        freeVars = freeIn $ lambdaBody l---- | Check that the consumer uses at least one output of the producer--- unmodified.-mapFusionOK :: [VName] -> FusedKer -> Bool-mapFusionOK outVars ker = any (`elem` inpIds) outVars-  where inpIds = mapMaybe SOAC.isVarishInput (inputs ker)---- | Check that the consumer uses all the outputs of the producer unmodified.-mapWriteFusionOK :: [VName] -> FusedKer -> Bool-mapWriteFusionOK outVars ker = all (`elem` inpIds) outVars-  where inpIds = mapMaybe SOAC.isVarishInput (inputs ker)---- | The brain of this module: Fusing a SOAC with a Kernel.-fuseSOACwithKer :: Names -> [VName] -> SOAC -> Names -> FusedKer-                -> TryFusion FusedKer-fuseSOACwithKer unfus_set outVars soac_p soac_p_consumed ker = do-  -- We are fusing soac_p into soac_c, i.e, the output of soac_p is going-  -- into soac_c.-  let soac_c    = fsoac ker-      inp_p_arr = SOAC.inputs soac_p-      horizFuse= unfus_set /= mempty &&-                 SOAC.width soac_p == SOAC.width soac_c-      inp_c_arr = SOAC.inputs soac_c-      lam_p     = SOAC.lambda soac_p-      lam_c     = SOAC.lambda soac_c-      w        = SOAC.width soac_p-      returned_outvars = filter (`nameIn` unfus_set) outVars-      success res_outnms res_soac = do-        let fusedVars_new = fusedVars ker++outVars-        -- Avoid name duplication, because the producer lambda is not-        -- removed from the program until much later.-        uniq_lam <- renameLambda $ SOAC.lambda res_soac-        return $ ker { fsoac = uniq_lam `SOAC.setLambda` res_soac-                     , fusedVars = fusedVars_new-                     , inplace = inplace ker <> soac_p_consumed-                     , fusedConsumed = fusedConsumed ker <> soac_p_consumed-                     , outNames = res_outnms-                     }--  outPairs <- forM (zip outVars $ map rowType $ SOAC.typeOf soac_p) $ \(outVar, t) -> do-                outVar' <- newVName $ baseString outVar ++ "_elem"-                return (outVar, Ident outVar' t)--  let mapLikeFusionCheck =-        let (res_lam, new_inp) = fuseMaps unfus_set lam_p inp_p_arr outPairs lam_c inp_c_arr-            (extra_nms,extra_rtps) = unzip $ filter ((`nameIn` unfus_set) . fst) $-              zip outVars $ map (stripArray 1) $ SOAC.typeOf soac_p-            res_lam' = res_lam { lambdaReturnType = lambdaReturnType res_lam ++ extra_rtps }-        in (extra_nms, res_lam', new_inp)--  when (horizFuse && not (SOAC.nullTransforms $ outputTransform ker)) $-    fail "Horizontal fusion is invalid in the presence of output transforms."--  case (soac_c, soac_p) of-    _ | SOAC.width soac_p /= SOAC.width soac_c -> fail "SOAC widths must match."--    (SOAC.Screma _ (ScremaForm scans_c reds_c _) _,-     SOAC.Screma _ (ScremaForm scans_p reds_p _) _)-      | mapFusionOK (drop (Futhark.scanResults scans_p+Futhark.redResults reds_p) outVars) ker-        || horizFuse -> do-      let red_nes_p = concatMap redNeutral reds_p-          red_nes_c = concatMap redNeutral reds_c-          scan_nes_p = concatMap scanNeutral scans_p-          scan_nes_c = concatMap scanNeutral scans_c-          (res_lam', new_inp) = fuseRedomap unfus_set outVars-                                            lam_p scan_nes_p red_nes_p inp_p_arr-                                            outPairs-                                            lam_c scan_nes_c red_nes_c inp_c_arr-          (soac_p_scanout, soac_p_redout, _soac_p_mapout) =-            splitAt3 (length scan_nes_p) (length red_nes_p) outVars-          (soac_c_scanout, soac_c_redout, soac_c_mapout) =-            splitAt3 (length scan_nes_c) (length red_nes_c) $ outNames ker-          unfus_arrs  = returned_outvars \\ (soac_p_scanout++soac_p_redout)-      success (soac_p_scanout ++ soac_c_scanout ++-               soac_p_redout ++ soac_c_redout ++-               soac_c_mapout ++ unfus_arrs) $-        SOAC.Screma w (ScremaForm (scans_p ++ scans_c) (reds_p ++ reds_c) res_lam')-        new_inp--    -------------------    -- Scatter fusion ---    --------------------    -- Map-Scatter fusion.-    ---    -- The 'inplace' mechanism for kernels already takes care of-    -- checking that the Scatter is not writing to any array used in-    -- the Map.-    (SOAC.Scatter _len _lam _ivs dests,-     SOAC.Screma _ form _)-      | isJust $ isMapSOAC form,-        -- 1. all arrays produced by the map are ONLY used (consumed)-        --    by the scatter, i.e., not used elsewhere.-        not (any (`nameIn` unfus_set) outVars),-        -- 2. all arrays produced by the map are input to the scatter.-        mapWriteFusionOK outVars ker -> do-          let (extra_nms, res_lam', new_inp) = mapLikeFusionCheck-          success (outNames ker ++ extra_nms) $-            SOAC.Scatter w res_lam' new_inp dests--    -- Map-Hist fusion.-    ---    -- The 'inplace' mechanism for kernels already takes care of-    -- checking that the Hist is not writing to any array used in-    -- the Map.-    (SOAC.Hist _ ops _ _,-     SOAC.Screma _ form _)-      | isJust $ isMapSOAC form,-        -- 1. all arrays produced by the map are ONLY used (consumed)-        --    by the hist, i.e., not used elsewhere.-        not (any (`nameIn` unfus_set) outVars),-        -- 2. all arrays produced by the map are input to the scatter.-        mapWriteFusionOK outVars ker -> do-          let (extra_nms, res_lam', new_inp) = mapLikeFusionCheck-          success (outNames ker ++ extra_nms) $-            SOAC.Hist w ops res_lam' new_inp--    -- Hist-Hist fusion-    (SOAC.Hist _ ops_c _ _,-     SOAC.Hist _ ops_p _ _)-      | horizFuse -> do-          let p_num_buckets = length ops_p-              c_num_buckets = length ops_c-              (body_p, body_c) = (lambdaBody lam_p, lambdaBody lam_c)-              body' =-                Body { bodyDec = bodyDec body_p -- body_p and body_c have the same lores-                     , bodyStms = bodyStms body_p <> bodyStms body_c-                     , bodyResult = take c_num_buckets (bodyResult body_c) ++-                                    take p_num_buckets (bodyResult body_p) ++-                                    drop c_num_buckets (bodyResult body_c) ++-                                    drop p_num_buckets (bodyResult body_p)-                     }-              lam' =-                Lambda { lambdaParams = lambdaParams lam_c ++ lambdaParams lam_p-                       , lambdaBody = body'-                       , lambdaReturnType = replicate (c_num_buckets+p_num_buckets) (Prim int32) ++-                                            drop c_num_buckets (lambdaReturnType lam_c) ++-                                            drop p_num_buckets (lambdaReturnType lam_p)-                       }-          success (outNames ker ++ returned_outvars) $-            SOAC.Hist w (ops_c <> ops_p) lam' (inp_c_arr <> inp_p_arr)--    -- Scatter-write fusion.-    (SOAC.Scatter _len2 _lam_c ivs2 as2,-     SOAC.Scatter _len_p _lam_p ivs_p as_p)-      | horizFuse -> do-          let zipW xs ys = ys_p ++ xs_p ++ ys2 ++ xs2-                where lenx = length xs `div` 2-                      xs_p  = take lenx xs-                      xs2  = drop lenx xs-                      leny = length ys `div` 2-                      ys_p  = take leny ys-                      ys2  = drop leny ys-          let (body_p, body2) = (lambdaBody lam_p, lambdaBody lam_c)-          let body' = Body { bodyDec = bodyDec body_p -- body_p and body2 have the same lores-                           , bodyStms = bodyStms body_p <> bodyStms body2-                           , bodyResult = zipW (bodyResult body_p) (bodyResult body2)-                           }-          let lam' = Lambda { lambdaParams = lambdaParams lam_p ++ lambdaParams lam_c-                            , lambdaBody = body'-                            , lambdaReturnType = zipW (lambdaReturnType lam_p) (lambdaReturnType lam_c)-                            }-          success (outNames ker ++ returned_outvars) $-            SOAC.Scatter w lam' (ivs_p ++ ivs2) (as2 ++ as_p)--    (SOAC.Scatter {}, _) ->-      fail "Cannot fuse a write with anything else than a write or a map"-    (_, SOAC.Scatter {}) ->-      fail "Cannot fuse a write with anything else than a write or a map"--    -----------------------------    -- Stream-Stream Fusions: ---    -----------------------------    (SOAC.Stream _ Sequential{} _ _, SOAC.Stream _ form_p@Sequential{} _ _)-     | mapFusionOK (drop (length $ getStreamAccums form_p) outVars) ker || horizFuse -> do-      -- fuse two SEQUENTIAL streams-      (res_nms, res_stream) <- fuseStreamHelper (outNames ker) unfus_set outVars outPairs soac_c soac_p-      success res_nms res_stream--    (SOAC.Stream _ Sequential{} _ _, SOAC.Stream _ Sequential{} _ _) ->-      fail "Fusion conditions not met for two SEQ streams!"--    (SOAC.Stream _ Sequential{} _ _, SOAC.Stream{}) ->-      fail "Cannot fuse a parallel with a sequential Stream!"--    (SOAC.Stream{}, SOAC.Stream _ Sequential{} _ _) ->-      fail "Cannot fuse a parallel with a sequential Stream!"--    (SOAC.Stream{}, SOAC.Stream _ form_p _ _)-     | mapFusionOK (drop (length $ getStreamAccums form_p) outVars) ker || horizFuse -> do-      -- fuse two PARALLEL streams-      (res_nms, res_stream) <- fuseStreamHelper (outNames ker) unfus_set outVars outPairs soac_c soac_p-      success res_nms res_stream--    (SOAC.Stream{}, SOAC.Stream {}) ->-      fail "Fusion conditions not met for two PAR streams!"--    --------------------------------------------------------------------    --- If one is a stream, translate the other to a stream as well.----    --- This does not get in trouble (infinite computation) because ----    ---   scan's translation to Stream introduces a hindrance to    ----    ---   (horizontal fusion), hence repeated application is for the----    ---   moment impossible. However, if with a dependence-graph rep----    ---   we could run in an infinite recursion, i.e., repeatedly   ----    ---   fusing map o scan into an infinity of Stream levels!      ----    --------------------------------------------------------------------    (SOAC.Stream _ form2 _ _, _) -> do-      -- If this rule is matched then soac_p is NOT a stream.-      -- To fuse a stream kernel, we transform soac_p to a stream, which-      -- borrows the sequential/parallel property of the soac_c Stream,-      -- and recursively perform stream-stream fusion.-      (soac_p', newacc_ids) <- SOAC.soacToStream soac_p-      soac_p'' <- case form2 of-                    Sequential{} -> toSeqStream soac_p'-                    _            -> return soac_p'-      if soac_p' == soac_p-        then fail "SOAC could not be turned into stream."-        else fuseSOACwithKer unfus_set (map identName newacc_ids++outVars) soac_p'' soac_p_consumed ker--    (_, SOAC.Screma _ form _) | Just _ <- Futhark.isScanSOAC form -> do-      -- A Scan soac can be currently only fused as a (sequential) stream,-      -- hence it is first translated to a (sequential) Stream and then-      -- fusion with a kernel is attempted.-      (soac_p', newacc_ids) <- SOAC.soacToStream soac_p-      if soac_p' /= soac_p then-        fuseSOACwithKer unfus_set (map identName newacc_ids++outVars) soac_p' soac_p_consumed ker-        else fail "SOAC could not be turned into stream."--    (_, SOAC.Stream _ form_p _ _) -> do-      -- If it reached this case then soac_c is NOT a Stream kernel,-      -- hence transform the kernel's soac to a stream and attempt-      -- stream-stream fusion recursivelly.-      -- The newly created stream corresponding to soac_c borrows the-      -- sequential/parallel property of the soac_p stream.-      (soac_c', newacc_ids) <- SOAC.soacToStream soac_c-      when (soac_c' == soac_c) $ fail "SOAC could not be turned into stream."-      soac_c'' <- case form_p of-                    Sequential _ -> toSeqStream soac_c'-                    _            -> return soac_c'--      fuseSOACwithKer unfus_set outVars soac_p soac_p_consumed $-        ker { fsoac = soac_c'', outNames = map identName newacc_ids ++ outNames ker }--    ----------------------------------    --- DEFAULT, CANNOT FUSE CASE ----    ----------------------------------    _ -> fail "Cannot fuse"--getStreamOrder :: StreamForm lore -> StreamOrd-getStreamOrder (Parallel o _ _ _) = o-getStreamOrder (Sequential  _) = InOrder--fuseStreamHelper :: [VName] -> Names -> [VName] -> [(VName,Ident)]-                 -> SOAC -> SOAC -> TryFusion ([VName], SOAC)-fuseStreamHelper out_kernms unfus_set outVars outPairs-                 (SOAC.Stream w2 form2 lam2 inp2_arr)-                 (SOAC.Stream _ form1 lam1 inp1_arr) =-  if getStreamOrder form2 /= getStreamOrder form1-  then fail "fusion conditions not met!"-  else do -- very similar to redomap o redomap composition, but need-          -- to remove first the `chunk' parameters of streams'-          -- lambdas and put them in the resulting stream lambda.-          let nes1    = getStreamAccums form1-              chunk1  = head $ lambdaParams lam1-              chunk2  = head $ lambdaParams lam2-              hmnms = M.fromList [(paramName chunk2, paramName chunk1)]-              lam20 = substituteNames hmnms lam2-              lam1' = lam1  { lambdaParams = tail $ lambdaParams lam1  }-              lam2' = lam20 { lambdaParams = tail $ lambdaParams lam20 }-              (res_lam', new_inp) = fuseRedomap unfus_set outVars-                                                lam1' [] nes1-                                                inp1_arr outPairs-                                                lam2' [] (getStreamAccums form2)-                                                inp2_arr-              res_lam'' = res_lam' { lambdaParams = chunk1 : lambdaParams res_lam' }-              unfus_accs  = take (length nes1) outVars-              unfus_arrs  = filter (`nameIn` unfus_set) outVars-          res_form <- mergeForms form2 form1-          return (unfus_accs ++ out_kernms ++ unfus_arrs,-                  SOAC.Stream w2 res_form res_lam'' new_inp )-  where mergeForms (Sequential acc2) (Sequential acc1) = return $ Sequential (acc1++acc2)-        mergeForms (Parallel _ comm2 lam2r acc2) (Parallel o1 comm1 lam1r acc1) =-            return $ Parallel o1 (comm1<>comm2) (mergeReduceOps lam1r lam2r) (acc1++acc2)-        mergeForms _ _ = fail "Fusing sequential to parallel stream disallowed!"-fuseStreamHelper _ _ _ _ _ _ = fail "Cannot Fuse Streams!"---- | If a Stream is passed as argument then it converts it to a---   Sequential Stream; Otherwise it FAILS!-toSeqStream :: SOAC -> TryFusion SOAC-toSeqStream s@(SOAC.Stream _ (Sequential _) _ _) = return s-toSeqStream (SOAC.Stream w (Parallel _ _ _ acc) l inps) =-    return $ SOAC.Stream w (Sequential acc) l inps-toSeqStream _ = fail "toSeqStream expects a stream, but given a SOAC."---- Here follows optimizations and transforms to expose fusability.--optimizeKernel :: Maybe [VName] -> FusedKer -> TryFusion FusedKer-optimizeKernel inp ker = do-  (soac, resTrans) <- optimizeSOAC inp (fsoac ker) startTrans-  return $ ker { fsoac = soac-               , outputTransform = resTrans-               }-  where startTrans = outputTransform ker--optimizeSOAC :: Maybe [VName] -> SOAC -> SOAC.ArrayTransforms-             -> TryFusion (SOAC, SOAC.ArrayTransforms)-optimizeSOAC inp soac os = do-  res <- foldM comb (False, soac, os) optimizations-  case res of-    (False, _, _)      -> fail "No optimisation applied"-    (True, soac', os') -> return (soac', os')-  where comb (changed, soac', os') f = do-          (soac'', os'') <- f inp soac' os-          return (True, soac'', os'')-          <|> return (changed, soac', os')--type Optimization = Maybe [VName]-                    -> SOAC-                    -> SOAC.ArrayTransforms-                    -> TryFusion (SOAC, SOAC.ArrayTransforms)--optimizations :: [Optimization]-optimizations = [iswim]--iswim :: Maybe [VName] -> SOAC -> SOAC.ArrayTransforms-      -> TryFusion (SOAC, SOAC.ArrayTransforms)-iswim _ (SOAC.Screma w form arrs) ots-  | Just [Futhark.Scan scan_fun nes] <- Futhark.isScanSOAC form,-    Just (map_pat, map_cs, map_w, map_fun) <- rwimPossible scan_fun,-    Just nes_names <- mapM subExpVar nes = do--      let nes_idents = zipWith Ident nes_names $ lambdaReturnType scan_fun-          map_nes = map SOAC.identInput nes_idents-          map_arrs' = map_nes ++ map (SOAC.transposeInput 0 1) arrs-          (scan_acc_params, scan_elem_params) =-            splitAt (length arrs) $ lambdaParams scan_fun-          map_params = map removeParamOuterDim scan_acc_params ++-                       map (setParamOuterDimTo w) scan_elem_params-          map_rettype = map (`setOuterSize` w) $ lambdaReturnType scan_fun--          scan_params = lambdaParams map_fun-          scan_body = lambdaBody map_fun-          scan_rettype = lambdaReturnType map_fun-          scan_fun' = Lambda scan_params scan_body scan_rettype-          nes' = map Var $ take (length map_nes) $ map paramName map_params-          arrs' = drop (length map_nes) $ map paramName map_params--      scan_form <- scanSOAC [Futhark.Scan scan_fun' nes']--      let map_body = mkBody (oneStm $-                              Let (setPatternOuterDimTo w map_pat) (defAux ()) $-                              Op $ Futhark.Screma w scan_form arrs') $-                            map Var $ patternNames map_pat-          map_fun' = Lambda map_params map_body map_rettype-          perm = case lambdaReturnType map_fun of-                   []  -> []-                   t:_ -> 1 : 0 : [2..arrayRank t]--      return (SOAC.Screma map_w (ScremaForm [] [] map_fun') map_arrs',-              ots SOAC.|> SOAC.Rearrange map_cs perm)--iswim _ _ _ =-  fail "ISWIM does not apply."--removeParamOuterDim :: LParam -> LParam-removeParamOuterDim param =-  let t = rowType $ paramType param-  in param { paramDec = t }--setParamOuterDimTo :: SubExp -> LParam -> LParam-setParamOuterDimTo w param =-  let t = paramType param `setOuterSize` w-  in param { paramDec = t }--setPatternOuterDimTo :: SubExp -> Pattern -> Pattern-setPatternOuterDimTo w = fmap (`setOuterSize` w)---- Now for fiddling with transpositions...--commonTransforms :: [VName] -> [SOAC.Input]-                 -> (SOAC.ArrayTransforms, [SOAC.Input])-commonTransforms interesting inps = commonTransforms' inps'-  where inps' = [ (SOAC.inputArray inp `elem` interesting, inp)-                | inp <- inps ]--commonTransforms' :: [(Bool, SOAC.Input)] -> (SOAC.ArrayTransforms, [SOAC.Input])-commonTransforms' inps =-  case foldM inspect (Nothing, []) inps of-    Just (Just mot, inps') -> first (mot SOAC.<|) $ commonTransforms' $ reverse inps'-    _                      -> (SOAC.noTransforms, map snd inps)-  where inspect (mot, prev) (True, inp) =-          case (mot, inputToOutput inp) of-           (Nothing,  Just (ot, inp'))  -> Just (Just ot, (True, inp') : prev)-           (Just ot1, Just (ot2, inp'))-             | ot1 == ot2 -> Just (Just ot2, (True, inp') : prev)-           _              -> Nothing-        inspect (mot, prev) inp = Just (mot,inp:prev)--mapDepth :: MapNest -> Int-mapDepth (MapNest.MapNest _ lam levels _) =-  min resDims (length levels) + 1-  where resDims = minDim $ case levels of-                    [] -> lambdaReturnType lam-                    nest:_ -> MapNest.nestingReturnType nest-        minDim [] = 0-        minDim (t:ts) = foldl min (arrayRank t) $ map arrayRank ts--pullRearrange :: SOAC -> SOAC.ArrayTransforms-              -> TryFusion (SOAC, SOAC.ArrayTransforms)-pullRearrange soac ots = do-  nest <- liftMaybe =<< MapNest.fromSOAC soac-  SOAC.Rearrange cs perm SOAC.:< ots' <- return $ SOAC.viewf ots-  if rearrangeReach perm <= mapDepth nest then do-    let -- Expand perm to cover the full extent of the input dimensionality-        perm' inp = take r perm ++ [length perm..r-1]-          where r = SOAC.inputRank inp-        addPerm inp = SOAC.addTransform (SOAC.Rearrange cs $ perm' inp) inp-        inputs' = map addPerm $ MapNest.inputs nest-    soac' <- MapNest.toSOAC $-      inputs' `MapNest.setInputs` rearrangeReturnTypes nest perm-    return (soac', ots')-  else fail "Cannot pull transpose"--pushRearrange :: [VName] -> SOAC -> SOAC.ArrayTransforms-              -> TryFusion (SOAC, SOAC.ArrayTransforms)-pushRearrange inpIds soac ots = do-  nest <- liftMaybe =<< MapNest.fromSOAC soac-  (perm, inputs') <- liftMaybe $ fixupInputs inpIds $ MapNest.inputs nest-  if rearrangeReach perm <= mapDepth nest then do-    let invertRearrange = SOAC.Rearrange mempty $ rearrangeInverse perm-    soac' <- MapNest.toSOAC $-      inputs' `MapNest.setInputs`-      rearrangeReturnTypes nest perm-    return (soac', invertRearrange SOAC.<| ots)-  else fail "Cannot push transpose"---- | Actually also rearranges indices.-rearrangeReturnTypes :: MapNest -> [Int] -> MapNest-rearrangeReturnTypes nest@(MapNest.MapNest w body nestings inps) perm =-  MapNest.MapNest w-  body-  (zipWith setReturnType-   nestings $-   drop 1 $ iterate (map rowType) ts)-  inps-  where origts = MapNest.typeOf nest-        -- The permutation may be deeper than the rank of the type,-        -- but it is required that it is an identity permutation-        -- beyond that.  This is supposed to be checked as an-        -- invariant by whoever calls rearrangeReturnTypes.-        rearrangeType' t = rearrangeType (take (arrayRank t) perm) t-        ts = map rearrangeType' origts--        setReturnType nesting t' =-          nesting { MapNest.nestingReturnType = t' }--fixupInputs :: [VName] -> [SOAC.Input] -> Maybe ([Int], [SOAC.Input])-fixupInputs inpIds inps =-  case mapMaybe inputRearrange $ filter exposable inps of-    perm:_ -> do inps' <- mapM (fixupInput (rearrangeReach perm) perm) inps-                 return (perm, inps')-    _    -> Nothing-  where exposable = (`elem` inpIds) . SOAC.inputArray--        inputRearrange (SOAC.Input ts _ _)-          | _ SOAC.:> SOAC.Rearrange _ perm <- SOAC.viewl ts = Just perm-        inputRearrange _                                     = Nothing--        fixupInput d perm inp-          | r <- SOAC.inputRank inp,-            r >= d =-              Just $ SOAC.addTransform (SOAC.Rearrange mempty $ take r perm) inp-          | otherwise = Nothing--pullReshape :: SOAC -> SOAC.ArrayTransforms -> TryFusion (SOAC, SOAC.ArrayTransforms)-pullReshape (SOAC.Screma _ form inps) ots-  | Just maplam <- Futhark.isMapSOAC form,-    SOAC.Reshape cs shape SOAC.:< ots' <- SOAC.viewf ots,-    all primType $ lambdaReturnType maplam = do-  let mapw' = case reverse $ newDims shape of-        []  -> intConst Int32 0-        d:_ -> d-      inputs' = map (SOAC.addTransform $ SOAC.ReshapeOuter cs shape) inps-      inputTypes = map SOAC.inputType inputs'--  let outersoac :: ([SOAC.Input] -> SOAC) -> (SubExp, [SubExp])-                -> TryFusion ([SOAC.Input] -> SOAC)-      outersoac inner (w, outershape) = do-        let addDims t = arrayOf t (Shape outershape) NoUniqueness-            retTypes = map addDims $ lambdaReturnType maplam--        ps <- forM inputTypes $ \inpt ->-          newParam "pullReshape_param" $-            stripArray (length shape-length outershape) inpt--        inner_body <- runBodyBinder $-          eBody [SOAC.toExp $ inner $ map (SOAC.identInput . paramIdent) ps]-        let inner_fun = Lambda { lambdaParams = ps-                               , lambdaReturnType = retTypes-                               , lambdaBody = inner_body-                               }-        return $ SOAC.Screma w $ Futhark.mapSOAC inner_fun--  op' <- foldM outersoac (SOAC.Screma mapw' $ Futhark.mapSOAC maplam) $-         zip (drop 1 $ reverse $ newDims shape) $-         drop 1 $ reverse $ drop 1 $ tails $ newDims shape-  return (op' inputs', ots')-pullReshape _ _ = fail "Cannot pull reshape"---- Tie it all together in exposeInputs (for making inputs to a--- consumer available) and pullOutputTransforms (for moving--- output-transforms of a producer to its inputs instead).--exposeInputs :: [VName] -> FusedKer-             -> TryFusion (FusedKer, SOAC.ArrayTransforms)-exposeInputs inpIds ker =-  (exposeInputs' =<< pushRearrange') <|>-  (exposeInputs' =<< pullRearrange') <|>-  exposeInputs' ker-  where ot = outputTransform ker--        pushRearrange' = do-          (soac', ot') <- pushRearrange inpIds (fsoac ker) ot-          return ker { fsoac = soac'-                     , outputTransform = ot'-                     }--        pullRearrange' = do-          (soac',ot') <- pullRearrange (fsoac ker) ot-          unless (SOAC.nullTransforms ot') $-            fail "pullRearrange was not enough"-          return ker { fsoac = soac'-                     , outputTransform = SOAC.noTransforms-                     }--        exposeInputs' ker' =-          case commonTransforms inpIds $ inputs ker' of-            (ot', inps') | all exposed inps' ->-              return (ker' { fsoac = inps' `SOAC.setInputs` fsoac ker'}, ot')-            _ -> fail "Cannot expose"--        exposed (SOAC.Input ts _ _)-          | SOAC.nullTransforms ts = True-        exposed inp = SOAC.inputArray inp `notElem` inpIds--outputTransformPullers :: [SOAC -> SOAC.ArrayTransforms -> TryFusion (SOAC, SOAC.ArrayTransforms)]-outputTransformPullers = [pullRearrange, pullReshape]--pullOutputTransforms :: SOAC -> SOAC.ArrayTransforms-                     -> TryFusion (SOAC, SOAC.ArrayTransforms)-pullOutputTransforms = attempt outputTransformPullers-  where attempt [] _ _ = fail "Cannot pull anything"-        attempt (p:ps) soac ots = do-          (soac',ots') <- p soac ots-          if SOAC.nullTransforms ots' then return (soac', SOAC.noTransforms)-          else pullOutputTransforms soac' ots' <|> return (soac', ots')-          <|> attempt ps soac ots+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeFamilies #-}++module Futhark.Optimise.Fusion.LoopKernel+  ( FusedKer (..),+    newKernel,+    inputs,+    setInputs,+    arrInputs,+    transformOutput,+    attemptFusion,+    SOAC,+    MapNest,+  )+where++import Control.Applicative+import Control.Arrow (first)+import Control.Monad+import Control.Monad.Reader+import Control.Monad.State+import Data.List (find, tails, (\\))+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Data.Set as S+import qualified Futhark.Analysis.HORep.MapNest as MapNest+import qualified Futhark.Analysis.HORep.SOAC as SOAC+import Futhark.Construct+import Futhark.IR.SOACS hiding (SOAC (..))+import qualified Futhark.IR.SOACS as Futhark+import Futhark.Optimise.Fusion.Composing+import Futhark.Pass.ExtractKernels.ISRWIM (rwimPossible)+import Futhark.Transform.Rename (renameLambda)+import Futhark.Transform.Substitute+import Futhark.Util (splitAt3)++newtype TryFusion a+  = TryFusion+      ( ReaderT+          (Scope SOACS)+          (StateT VNameSource Maybe)+          a+      )+  deriving+    ( Functor,+      Applicative,+      Alternative,+      Monad,+      MonadFail,+      MonadFreshNames,+      HasScope SOACS,+      LocalScope SOACS+    )++tryFusion ::+  MonadFreshNames m =>+  TryFusion a ->+  Scope SOACS ->+  m (Maybe a)+tryFusion (TryFusion m) types = modifyNameSource $ \src ->+  case runStateT (runReaderT m types) src of+    Just (x, src') -> (Just x, src')+    Nothing -> (Nothing, src)++liftMaybe :: Maybe a -> TryFusion a+liftMaybe Nothing = fail "Nothing"+liftMaybe (Just x) = return x++type SOAC = SOAC.SOAC SOACS++type MapNest = MapNest.MapNest SOACS++-- XXX: This function is very gross.+transformOutput ::+  SOAC.ArrayTransforms ->+  [VName] ->+  [Ident] ->+  Binder SOACS ()+transformOutput ts names = descend ts+  where+    descend ts' validents =+      case SOAC.viewf ts' of+        SOAC.EmptyF ->+          forM_ (zip names validents) $ \(k, valident) ->+            letBindNames [k] $ BasicOp $ SubExp $ Var $ identName valident+        t SOAC.:< ts'' -> do+          let (es, css) = unzip $ map (applyTransform t) validents+              mkPat (Ident nm tp) = Pattern [] [PatElem nm tp]+          opts <- concat <$> mapM primOpType es+          newIds <- forM (zip names opts) $ \(k, opt) ->+            newIdent (baseString k) opt+          forM_ (zip3 css newIds es) $ \(cs, ids, e) ->+            certifying cs $ letBind (mkPat ids) (BasicOp e)+          descend ts'' newIds++applyTransform :: SOAC.ArrayTransform -> Ident -> (BasicOp, Certificates)+applyTransform (SOAC.Rearrange cs perm) v =+  (Rearrange perm' $ identName v, cs)+  where+    perm' = perm ++ drop (length perm) [0 .. arrayRank (identType v) -1]+applyTransform (SOAC.Reshape cs shape) v =+  (Reshape shape $ identName v, cs)+applyTransform (SOAC.ReshapeOuter cs shape) v =+  let shapes = reshapeOuter shape 1 $ arrayShape $ identType v+   in (Reshape shapes $ identName v, cs)+applyTransform (SOAC.ReshapeInner cs shape) v =+  let shapes = reshapeInner shape 1 $ arrayShape $ identType v+   in (Reshape shapes $ identName v, cs)+applyTransform (SOAC.Replicate cs n) v =+  (Replicate n $ Var $ identName v, cs)++inputToOutput :: SOAC.Input -> Maybe (SOAC.ArrayTransform, SOAC.Input)+inputToOutput (SOAC.Input ts ia iat) =+  case SOAC.viewf ts of+    t SOAC.:< ts' -> Just (t, SOAC.Input ts' ia iat)+    SOAC.EmptyF -> Nothing++data FusedKer = FusedKer+  { -- | the SOAC expression, e.g., mapT( f(a,b), x, y )+    fsoac :: SOAC,+    -- | Variables used in in-place updates in the kernel itself, as+    -- well as on the path to the kernel from the current position.+    -- This is used to avoid fusion that would violate in-place+    -- restrictions.+    inplace :: Names,+    -- | whether at least a fusion has been performed.+    fusedVars :: [VName],+    -- | The set of variables that were consumed by the SOACs+    -- contributing to this kernel.  Note that, by the type rules, the+    -- final SOAC may actually consume _more_ than its original+    -- contributors, which implies the need for 'Copy' expressions.+    fusedConsumed :: Names,+    -- | The names in scope at the kernel.+    kernelScope :: Scope SOACS,+    outputTransform :: SOAC.ArrayTransforms,+    outNames :: [VName],+    kerAux :: StmAux ()+  }+  deriving (Show)++newKernel :: StmAux () -> SOAC -> Names -> [VName] -> Scope SOACS -> FusedKer+newKernel aux soac consumed out_nms scope =+  FusedKer+    { fsoac = soac,+      inplace = consumed,+      fusedVars = [],+      fusedConsumed = consumed,+      outputTransform = SOAC.noTransforms,+      outNames = out_nms,+      kernelScope = scope,+      kerAux = aux+    }++arrInputs :: FusedKer -> S.Set VName+arrInputs = S.fromList . map SOAC.inputArray . inputs++inputs :: FusedKer -> [SOAC.Input]+inputs = SOAC.inputs . fsoac++setInputs :: [SOAC.Input] -> FusedKer -> FusedKer+setInputs inps ker = ker {fsoac = inps `SOAC.setInputs` fsoac ker}++tryOptimizeSOAC ::+  Names ->+  [VName] ->+  SOAC ->+  Names ->+  FusedKer ->+  TryFusion FusedKer+tryOptimizeSOAC unfus_nms outVars soac consumed ker = do+  (soac', ots) <- optimizeSOAC Nothing soac mempty+  let ker' = map (addInitialTransformIfRelevant ots) (inputs ker) `setInputs` ker+      outIdents = zipWith Ident outVars $ SOAC.typeOf soac'+      ker'' = fixInputTypes outIdents ker'+  applyFusionRules unfus_nms outVars soac' consumed ker''+  where+    addInitialTransformIfRelevant ots inp+      | SOAC.inputArray inp `elem` outVars =+        SOAC.addInitialTransforms ots inp+      | otherwise =+        inp++tryOptimizeKernel ::+  Names ->+  [VName] ->+  SOAC ->+  Names ->+  FusedKer ->+  TryFusion FusedKer+tryOptimizeKernel unfus_nms outVars soac consumed ker = do+  ker' <- optimizeKernel (Just outVars) ker+  applyFusionRules unfus_nms outVars soac consumed ker'++tryExposeInputs ::+  Names ->+  [VName] ->+  SOAC ->+  Names ->+  FusedKer ->+  TryFusion FusedKer+tryExposeInputs unfus_nms outVars soac consumed ker = do+  (ker', ots) <- exposeInputs outVars ker+  if SOAC.nullTransforms ots+    then fuseSOACwithKer unfus_nms outVars soac consumed ker'+    else do+      (soac', ots') <- pullOutputTransforms soac ots+      let outIdents = zipWith Ident outVars $ SOAC.typeOf soac'+          ker'' = fixInputTypes outIdents ker'+      if SOAC.nullTransforms ots'+        then applyFusionRules unfus_nms outVars soac' consumed ker''+        else fail "tryExposeInputs could not pull SOAC transforms"++fixInputTypes :: [Ident] -> FusedKer -> FusedKer+fixInputTypes outIdents ker =+  ker {fsoac = fixInputTypes' $ fsoac ker}+  where+    fixInputTypes' soac =+      map fixInputType (SOAC.inputs soac) `SOAC.setInputs` soac+    fixInputType (SOAC.Input ts v _)+      | Just v' <- find ((== v) . identName) outIdents =+        SOAC.Input ts v $ identType v'+    fixInputType inp = inp++applyFusionRules ::+  Names ->+  [VName] ->+  SOAC ->+  Names ->+  FusedKer ->+  TryFusion FusedKer+applyFusionRules unfus_nms outVars soac consumed ker =+  tryOptimizeSOAC unfus_nms outVars soac consumed ker+    <|> tryOptimizeKernel unfus_nms outVars soac consumed ker+    <|> fuseSOACwithKer unfus_nms outVars soac consumed ker+    <|> tryExposeInputs unfus_nms outVars soac consumed ker++attemptFusion ::+  MonadFreshNames m =>+  Names ->+  [VName] ->+  SOAC ->+  Names ->+  FusedKer ->+  m (Maybe FusedKer)+attemptFusion unfus_nms outVars soac consumed ker =+  fmap removeUnusedParamsFromKer+    <$> tryFusion+      (applyFusionRules unfus_nms outVars soac consumed ker)+      (kernelScope ker)++removeUnusedParamsFromKer :: FusedKer -> FusedKer+removeUnusedParamsFromKer ker =+  case soac of+    SOAC.Screma {} -> ker {fsoac = soac'}+    _ -> ker+  where+    soac = fsoac ker+    l = SOAC.lambda soac+    inps = SOAC.inputs soac+    (l', inps') = removeUnusedParams l inps+    soac' =+      l'+        `SOAC.setLambda` (inps' `SOAC.setInputs` soac)++removeUnusedParams :: Lambda -> [SOAC.Input] -> (Lambda, [SOAC.Input])+removeUnusedParams l inps =+  (l {lambdaParams = ps'}, inps')+  where+    pInps = zip (lambdaParams l) inps+    (ps', inps') = case (unzip $ filter (used . fst) pInps, pInps) of+      (([], []), (p, inp) : _) -> ([p], [inp])+      ((ps_, inps_), _) -> (ps_, inps_)+    used p = paramName p `nameIn` freeVars+    freeVars = freeIn $ lambdaBody l++-- | Check that the consumer uses at least one output of the producer+-- unmodified.+mapFusionOK :: [VName] -> FusedKer -> Bool+mapFusionOK outVars ker = any (`elem` inpIds) outVars+  where+    inpIds = mapMaybe SOAC.isVarishInput (inputs ker)++-- | Check that the consumer uses all the outputs of the producer unmodified.+mapWriteFusionOK :: [VName] -> FusedKer -> Bool+mapWriteFusionOK outVars ker = all (`elem` inpIds) outVars+  where+    inpIds = mapMaybe SOAC.isVarishInput (inputs ker)++-- | The brain of this module: Fusing a SOAC with a Kernel.+fuseSOACwithKer ::+  Names ->+  [VName] ->+  SOAC ->+  Names ->+  FusedKer ->+  TryFusion FusedKer+fuseSOACwithKer unfus_set outVars soac_p soac_p_consumed ker = do+  -- We are fusing soac_p into soac_c, i.e, the output of soac_p is going+  -- into soac_c.+  let soac_c = fsoac ker+      inp_p_arr = SOAC.inputs soac_p+      horizFuse =+        unfus_set /= mempty+          && SOAC.width soac_p == SOAC.width soac_c+      inp_c_arr = SOAC.inputs soac_c+      lam_p = SOAC.lambda soac_p+      lam_c = SOAC.lambda soac_c+      w = SOAC.width soac_p+      returned_outvars = filter (`nameIn` unfus_set) outVars+      success res_outnms res_soac = do+        let fusedVars_new = fusedVars ker ++ outVars+        -- Avoid name duplication, because the producer lambda is not+        -- removed from the program until much later.+        uniq_lam <- renameLambda $ SOAC.lambda res_soac+        return $+          ker+            { fsoac = uniq_lam `SOAC.setLambda` res_soac,+              fusedVars = fusedVars_new,+              inplace = inplace ker <> soac_p_consumed,+              fusedConsumed = fusedConsumed ker <> soac_p_consumed,+              outNames = res_outnms+            }++  outPairs <- forM (zip outVars $ map rowType $ SOAC.typeOf soac_p) $ \(outVar, t) -> do+    outVar' <- newVName $ baseString outVar ++ "_elem"+    return (outVar, Ident outVar' t)++  let mapLikeFusionCheck =+        let (res_lam, new_inp) = fuseMaps unfus_set lam_p inp_p_arr outPairs lam_c inp_c_arr+            (extra_nms, extra_rtps) =+              unzip $+                filter ((`nameIn` unfus_set) . fst) $+                  zip outVars $ map (stripArray 1) $ SOAC.typeOf soac_p+            res_lam' = res_lam {lambdaReturnType = lambdaReturnType res_lam ++ extra_rtps}+         in (extra_nms, res_lam', new_inp)++  when (horizFuse && not (SOAC.nullTransforms $ outputTransform ker)) $+    fail "Horizontal fusion is invalid in the presence of output transforms."++  case (soac_c, soac_p) of+    _ | SOAC.width soac_p /= SOAC.width soac_c -> fail "SOAC widths must match."+    ( SOAC.Screma _ (ScremaForm scans_c reds_c _) _,+      SOAC.Screma _ (ScremaForm scans_p reds_p _) _+      )+        | mapFusionOK (drop (Futhark.scanResults scans_p + Futhark.redResults reds_p) outVars) ker+            || horizFuse -> do+          let red_nes_p = concatMap redNeutral reds_p+              red_nes_c = concatMap redNeutral reds_c+              scan_nes_p = concatMap scanNeutral scans_p+              scan_nes_c = concatMap scanNeutral scans_c+              (res_lam', new_inp) =+                fuseRedomap+                  unfus_set+                  outVars+                  lam_p+                  scan_nes_p+                  red_nes_p+                  inp_p_arr+                  outPairs+                  lam_c+                  scan_nes_c+                  red_nes_c+                  inp_c_arr+              (soac_p_scanout, soac_p_redout, _soac_p_mapout) =+                splitAt3 (length scan_nes_p) (length red_nes_p) outVars+              (soac_c_scanout, soac_c_redout, soac_c_mapout) =+                splitAt3 (length scan_nes_c) (length red_nes_c) $ outNames ker+              unfus_arrs = returned_outvars \\ (soac_p_scanout ++ soac_p_redout)+          success+            ( soac_p_scanout ++ soac_c_scanout+                ++ soac_p_redout+                ++ soac_c_redout+                ++ soac_c_mapout+                ++ unfus_arrs+            )+            $ SOAC.Screma+              w+              (ScremaForm (scans_p ++ scans_c) (reds_p ++ reds_c) res_lam')+              new_inp++    ------------------+    -- Scatter fusion --+    ------------------++    -- Map-Scatter fusion.+    --+    -- The 'inplace' mechanism for kernels already takes care of+    -- checking that the Scatter is not writing to any array used in+    -- the Map.+    ( SOAC.Scatter _len _lam _ivs dests,+      SOAC.Screma _ form _+      )+        | isJust $ isMapSOAC form,+          -- 1. all arrays produced by the map are ONLY used (consumed)+          --    by the scatter, i.e., not used elsewhere.+          not (any (`nameIn` unfus_set) outVars),+          -- 2. all arrays produced by the map are input to the scatter.+          mapWriteFusionOK outVars ker -> do+          let (extra_nms, res_lam', new_inp) = mapLikeFusionCheck+          success (outNames ker ++ extra_nms) $+            SOAC.Scatter w res_lam' new_inp dests++    -- Map-Hist fusion.+    --+    -- The 'inplace' mechanism for kernels already takes care of+    -- checking that the Hist is not writing to any array used in+    -- the Map.+    ( SOAC.Hist _ ops _ _,+      SOAC.Screma _ form _+      )+        | isJust $ isMapSOAC form,+          -- 1. all arrays produced by the map are ONLY used (consumed)+          --    by the hist, i.e., not used elsewhere.+          not (any (`nameIn` unfus_set) outVars),+          -- 2. all arrays produced by the map are input to the scatter.+          mapWriteFusionOK outVars ker -> do+          let (extra_nms, res_lam', new_inp) = mapLikeFusionCheck+          success (outNames ker ++ extra_nms) $+            SOAC.Hist w ops res_lam' new_inp++    -- Hist-Hist fusion+    ( SOAC.Hist _ ops_c _ _,+      SOAC.Hist _ ops_p _ _+      )+        | horizFuse -> do+          let p_num_buckets = length ops_p+              c_num_buckets = length ops_c+              (body_p, body_c) = (lambdaBody lam_p, lambdaBody lam_c)+              body' =+                Body+                  { bodyDec = bodyDec body_p, -- body_p and body_c have the same lores+                    bodyStms = bodyStms body_p <> bodyStms body_c,+                    bodyResult =+                      take c_num_buckets (bodyResult body_c)+                        ++ take p_num_buckets (bodyResult body_p)+                        ++ drop c_num_buckets (bodyResult body_c)+                        ++ drop p_num_buckets (bodyResult body_p)+                  }+              lam' =+                Lambda+                  { lambdaParams = lambdaParams lam_c ++ lambdaParams lam_p,+                    lambdaBody = body',+                    lambdaReturnType =+                      replicate (c_num_buckets + p_num_buckets) (Prim int64)+                        ++ drop c_num_buckets (lambdaReturnType lam_c)+                        ++ drop p_num_buckets (lambdaReturnType lam_p)+                  }+          success (outNames ker ++ returned_outvars) $+            SOAC.Hist w (ops_c <> ops_p) lam' (inp_c_arr <> inp_p_arr)++    -- Scatter-write fusion.+    ( SOAC.Scatter _len2 _lam_c ivs2 as2,+      SOAC.Scatter _len_p _lam_p ivs_p as_p+      )+        | horizFuse -> do+          let zipW xs ys = ys_p ++ xs_p ++ ys2 ++ xs2+                where+                  lenx = length xs `div` 2+                  xs_p = take lenx xs+                  xs2 = drop lenx xs+                  leny = length ys `div` 2+                  ys_p = take leny ys+                  ys2 = drop leny ys+          let (body_p, body2) = (lambdaBody lam_p, lambdaBody lam_c)+          let body' =+                Body+                  { bodyDec = bodyDec body_p, -- body_p and body2 have the same lores+                    bodyStms = bodyStms body_p <> bodyStms body2,+                    bodyResult = zipW (bodyResult body_p) (bodyResult body2)+                  }+          let lam' =+                Lambda+                  { lambdaParams = lambdaParams lam_p ++ lambdaParams lam_c,+                    lambdaBody = body',+                    lambdaReturnType = zipW (lambdaReturnType lam_p) (lambdaReturnType lam_c)+                  }+          success (outNames ker ++ returned_outvars) $+            SOAC.Scatter w lam' (ivs_p ++ ivs2) (as2 ++ as_p)+    (SOAC.Scatter {}, _) ->+      fail "Cannot fuse a write with anything else than a write or a map"+    (_, SOAC.Scatter {}) ->+      fail "Cannot fuse a write with anything else than a write or a map"+    ----------------------------+    -- Stream-Stream Fusions: --+    ----------------------------+    (SOAC.Stream _ Sequential {} _ _, SOAC.Stream _ form_p@Sequential {} _ _)+      | mapFusionOK (drop (length $ getStreamAccums form_p) outVars) ker || horizFuse -> do+        -- fuse two SEQUENTIAL streams+        (res_nms, res_stream) <- fuseStreamHelper (outNames ker) unfus_set outVars outPairs soac_c soac_p+        success res_nms res_stream+    (SOAC.Stream _ Sequential {} _ _, SOAC.Stream _ Sequential {} _ _) ->+      fail "Fusion conditions not met for two SEQ streams!"+    (SOAC.Stream _ Sequential {} _ _, SOAC.Stream {}) ->+      fail "Cannot fuse a parallel with a sequential Stream!"+    (SOAC.Stream {}, SOAC.Stream _ Sequential {} _ _) ->+      fail "Cannot fuse a parallel with a sequential Stream!"+    (SOAC.Stream {}, SOAC.Stream _ form_p _ _)+      | mapFusionOK (drop (length $ getStreamAccums form_p) outVars) ker || horizFuse -> do+        -- fuse two PARALLEL streams+        (res_nms, res_stream) <- fuseStreamHelper (outNames ker) unfus_set outVars outPairs soac_c soac_p+        success res_nms res_stream+    (SOAC.Stream {}, SOAC.Stream {}) ->+      fail "Fusion conditions not met for two PAR streams!"+    -------------------------------------------------------------------+    --- If one is a stream, translate the other to a stream as well.---+    --- This does not get in trouble (infinite computation) because ---+    ---   scan's translation to Stream introduces a hindrance to    ---+    ---   (horizontal fusion), hence repeated application is for the---+    ---   moment impossible. However, if with a dependence-graph rep---+    ---   we could run in an infinite recursion, i.e., repeatedly   ---+    ---   fusing map o scan into an infinity of Stream levels!      ---+    -------------------------------------------------------------------+    (SOAC.Stream _ form2 _ _, _) -> do+      -- If this rule is matched then soac_p is NOT a stream.+      -- To fuse a stream kernel, we transform soac_p to a stream, which+      -- borrows the sequential/parallel property of the soac_c Stream,+      -- and recursively perform stream-stream fusion.+      (soac_p', newacc_ids) <- SOAC.soacToStream soac_p+      soac_p'' <- case form2 of+        Sequential {} -> toSeqStream soac_p'+        _ -> return soac_p'+      if soac_p' == soac_p+        then fail "SOAC could not be turned into stream."+        else fuseSOACwithKer unfus_set (map identName newacc_ids ++ outVars) soac_p'' soac_p_consumed ker+    (_, SOAC.Screma _ form _) | Just _ <- Futhark.isScanSOAC form -> do+      -- A Scan soac can be currently only fused as a (sequential) stream,+      -- hence it is first translated to a (sequential) Stream and then+      -- fusion with a kernel is attempted.+      (soac_p', newacc_ids) <- SOAC.soacToStream soac_p+      if soac_p' /= soac_p+        then fuseSOACwithKer unfus_set (map identName newacc_ids ++ outVars) soac_p' soac_p_consumed ker+        else fail "SOAC could not be turned into stream."+    (_, SOAC.Stream _ form_p _ _) -> do+      -- If it reached this case then soac_c is NOT a Stream kernel,+      -- hence transform the kernel's soac to a stream and attempt+      -- stream-stream fusion recursivelly.+      -- The newly created stream corresponding to soac_c borrows the+      -- sequential/parallel property of the soac_p stream.+      (soac_c', newacc_ids) <- SOAC.soacToStream soac_c+      when (soac_c' == soac_c) $ fail "SOAC could not be turned into stream."+      soac_c'' <- case form_p of+        Sequential _ -> toSeqStream soac_c'+        _ -> return soac_c'++      fuseSOACwithKer unfus_set outVars soac_p soac_p_consumed $+        ker {fsoac = soac_c'', outNames = map identName newacc_ids ++ outNames ker}++    ---------------------------------+    --- DEFAULT, CANNOT FUSE CASE ---+    ---------------------------------+    _ -> fail "Cannot fuse"++getStreamOrder :: StreamForm lore -> StreamOrd+getStreamOrder (Parallel o _ _ _) = o+getStreamOrder (Sequential _) = InOrder++fuseStreamHelper ::+  [VName] ->+  Names ->+  [VName] ->+  [(VName, Ident)] ->+  SOAC ->+  SOAC ->+  TryFusion ([VName], SOAC)+fuseStreamHelper+  out_kernms+  unfus_set+  outVars+  outPairs+  (SOAC.Stream w2 form2 lam2 inp2_arr)+  (SOAC.Stream _ form1 lam1 inp1_arr) =+    if getStreamOrder form2 /= getStreamOrder form1+      then fail "fusion conditions not met!"+      else do+        -- very similar to redomap o redomap composition, but need+        -- to remove first the `chunk' parameters of streams'+        -- lambdas and put them in the resulting stream lambda.+        let nes1 = getStreamAccums form1+            chunk1 = head $ lambdaParams lam1+            chunk2 = head $ lambdaParams lam2+            hmnms = M.fromList [(paramName chunk2, paramName chunk1)]+            lam20 = substituteNames hmnms lam2+            lam1' = lam1 {lambdaParams = tail $ lambdaParams lam1}+            lam2' = lam20 {lambdaParams = tail $ lambdaParams lam20}+            (res_lam', new_inp) =+              fuseRedomap+                unfus_set+                outVars+                lam1'+                []+                nes1+                inp1_arr+                outPairs+                lam2'+                []+                (getStreamAccums form2)+                inp2_arr+            res_lam'' = res_lam' {lambdaParams = chunk1 : lambdaParams res_lam'}+            unfus_accs = take (length nes1) outVars+            unfus_arrs = filter (`nameIn` unfus_set) outVars+        res_form <- mergeForms form2 form1+        return+          ( unfus_accs ++ out_kernms ++ unfus_arrs,+            SOAC.Stream w2 res_form res_lam'' new_inp+          )+    where+      mergeForms (Sequential acc2) (Sequential acc1) = return $ Sequential (acc1 ++ acc2)+      mergeForms (Parallel _ comm2 lam2r acc2) (Parallel o1 comm1 lam1r acc1) =+        return $ Parallel o1 (comm1 <> comm2) (mergeReduceOps lam1r lam2r) (acc1 ++ acc2)+      mergeForms _ _ = fail "Fusing sequential to parallel stream disallowed!"+fuseStreamHelper _ _ _ _ _ _ = fail "Cannot Fuse Streams!"++-- | If a Stream is passed as argument then it converts it to a+--   Sequential Stream; Otherwise it FAILS!+toSeqStream :: SOAC -> TryFusion SOAC+toSeqStream s@(SOAC.Stream _ (Sequential _) _ _) = return s+toSeqStream (SOAC.Stream w (Parallel _ _ _ acc) l inps) =+  return $ SOAC.Stream w (Sequential acc) l inps+toSeqStream _ = fail "toSeqStream expects a stream, but given a SOAC."++-- Here follows optimizations and transforms to expose fusability.++optimizeKernel :: Maybe [VName] -> FusedKer -> TryFusion FusedKer+optimizeKernel inp ker = do+  (soac, resTrans) <- optimizeSOAC inp (fsoac ker) startTrans+  return $+    ker+      { fsoac = soac,+        outputTransform = resTrans+      }+  where+    startTrans = outputTransform ker++optimizeSOAC ::+  Maybe [VName] ->+  SOAC ->+  SOAC.ArrayTransforms ->+  TryFusion (SOAC, SOAC.ArrayTransforms)+optimizeSOAC inp soac os = do+  res <- foldM comb (False, soac, os) optimizations+  case res of+    (False, _, _) -> fail "No optimisation applied"+    (True, soac', os') -> return (soac', os')+  where+    comb (changed, soac', os') f =+      do+        (soac'', os'') <- f inp soac' os+        return (True, soac'', os'')+        <|> return (changed, soac', os')++type Optimization =+  Maybe [VName] ->+  SOAC ->+  SOAC.ArrayTransforms ->+  TryFusion (SOAC, SOAC.ArrayTransforms)++optimizations :: [Optimization]+optimizations = [iswim]++iswim ::+  Maybe [VName] ->+  SOAC ->+  SOAC.ArrayTransforms ->+  TryFusion (SOAC, SOAC.ArrayTransforms)+iswim _ (SOAC.Screma w form arrs) ots+  | Just [Futhark.Scan scan_fun nes] <- Futhark.isScanSOAC form,+    Just (map_pat, map_cs, map_w, map_fun) <- rwimPossible scan_fun,+    Just nes_names <- mapM subExpVar nes = do+    let nes_idents = zipWith Ident nes_names $ lambdaReturnType scan_fun+        map_nes = map SOAC.identInput nes_idents+        map_arrs' = map_nes ++ map (SOAC.transposeInput 0 1) arrs+        (scan_acc_params, scan_elem_params) =+          splitAt (length arrs) $ lambdaParams scan_fun+        map_params =+          map removeParamOuterDim scan_acc_params+            ++ map (setParamOuterDimTo w) scan_elem_params+        map_rettype = map (`setOuterSize` w) $ lambdaReturnType scan_fun++        scan_params = lambdaParams map_fun+        scan_body = lambdaBody map_fun+        scan_rettype = lambdaReturnType map_fun+        scan_fun' = Lambda scan_params scan_body scan_rettype+        nes' = map Var $ take (length map_nes) $ map paramName map_params+        arrs' = drop (length map_nes) $ map paramName map_params++    scan_form <- scanSOAC [Futhark.Scan scan_fun' nes']++    let map_body =+          mkBody+            ( oneStm $+                Let (setPatternOuterDimTo w map_pat) (defAux ()) $+                  Op $ Futhark.Screma w scan_form arrs'+            )+            $ map Var $ patternNames map_pat+        map_fun' = Lambda map_params map_body map_rettype+        perm = case lambdaReturnType map_fun of+          [] -> []+          t : _ -> 1 : 0 : [2 .. arrayRank t]++    return+      ( SOAC.Screma map_w (ScremaForm [] [] map_fun') map_arrs',+        ots SOAC.|> SOAC.Rearrange map_cs perm+      )+iswim _ _ _ =+  fail "ISWIM does not apply."++removeParamOuterDim :: LParam -> LParam+removeParamOuterDim param =+  let t = rowType $ paramType param+   in param {paramDec = t}++setParamOuterDimTo :: SubExp -> LParam -> LParam+setParamOuterDimTo w param =+  let t = paramType param `setOuterSize` w+   in param {paramDec = t}++setPatternOuterDimTo :: SubExp -> Pattern -> Pattern+setPatternOuterDimTo w = fmap (`setOuterSize` w)++-- Now for fiddling with transpositions...++commonTransforms ::+  [VName] ->+  [SOAC.Input] ->+  (SOAC.ArrayTransforms, [SOAC.Input])+commonTransforms interesting inps = commonTransforms' inps'+  where+    inps' =+      [ (SOAC.inputArray inp `elem` interesting, inp)+        | inp <- inps+      ]++commonTransforms' :: [(Bool, SOAC.Input)] -> (SOAC.ArrayTransforms, [SOAC.Input])+commonTransforms' inps =+  case foldM inspect (Nothing, []) inps of+    Just (Just mot, inps') -> first (mot SOAC.<|) $ commonTransforms' $ reverse inps'+    _ -> (SOAC.noTransforms, map snd inps)+  where+    inspect (mot, prev) (True, inp) =+      case (mot, inputToOutput inp) of+        (Nothing, Just (ot, inp')) -> Just (Just ot, (True, inp') : prev)+        (Just ot1, Just (ot2, inp'))+          | ot1 == ot2 -> Just (Just ot2, (True, inp') : prev)+        _ -> Nothing+    inspect (mot, prev) inp = Just (mot, inp : prev)++mapDepth :: MapNest -> Int+mapDepth (MapNest.MapNest _ lam levels _) =+  min resDims (length levels) + 1+  where+    resDims = minDim $ case levels of+      [] -> lambdaReturnType lam+      nest : _ -> MapNest.nestingReturnType nest+    minDim [] = 0+    minDim (t : ts) = foldl min (arrayRank t) $ map arrayRank ts++pullRearrange ::+  SOAC ->+  SOAC.ArrayTransforms ->+  TryFusion (SOAC, SOAC.ArrayTransforms)+pullRearrange soac ots = do+  nest <- liftMaybe =<< MapNest.fromSOAC soac+  SOAC.Rearrange cs perm SOAC.:< ots' <- return $ SOAC.viewf ots+  if rearrangeReach perm <= mapDepth nest+    then do+      let -- Expand perm to cover the full extent of the input dimensionality+          perm' inp = take r perm ++ [length perm .. r -1]+            where+              r = SOAC.inputRank inp+          addPerm inp = SOAC.addTransform (SOAC.Rearrange cs $ perm' inp) inp+          inputs' = map addPerm $ MapNest.inputs nest+      soac' <-+        MapNest.toSOAC $+          inputs' `MapNest.setInputs` rearrangeReturnTypes nest perm+      return (soac', ots')+    else fail "Cannot pull transpose"++pushRearrange ::+  [VName] ->+  SOAC ->+  SOAC.ArrayTransforms ->+  TryFusion (SOAC, SOAC.ArrayTransforms)+pushRearrange inpIds soac ots = do+  nest <- liftMaybe =<< MapNest.fromSOAC soac+  (perm, inputs') <- liftMaybe $ fixupInputs inpIds $ MapNest.inputs nest+  if rearrangeReach perm <= mapDepth nest+    then do+      let invertRearrange = SOAC.Rearrange mempty $ rearrangeInverse perm+      soac' <-+        MapNest.toSOAC $+          inputs'+            `MapNest.setInputs` rearrangeReturnTypes nest perm+      return (soac', invertRearrange SOAC.<| ots)+    else fail "Cannot push transpose"++-- | Actually also rearranges indices.+rearrangeReturnTypes :: MapNest -> [Int] -> MapNest+rearrangeReturnTypes nest@(MapNest.MapNest w body nestings inps) perm =+  MapNest.MapNest+    w+    body+    ( zipWith+        setReturnType+        nestings+        $ drop 1 $ iterate (map rowType) ts+    )+    inps+  where+    origts = MapNest.typeOf nest+    -- The permutation may be deeper than the rank of the type,+    -- but it is required that it is an identity permutation+    -- beyond that.  This is supposed to be checked as an+    -- invariant by whoever calls rearrangeReturnTypes.+    rearrangeType' t = rearrangeType (take (arrayRank t) perm) t+    ts = map rearrangeType' origts++    setReturnType nesting t' =+      nesting {MapNest.nestingReturnType = t'}++fixupInputs :: [VName] -> [SOAC.Input] -> Maybe ([Int], [SOAC.Input])+fixupInputs inpIds inps =+  case mapMaybe inputRearrange $ filter exposable inps of+    perm : _ -> do+      inps' <- mapM (fixupInput (rearrangeReach perm) perm) inps+      return (perm, inps')+    _ -> Nothing+  where+    exposable = (`elem` inpIds) . SOAC.inputArray++    inputRearrange (SOAC.Input ts _ _)+      | _ SOAC.:> SOAC.Rearrange _ perm <- SOAC.viewl ts = Just perm+    inputRearrange _ = Nothing++    fixupInput d perm inp+      | r <- SOAC.inputRank inp,+        r >= d =+        Just $ SOAC.addTransform (SOAC.Rearrange mempty $ take r perm) inp+      | otherwise = Nothing++pullReshape :: SOAC -> SOAC.ArrayTransforms -> TryFusion (SOAC, SOAC.ArrayTransforms)+pullReshape (SOAC.Screma _ form inps) ots+  | Just maplam <- Futhark.isMapSOAC form,+    SOAC.Reshape cs shape SOAC.:< ots' <- SOAC.viewf ots,+    all primType $ lambdaReturnType maplam = do+    let mapw' = case reverse $ newDims shape of+          [] -> intConst Int64 0+          d : _ -> d+        inputs' = map (SOAC.addTransform $ SOAC.ReshapeOuter cs shape) inps+        inputTypes = map SOAC.inputType inputs'++    let outersoac ::+          ([SOAC.Input] -> SOAC) ->+          (SubExp, [SubExp]) ->+          TryFusion ([SOAC.Input] -> SOAC)+        outersoac inner (w, outershape) = do+          let addDims t = arrayOf t (Shape outershape) NoUniqueness+              retTypes = map addDims $ lambdaReturnType maplam++          ps <- forM inputTypes $ \inpt ->+            newParam "pullReshape_param" $+              stripArray (length shape - length outershape) inpt++          inner_body <-+            runBodyBinder $+              eBody [SOAC.toExp $ inner $ map (SOAC.identInput . paramIdent) ps]+          let inner_fun =+                Lambda+                  { lambdaParams = ps,+                    lambdaReturnType = retTypes,+                    lambdaBody = inner_body+                  }+          return $ SOAC.Screma w $ Futhark.mapSOAC inner_fun++    op' <-+      foldM outersoac (SOAC.Screma mapw' $ Futhark.mapSOAC maplam) $+        zip (drop 1 $ reverse $ newDims shape) $+          drop 1 $ reverse $ drop 1 $ tails $ newDims shape+    return (op' inputs', ots')+pullReshape _ _ = fail "Cannot pull reshape"++-- Tie it all together in exposeInputs (for making inputs to a+-- consumer available) and pullOutputTransforms (for moving+-- output-transforms of a producer to its inputs instead).++exposeInputs ::+  [VName] ->+  FusedKer ->+  TryFusion (FusedKer, SOAC.ArrayTransforms)+exposeInputs inpIds ker =+  (exposeInputs' =<< pushRearrange')+    <|> (exposeInputs' =<< pullRearrange')+    <|> exposeInputs' ker+  where+    ot = outputTransform ker++    pushRearrange' = do+      (soac', ot') <- pushRearrange inpIds (fsoac ker) ot+      return+        ker+          { fsoac = soac',+            outputTransform = ot'+          }++    pullRearrange' = do+      (soac', ot') <- pullRearrange (fsoac ker) ot+      unless (SOAC.nullTransforms ot') $+        fail "pullRearrange was not enough"+      return+        ker+          { fsoac = soac',+            outputTransform = SOAC.noTransforms+          }++    exposeInputs' ker' =+      case commonTransforms inpIds $ inputs ker' of+        (ot', inps')+          | all exposed inps' ->+            return (ker' {fsoac = inps' `SOAC.setInputs` fsoac ker'}, ot')+        _ -> fail "Cannot expose"++    exposed (SOAC.Input ts _ _)+      | SOAC.nullTransforms ts = True+    exposed inp = SOAC.inputArray inp `notElem` inpIds++outputTransformPullers :: [SOAC -> SOAC.ArrayTransforms -> TryFusion (SOAC, SOAC.ArrayTransforms)]+outputTransformPullers = [pullRearrange, pullReshape]++pullOutputTransforms ::+  SOAC ->+  SOAC.ArrayTransforms ->+  TryFusion (SOAC, SOAC.ArrayTransforms)+pullOutputTransforms = attempt outputTransformPullers+  where+    attempt [] _ _ = fail "Cannot pull anything"+    attempt (p : ps) soac ots =+      do+        (soac', ots') <- p soac ots+        if SOAC.nullTransforms ots'+          then return (soac', SOAC.noTransforms)+          else pullOutputTransforms soac' ots' <|> return (soac', ots')+        <|> attempt ps soac ots
src/Futhark/Optimise/InPlaceLowering.hs view
@@ -1,10 +1,11 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE UndecidableInstances #-}+ -- | This module implements an optimisation that moves in-place -- updates into/before loops where possible, with the end goal of -- minimising memory copies.  As an example, consider this program:@@ -63,21 +64,19 @@ -- FIXME: the implementation is not finished yet.  Specifically, not -- all of the above conditions are checked. module Futhark.Optimise.InPlaceLowering-       ( inPlaceLoweringKernels-       , inPlaceLoweringSeq-       )+  ( inPlaceLoweringKernels,+    inPlaceLoweringSeq,+  ) where  import Control.Monad.RWS import qualified Data.Map.Strict as M- import Futhark.Analysis.Alias+import Futhark.Binder import Futhark.IR.Aliases import Futhark.IR.Kernels import Futhark.IR.Seq (Seq) import Futhark.Optimise.InPlaceLowering.LowerIntoStm-import Futhark.MonadFreshNames-import Futhark.Binder import Futhark.Pass  -- | Apply the in-place lowering optimisation to the given program.@@ -89,49 +88,62 @@ inPlaceLoweringSeq = inPlaceLowering pure lowerUpdate  -- | Apply the in-place lowering optimisation to the given program.-inPlaceLowering :: Constraints lore =>-                   OnOp lore -> LowerUpdate lore (ForwardingM lore)-                -> Pass lore lore+inPlaceLowering ::+  Constraints lore =>+  OnOp lore ->+  LowerUpdate lore (ForwardingM lore) ->+  Pass lore lore inPlaceLowering onOp lower =   Pass "In-place lowering" "Lower in-place updates into loops" $-  fmap removeProgAliases .-  intraproceduralTransformationWithConsts optimiseConsts optimiseFunDef .-  aliasAnalysis-  where optimiseConsts stms =-          modifyNameSource $ runForwardingM lower onOp $+    fmap removeProgAliases+      . intraproceduralTransformationWithConsts optimiseConsts optimiseFunDef+      . aliasAnalysis+  where+    optimiseConsts stms =+      modifyNameSource $+        runForwardingM lower onOp $           stmsFromList <$> optimiseStms (stmsToList stms) (pure ()) -        optimiseFunDef consts fundec =-          modifyNameSource $ runForwardingM lower onOp $-          descend (stmsToList consts) $ bindingFParams (funDefParams fundec) $ do-          body <- optimiseBody $ funDefBody fundec-          return $ fundec { funDefBody = body }+    optimiseFunDef consts fundec =+      modifyNameSource $+        runForwardingM lower onOp $+          descend (stmsToList consts) $+            bindingFParams (funDefParams fundec) $ do+              body <- optimiseBody $ funDefBody fundec+              return $ fundec {funDefBody = body} -        descend [] m = m-        descend (stm:stms) m = bindingStm stm $ descend stms m+    descend [] m = m+    descend (stm : stms) m = bindingStm stm $ descend stms m  type Constraints lore = (Bindable lore, CanBeAliased (Op lore)) -optimiseBody :: Constraints lore =>-                Body (Aliases lore) -> ForwardingM lore (Body (Aliases lore))+optimiseBody ::+  Constraints lore =>+  Body (Aliases lore) ->+  ForwardingM lore (Body (Aliases lore)) optimiseBody (Body als bnds res) = do-  bnds' <- deepen $ optimiseStms (stmsToList bnds) $-    mapM_ seen res+  bnds' <-+    deepen $+      optimiseStms (stmsToList bnds) $+        mapM_ seen res   return $ Body als (stmsFromList bnds') res-  where seen Constant{} = return ()-        seen (Var v)    = seenVar v+  where+    seen Constant {} = return ()+    seen (Var v) = seenVar v -optimiseStms :: Constraints lore =>-                [Stm (Aliases lore)] -> ForwardingM lore ()-             -> ForwardingM lore [Stm (Aliases lore)]+optimiseStms ::+  Constraints lore =>+  [Stm (Aliases lore)] ->+  ForwardingM lore () ->+  ForwardingM lore [Stm (Aliases lore)] optimiseStms [] m = m >> return []--optimiseStms (bnd:bnds) m = do+optimiseStms (bnd : bnds) m = do   (bnds', bup) <- tapBottomUp $ bindingStm bnd $ optimiseStms bnds m   bnd' <- optimiseInStm bnd   case filter ((`elem` boundHere) . updateValue) $ forwardThese bup of-    [] -> do checkIfForwardableUpdate bnd'-             return $ bnd':bnds'+    [] -> do+      checkIfForwardableUpdate bnd'+      return $ bnd' : bnds'     updates -> do       lower <- asks topLowerUpdate       scope <- askScope@@ -145,20 +157,23 @@       -- Condition (5) and (7) are assumed to be checked by       -- lowerUpdate.       case lower scope bnd' updates of-        Just lowering -> do new_bnds <- lowering-                            new_bnds' <- optimiseStms new_bnds $-                                         tell bup { forwardThese = [] }-                            return $ new_bnds' ++ filter notUpdated bnds'-        Nothing       -> do checkIfForwardableUpdate bnd'-                            return $ bnd':bnds'--  where boundHere = patternNames $ stmPattern bnd+        Just lowering -> do+          new_bnds <- lowering+          new_bnds' <-+            optimiseStms new_bnds $+              tell bup {forwardThese = []}+          return $ new_bnds' ++ filter notUpdated bnds'+        Nothing -> do+          checkIfForwardableUpdate bnd'+          return $ bnd' : bnds'+  where+    boundHere = patternNames $ stmPattern bnd -        checkIfForwardableUpdate (Let pat (StmAux cs _ _) e)-            | Pattern [] [PatElem v dec] <- pat,-              BasicOp (Update src slice (Var ve)) <- e =-                maybeForward ve v dec cs src slice-        checkIfForwardableUpdate _ = return ()+    checkIfForwardableUpdate (Let pat (StmAux cs _ _) e)+      | Pattern [] [PatElem v dec] <- pat,+        BasicOp (Update src slice (Var ve)) <- e =+        maybeForward ve v dec cs src slice+    checkIfForwardableUpdate _ = return ()  optimiseInStm :: Constraints lore => Stm (Aliases lore) -> ForwardingM lore (Stm (Aliases lore)) optimiseInStm (Let pat dec e) =@@ -167,46 +182,55 @@ optimiseExp :: Constraints lore => Exp (Aliases lore) -> ForwardingM lore (Exp (Aliases lore)) optimiseExp (DoLoop ctx val form body) =   bindingScope (scopeOf form) $-  bindingFParams (map fst $ ctx ++ val) $-  DoLoop ctx val form <$> optimiseBody body+    bindingFParams (map fst $ ctx ++ val) $+      DoLoop ctx val form <$> optimiseBody body optimiseExp (Op op) = do   f <- asks topOnOp   Op <$> f op optimiseExp e = mapExpM optimise e-  where optimise = identityMapper { mapOnBody = const optimiseBody-                                  }+  where+    optimise =+      identityMapper+        { mapOnBody = const optimiseBody+        }+ onKernelOp :: OnOp Kernels onKernelOp (SegOp op) =   bindingScope (scopeOfSegSpace (segSpace op)) $ do-    let mapper = identitySegOpMapper { mapOnSegOpBody = onKernelBody }+    let mapper = identitySegOpMapper {mapOnSegOpBody = onKernelBody}         onKernelBody kbody = do-          stms <- deepen $ optimiseStms (stmsToList (kernelBodyStms kbody)) $-                  mapM_ seenVar $ namesToList $ freeIn $ kernelBodyResult kbody-          return kbody { kernelBodyStms = stmsFromList stms }+          stms <-+            deepen $+              optimiseStms (stmsToList (kernelBodyStms kbody)) $+                mapM_ seenVar $ namesToList $ freeIn $ kernelBodyResult kbody+          return kbody {kernelBodyStms = stmsFromList stms}     SegOp <$> mapSegOpM mapper op onKernelOp op = return op -data Entry lore = Entry { entryNumber :: Int-                        , entryAliases :: Names-                        , entryDepth :: Int-                        , entryOptimisable :: Bool-                        , entryType :: NameInfo (Aliases lore)-                        }+data Entry lore = Entry+  { entryNumber :: Int,+    entryAliases :: Names,+    entryDepth :: Int,+    entryOptimisable :: Bool,+    entryType :: NameInfo (Aliases lore)+  }  type VTable lore = M.Map VName (Entry lore)  type OnOp lore = Op (Aliases lore) -> ForwardingM lore (Op (Aliases lore)) -data TopDown lore = TopDown { topDownCounter :: Int-                            , topDownTable :: VTable lore-                            , topDownDepth :: Int-                            , topLowerUpdate :: LowerUpdate lore (ForwardingM lore)-                            , topOnOp :: OnOp lore-                            }+data TopDown lore = TopDown+  { topDownCounter :: Int,+    topDownTable :: VTable lore,+    topDownDepth :: Int,+    topLowerUpdate :: LowerUpdate lore (ForwardingM lore),+    topOnOp :: OnOp lore+  } -data BottomUp lore = BottomUp { bottomUpSeen :: Names-                              , forwardThese :: [DesiredUpdate (LetDec (Aliases lore))]-                              }+data BottomUp lore = BottomUp+  { bottomUpSeen :: Names,+    forwardThese :: [DesiredUpdate (LetDec (Aliases lore))]+  }  instance Semigroup (BottomUp lore) where   BottomUp seen1 forward1 <> BottomUp seen2 forward2 =@@ -216,10 +240,14 @@   mempty = BottomUp mempty mempty  newtype ForwardingM lore a = ForwardingM (RWS (TopDown lore) (BottomUp lore) VNameSource a)-                      deriving (Monad, Applicative, Functor,-                                MonadReader (TopDown lore),-                                MonadWriter (BottomUp lore),-                                MonadState VNameSource)+  deriving+    ( Monad,+      Applicative,+      Functor,+      MonadReader (TopDown lore),+      MonadWriter (BottomUp lore),+      MonadState VNameSource+    )  instance MonadFreshNames (ForwardingM lore) where   getNameSource = get@@ -228,63 +256,81 @@ instance Constraints lore => HasScope (Aliases lore) (ForwardingM lore) where   askScope = M.map entryType <$> asks topDownTable -runForwardingM :: LowerUpdate lore (ForwardingM lore) -> OnOp lore -> ForwardingM lore a-               -> VNameSource -> (a, VNameSource)-runForwardingM f g (ForwardingM m) src = let (x, src', _) = runRWS m emptyTopDown src-                                         in (x, src')-  where emptyTopDown = TopDown { topDownCounter = 0-                               , topDownTable = M.empty-                               , topDownDepth = 0-                               , topLowerUpdate = f-                               , topOnOp = g-                               }+runForwardingM ::+  LowerUpdate lore (ForwardingM lore) ->+  OnOp lore ->+  ForwardingM lore a ->+  VNameSource ->+  (a, VNameSource)+runForwardingM f g (ForwardingM m) src =+  let (x, src', _) = runRWS m emptyTopDown src+   in (x, src')+  where+    emptyTopDown =+      TopDown+        { topDownCounter = 0,+          topDownTable = M.empty,+          topDownDepth = 0,+          topLowerUpdate = f,+          topOnOp = g+        } -bindingParams :: (dec -> NameInfo (Aliases lore))-              -> [Param dec]-               -> ForwardingM lore a-               -> ForwardingM lore a+bindingParams ::+  (dec -> NameInfo (Aliases lore)) ->+  [Param dec] ->+  ForwardingM lore a ->+  ForwardingM lore a bindingParams f params = local $ \(TopDown n vtable d x y) ->   let entry fparam =-        (paramName fparam,-         Entry n mempty d False $ f $ paramDec fparam)+        ( paramName fparam,+          Entry n mempty d False $ f $ paramDec fparam+        )       entries = M.fromList $ map entry params-  in TopDown (n+1) (M.union entries vtable) d x y+   in TopDown (n + 1) (M.union entries vtable) d x y -bindingFParams :: [FParam (Aliases lore)]-               -> ForwardingM lore a-               -> ForwardingM lore a+bindingFParams ::+  [FParam (Aliases lore)] ->+  ForwardingM lore a ->+  ForwardingM lore a bindingFParams = bindingParams FParamName -bindingScope :: Scope (Aliases lore)-             -> ForwardingM lore a-             -> ForwardingM lore a+bindingScope ::+  Scope (Aliases lore) ->+  ForwardingM lore a ->+  ForwardingM lore a bindingScope scope = local $ \(TopDown n vtable d x y) ->   let entries = M.map entry scope       infoAliases (LetName (aliases, _)) = unAliases aliases       infoAliases _ = mempty       entry info = Entry n (infoAliases info) d False info-  in TopDown (n+1) (entries<>vtable) d x y+   in TopDown (n + 1) (entries <> vtable) d x y -bindingStm :: Stm (Aliases lore)-           -> ForwardingM lore a-           -> ForwardingM lore a+bindingStm ::+  Stm (Aliases lore) ->+  ForwardingM lore a ->+  ForwardingM lore a bindingStm (Let pat _ _) = local $ \(TopDown n vtable d x y) ->   let entries = M.fromList $ map entry $ patternElements pat       entry patElem =         let (aliases, _) = patElemDec patElem-        in (patElemName patElem,-            Entry n (unAliases aliases) d True $ LetName $ patElemDec patElem)-  in TopDown (n+1) (M.union entries vtable) d x y+         in ( patElemName patElem,+              Entry n (unAliases aliases) d True $ LetName $ patElemDec patElem+            )+   in TopDown (n + 1) (M.union entries vtable) d x y  bindingNumber :: VName -> ForwardingM lore Int bindingNumber name = do   res <- asks $ fmap entryNumber . M.lookup name . topDownTable-  case res of Just n  -> return n-              Nothing -> error $ "bindingNumber: variable " ++-                         pretty name ++ " not found."+  case res of+    Just n -> return n+    Nothing ->+      error $+        "bindingNumber: variable "+          ++ pretty name+          ++ " not found."  deepen :: ForwardingM lore a -> ForwardingM lore a-deepen = local $ \env -> env { topDownDepth = topDownDepth env + 1 }+deepen = local $ \env -> env {topDownDepth = topDownDepth env + 1}  areAvailableBefore :: Names -> VName -> ForwardingM lore Bool areAvailableBefore names point = do@@ -296,37 +342,53 @@ isInCurrentBody name = do   current <- asks topDownDepth   res <- asks $ fmap entryDepth . M.lookup name . topDownTable-  case res of Just d  -> return $ d == current-              Nothing -> error $ "isInCurrentBody: variable " ++-                         pretty name ++ " not found."+  case res of+    Just d -> return $ d == current+    Nothing ->+      error $+        "isInCurrentBody: variable "+          ++ pretty name+          ++ " not found."  isOptimisable :: VName -> ForwardingM lore Bool isOptimisable name = do   res <- asks $ fmap entryOptimisable . M.lookup name . topDownTable-  case res of Just b  -> return b-              Nothing -> error $ "isOptimisable: variable " ++-                         pretty name ++ " not found."+  case res of+    Just b -> return b+    Nothing ->+      error $+        "isOptimisable: variable "+          ++ pretty name+          ++ " not found."  seenVar :: VName -> ForwardingM lore () seenVar name = do-  aliases <- asks $-             maybe mempty entryAliases .-             M.lookup name . topDownTable-  tell $ mempty { bottomUpSeen = oneName name <> aliases }+  aliases <-+    asks $+      maybe mempty entryAliases+        . M.lookup name+        . topDownTable+  tell $ mempty {bottomUpSeen = oneName name <> aliases}  tapBottomUp :: ForwardingM lore a -> ForwardingM lore (a, BottomUp lore)-tapBottomUp m = do (x,bup) <- listen m-                   return (x, bup)+tapBottomUp m = do+  (x, bup) <- listen m+  return (x, bup) -maybeForward :: Constraints lore =>-                VName-             -> VName -> LetDec (Aliases lore)-             -> Certificates -> VName -> Slice SubExp-             -> ForwardingM lore ()+maybeForward ::+  Constraints lore =>+  VName ->+  VName ->+  LetDec (Aliases lore) ->+  Certificates ->+  VName ->+  Slice SubExp ->+  ForwardingM lore () maybeForward v dest_nm dest_dec cs src slice = do   -- Checks condition (2)-  available <- (freeIn src <> freeIn slice <> freeIn cs)-               `areAvailableBefore` v+  available <-+    (freeIn src <> freeIn slice <> freeIn cs)+      `areAvailableBefore` v   -- Check condition (3)   samebody <- isInCurrentBody v   -- Check condition (6)@@ -334,4 +396,4 @@   not_prim <- not . primType <$> lookupType v   when (available && samebody && optimisable && not_prim) $ do     let fwd = DesiredUpdate dest_nm dest_dec cs src slice v-    tell mempty { forwardThese = [fwd] }+    tell mempty {forwardThese = [fwd]}
src/Futhark/Optimise/InPlaceLowering/LowerIntoStm.hs view
@@ -1,72 +1,88 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}+ module Futhark.Optimise.InPlaceLowering.LowerIntoStm-  ( lowerUpdateKernels-  , lowerUpdate-  , LowerUpdate-  , DesiredUpdate (..)-  ) where+  ( lowerUpdateKernels,+    lowerUpdate,+    LowerUpdate,+    DesiredUpdate (..),+  )+where  import Control.Monad import Control.Monad.Writer-import Data.List (find, unzip4)-import Data.Maybe (mapMaybe) import Data.Either+import Data.List (find, unzip4) import qualified Data.Map as M-+import Data.Maybe (mapMaybe) import Futhark.Analysis.PrimExp.Convert-import Futhark.IR.Prop.Aliases+import Futhark.Construct import Futhark.IR.Aliases import Futhark.IR.Kernels-import Futhark.Construct import Futhark.Optimise.InPlaceLowering.SubstituteIndices -data DesiredUpdate dec =-  DesiredUpdate { updateName :: VName -- ^ Name of result.-                , updateType :: dec -- ^ Type of result.-                , updateCertificates :: Certificates-                , updateSource :: VName-                , updateIndices :: Slice SubExp-                , updateValue :: VName-                }+data DesiredUpdate dec = DesiredUpdate+  { -- | Name of result.+    updateName :: VName,+    -- | Type of result.+    updateType :: dec,+    updateCertificates :: Certificates,+    updateSource :: VName,+    updateIndices :: Slice SubExp,+    updateValue :: VName+  }   deriving (Show)  instance Functor DesiredUpdate where-  f `fmap` u = u { updateType = f $ updateType u }+  f `fmap` u = u {updateType = f $ updateType u}  updateHasValue :: VName -> DesiredUpdate dec -> Bool-updateHasValue name = (name==) . updateValue+updateHasValue name = (name ==) . updateValue -type LowerUpdate lore m = Scope (Aliases lore)-                          -> Stm (Aliases lore)-                          -> [DesiredUpdate (LetDec (Aliases lore))]-                          -> Maybe (m [Stm (Aliases lore)])+type LowerUpdate lore m =+  Scope (Aliases lore) ->+  Stm (Aliases lore) ->+  [DesiredUpdate (LetDec (Aliases lore))] ->+  Maybe (m [Stm (Aliases lore)]) -lowerUpdate :: (MonadFreshNames m, Bindable lore,-                LetDec lore ~ Type, CanBeAliased (Op lore)) => LowerUpdate lore m+lowerUpdate ::+  ( MonadFreshNames m,+    Bindable lore,+    LetDec lore ~ Type,+    CanBeAliased (Op lore)+  ) =>+  LowerUpdate lore m lowerUpdate scope (Let pat aux (DoLoop ctx val form body)) updates = do   canDo <- lowerUpdateIntoLoop scope updates pat ctx val form body   Just $ do     (prebnds, postbnds, ctxpat, valpat, ctx', val', body') <- canDo     return $-      prebnds ++ [certify (stmAuxCerts aux) $-                  mkLet ctxpat valpat $ DoLoop ctx' val' form body'] ++ postbnds-lowerUpdate _+      prebnds+        ++ [ certify (stmAuxCerts aux) $+               mkLet ctxpat valpat $ DoLoop ctx' val' form body'+           ]+        ++ postbnds+lowerUpdate+  _   (Let pat aux (BasicOp (SubExp (Var v))))   [DesiredUpdate bindee_nm bindee_dec cs src is val]-  | patternNames pat == [src] =-    let is' = fullSlice (typeOf bindee_dec) is-    in Just $-       return [certify (stmAuxCerts aux <> cs) $-               mkLet [] [Ident bindee_nm $ typeOf bindee_dec] $-               BasicOp $ Update v is' $ Var val]+    | patternNames pat == [src] =+      let is' = fullSlice (typeOf bindee_dec) is+       in Just $+            return+              [ certify (stmAuxCerts aux <> cs) $+                  mkLet [] [Ident bindee_nm $ typeOf bindee_dec] $+                    BasicOp $ Update v is' $ Var val+              ] lowerUpdate _ _ _ =   Nothing  lowerUpdateKernels :: MonadFreshNames m => LowerUpdate Kernels m-lowerUpdateKernels scope-  (Let pat aux (Op (SegOp (SegMap lvl space ts kbody)))) updates-  | all ((`elem` patternNames pat) . updateValue) updates = do+lowerUpdateKernels+  scope+  (Let pat aux (Op (SegOp (SegMap lvl space ts kbody))))+  updates+    | all ((`elem` patternNames pat) . updateValue) updates = do       mk <- lowerUpdatesIntoSegMap scope pat updates space kbody       Just $ do         (pat', kbody', poststms) <- mk@@ -76,71 +92,88 @@           stmsToList poststms lowerUpdateKernels scope stm updates = lowerUpdate scope stm updates -lowerUpdatesIntoSegMap :: MonadFreshNames m =>-                          Scope (Aliases Kernels)-                       -> Pattern (Aliases Kernels)-                       -> [DesiredUpdate (LetDec (Aliases Kernels))]-                       -> SegSpace-                       -> KernelBody (Aliases Kernels)-                       -> Maybe (m (Pattern (Aliases Kernels),-                                    KernelBody (Aliases Kernels),-                                    Stms (Aliases Kernels)))+lowerUpdatesIntoSegMap ::+  MonadFreshNames m =>+  Scope (Aliases Kernels) ->+  Pattern (Aliases Kernels) ->+  [DesiredUpdate (LetDec (Aliases Kernels))] ->+  SegSpace ->+  KernelBody (Aliases Kernels) ->+  Maybe+    ( m+        ( Pattern (Aliases Kernels),+          KernelBody (Aliases Kernels),+          Stms (Aliases Kernels)+        )+    ) lowerUpdatesIntoSegMap scope pat updates kspace kbody = do   -- The updates are all-or-nothing.  Being more liberal would require   -- changes to the in-place-lowering pass itself.   mk <- zipWithM onRet (patternElements pat) (kernelBodyResult kbody)   return $ do     (pes, bodystms, krets, poststms) <- unzip4 <$> sequence mk-    return (Pattern [] pes,-            kbody { kernelBodyStms = kernelBodyStms kbody <> mconcat bodystms-                  , kernelBodyResult = krets-                  },-            mconcat poststms)--  where (gtids, _dims) = unzip $ unSegSpace kspace--        onRet (PatElem v v_dec) ret-          | Just (DesiredUpdate bindee_nm bindee_dec _cs src slice _val) <--              find ((==v) . updateValue) updates = do--              Returns _ se <- Just ret+    return+      ( Pattern [] pes,+        kbody+          { kernelBodyStms = kernelBodyStms kbody <> mconcat bodystms,+            kernelBodyResult = krets+          },+        mconcat poststms+      )+  where+    (gtids, _dims) = unzip $ unSegSpace kspace -              Just $ do-                let pexp = primExpFromSubExp int32-                (slice', bodystms) <- flip runBinderT scope $-                  traverse (toSubExp "index") $-                  fixSlice (map (fmap pexp) slice) $-                  map (pexp . Var) gtids+    onRet (PatElem v v_dec) ret+      | Just (DesiredUpdate bindee_nm bindee_dec _cs src slice _val) <-+          find ((== v) . updateValue) updates = do+        Returns _ se <- Just ret -                let res_dims = arrayDims $ snd bindee_dec-                    ret' = WriteReturns res_dims src [(map DimFix slice', se)]+        Just $ do+          (slice', bodystms) <-+            flip runBinderT scope $+              traverse (toSubExp "index") $+                fixSlice (map (fmap pe32) slice) $+                  map (pe32 . Var) gtids -                return (PatElem bindee_nm bindee_dec,-                        bodystms,-                        ret',-                        oneStm $ mkLet [] [Ident v $ typeOf v_dec] $-                        BasicOp $ Index bindee_nm slice)+          let res_dims = arrayDims $ snd bindee_dec+              ret' = WriteReturns res_dims src [(map DimFix slice', se)] -        onRet pe ret =-          Just $ return (pe, mempty, ret, mempty)+          return+            ( PatElem bindee_nm bindee_dec,+              bodystms,+              ret',+              oneStm $+                mkLet [] [Ident v $ typeOf v_dec] $+                  BasicOp $ Index bindee_nm slice+            )+    onRet pe ret =+      Just $ return (pe, mempty, ret, mempty) -lowerUpdateIntoLoop :: (Bindable lore, BinderOps lore,-                        Aliased lore, LetDec lore ~ (als, Type),-                        MonadFreshNames m) =>-                       Scope lore-                    -> [DesiredUpdate (LetDec lore)]-                    -> Pattern lore-                    -> [(FParam lore, SubExp)]-                    -> [(FParam lore, SubExp)]-                    -> LoopForm lore-                    -> Body lore-                    -> Maybe (m ([Stm lore],-                                 [Stm lore],-                                 [Ident],-                                 [Ident],-                                 [(FParam lore, SubExp)],-                                 [(FParam lore, SubExp)],-                                 Body lore))+lowerUpdateIntoLoop ::+  ( Bindable lore,+    BinderOps lore,+    Aliased lore,+    LetDec lore ~ (als, Type),+    MonadFreshNames m+  ) =>+  Scope lore ->+  [DesiredUpdate (LetDec lore)] ->+  Pattern lore ->+  [(FParam lore, SubExp)] ->+  [(FParam lore, SubExp)] ->+  LoopForm lore ->+  Body lore ->+  Maybe+    ( m+        ( [Stm lore],+          [Stm lore],+          [Ident],+          [Ident],+          [(FParam lore, SubExp)],+          [(FParam lore, SubExp)],+          Body lore+        )+    ) lowerUpdateIntoLoop scope updates pat ctx val form body = do   -- Algorithm:   --@@ -176,124 +209,154 @@    Just $ do     in_place_map <- mk_in_place_map-    (val',prebnds,postbnds) <- mkMerges in_place_map-    let (ctxpat,valpat) = mkResAndPat in_place_map+    (val', prebnds, postbnds) <- mkMerges in_place_map+    let (ctxpat, valpat) = mkResAndPat in_place_map         idxsubsts = indexSubstitutions in_place_map     (idxsubsts', newbnds) <- substituteIndices idxsubsts $ bodyStms body     (body_res, res_bnds) <- manipulateResult in_place_map idxsubsts'-    let body' = mkBody (newbnds<>res_bnds) body_res+    let body' = mkBody (newbnds <> res_bnds) body_res     return (prebnds, postbnds, ctxpat, valpat, ctx, val', body')-  where usedInBody = mconcat $ map expandAliases $ namesToList $ freeIn body <> freeIn form-        expandAliases v = case M.lookup v scope of-                            Just (LetName dec) -> oneName v <> aliasesOf dec-                            _ -> oneName v-        resmap = zip (bodyResult body) $ patternValueIdents pat+  where+    usedInBody = mconcat $ map expandAliases $ namesToList $ freeIn body <> freeIn form+    expandAliases v = case M.lookup v scope of+      Just (LetName dec) -> oneName v <> aliasesOf dec+      _ -> oneName v+    resmap = zip (bodyResult body) $ patternValueIdents pat -        mkMerges :: (MonadFreshNames m, Bindable lore) =>-                    [LoopResultSummary (als, Type)]-                 -> m ([(Param DeclType, SubExp)], [Stm lore], [Stm lore])-        mkMerges summaries = do-          ((origmerge, extramerge), (prebnds, postbnds)) <--            runWriterT $ partitionEithers <$> mapM mkMerge summaries-          return (origmerge ++ extramerge, prebnds, postbnds)+    mkMerges ::+      (MonadFreshNames m, Bindable lore) =>+      [LoopResultSummary (als, Type)] ->+      m ([(Param DeclType, SubExp)], [Stm lore], [Stm lore])+    mkMerges summaries = do+      ((origmerge, extramerge), (prebnds, postbnds)) <-+        runWriterT $ partitionEithers <$> mapM mkMerge summaries+      return (origmerge ++ extramerge, prebnds, postbnds) -        mkMerge summary-          | Just (update, mergename, mergedec) <- relatedUpdate summary = do-            source <- newVName "modified_source"-            let source_t = snd $ updateType update-                elmident = Ident-                           (updateValue update)-                           (source_t `setArrayDims` sliceDims (updateIndices update))-            tell ([mkLet [] [Ident source source_t] $ BasicOp $ Update-                   (updateSource update)-                   (fullSlice source_t $ updateIndices update) $-                   snd $ mergeParam summary],-                  [mkLet [] [elmident] $ BasicOp $ Index-                   (updateName update)-                   (fullSlice source_t $ updateIndices update)])-            return $ Right (Param-                            mergename-                            (toDecl (typeOf mergedec) Unique),-                            Var source)-          | otherwise = return $ Left $ mergeParam summary+    mkMerge summary+      | Just (update, mergename, mergedec) <- relatedUpdate summary = do+        source <- newVName "modified_source"+        let source_t = snd $ updateType update+            elmident =+              Ident+                (updateValue update)+                (source_t `setArrayDims` sliceDims (updateIndices update))+        tell+          ( [ mkLet [] [Ident source source_t] $+                BasicOp $+                  Update+                    (updateSource update)+                    (fullSlice source_t $ updateIndices update)+                    $ snd $ mergeParam summary+            ],+            [ mkLet [] [elmident] $+                BasicOp $+                  Index+                    (updateName update)+                    (fullSlice source_t $ updateIndices update)+            ]+          )+        return $+          Right+            ( Param+                mergename+                (toDecl (typeOf mergedec) Unique),+              Var source+            )+      | otherwise = return $ Left $ mergeParam summary -        mkResAndPat summaries =-          let (origpat,extrapat) = partitionEithers $ map mkResAndPat' summaries-          in (patternContextIdents pat,-              origpat ++ extrapat)+    mkResAndPat summaries =+      let (origpat, extrapat) = partitionEithers $ map mkResAndPat' summaries+       in ( patternContextIdents pat,+            origpat ++ extrapat+          ) -        mkResAndPat' summary-          | Just (update, _, _) <- relatedUpdate summary =-              Right (Ident (updateName update) (snd $ updateType update))-          | otherwise =-              Left (inPatternAs summary)+    mkResAndPat' summary+      | Just (update, _, _) <- relatedUpdate summary =+        Right (Ident (updateName update) (snd $ updateType update))+      | otherwise =+        Left (inPatternAs summary) -summariseLoop :: MonadFreshNames m =>-                 [DesiredUpdate (als, Type)]-              -> Names-              -> [(SubExp, Ident)]-              -> [(Param DeclType, SubExp)]-              -> Maybe (m [LoopResultSummary (als, Type)])+summariseLoop ::+  MonadFreshNames m =>+  [DesiredUpdate (als, Type)] ->+  Names ->+  [(SubExp, Ident)] ->+  [(Param DeclType, SubExp)] ->+  Maybe (m [LoopResultSummary (als, Type)]) summariseLoop updates usedInBody resmap merge =   sequence <$> zipWithM summariseLoopResult resmap merge-  where summariseLoopResult (se, v) (fparam, mergeinit)-          | Just update <- find (updateHasValue $ identName v) updates =-            if updateSource update `nameIn` usedInBody-            then Nothing-            else if hasLoopInvariantShape fparam then Just $ do-              lowered_array <- newVName "lowered_array"-              return LoopResultSummary { resultSubExp = se-                                       , inPatternAs = v-                                       , mergeParam = (fparam, mergeinit)-                                       , relatedUpdate = Just (update,-                                                               lowered_array,-                                                               updateType update)-                                       }-            else Nothing-        summariseLoopResult _ _ =-          Nothing -- XXX: conservative; but this entire pass is going away.--        hasLoopInvariantShape = all loopInvariant . arrayDims . paramType+  where+    summariseLoopResult (se, v) (fparam, mergeinit)+      | Just update <- find (updateHasValue $ identName v) updates =+        if updateSource update `nameIn` usedInBody+          then Nothing+          else+            if hasLoopInvariantShape fparam+              then Just $ do+                lowered_array <- newVName "lowered_array"+                return+                  LoopResultSummary+                    { resultSubExp = se,+                      inPatternAs = v,+                      mergeParam = (fparam, mergeinit),+                      relatedUpdate =+                        Just+                          ( update,+                            lowered_array,+                            updateType update+                          )+                    }+              else Nothing+    summariseLoopResult _ _ =+      Nothing -- XXX: conservative; but this entire pass is going away.+    hasLoopInvariantShape = all loopInvariant . arrayDims . paramType -        merge_param_names = map (paramName . fst) merge+    merge_param_names = map (paramName . fst) merge -        loopInvariant (Var v)    = v `notElem` merge_param_names-        loopInvariant Constant{} = True+    loopInvariant (Var v) = v `notElem` merge_param_names+    loopInvariant Constant {} = True -data LoopResultSummary dec =-  LoopResultSummary { resultSubExp :: SubExp-                    , inPatternAs :: Ident-                    , mergeParam :: (Param DeclType, SubExp)-                    , relatedUpdate :: Maybe (DesiredUpdate dec, VName, dec)-                    }+data LoopResultSummary dec = LoopResultSummary+  { resultSubExp :: SubExp,+    inPatternAs :: Ident,+    mergeParam :: (Param DeclType, SubExp),+    relatedUpdate :: Maybe (DesiredUpdate dec, VName, dec)+  }   deriving (Show) -indexSubstitutions :: [LoopResultSummary dec]-                   -> IndexSubstitutions dec+indexSubstitutions ::+  [LoopResultSummary dec] ->+  IndexSubstitutions dec indexSubstitutions = mapMaybe getSubstitution-  where getSubstitution res = do-          (DesiredUpdate _ _ cs _ is _, nm, dec) <- relatedUpdate res-          let name = paramName $ fst $ mergeParam res-          return (name, (cs, nm, dec, is))+  where+    getSubstitution res = do+      (DesiredUpdate _ _ cs _ is _, nm, dec) <- relatedUpdate res+      let name = paramName $ fst $ mergeParam res+      return (name, (cs, nm, dec, is)) -manipulateResult :: (Bindable lore, MonadFreshNames m) =>-                    [LoopResultSummary (LetDec lore)]-                 -> IndexSubstitutions (LetDec lore)-                 -> m (Result, Stms lore)+manipulateResult ::+  (Bindable lore, MonadFreshNames m) =>+  [LoopResultSummary (LetDec lore)] ->+  IndexSubstitutions (LetDec lore) ->+  m (Result, Stms lore) manipulateResult summaries substs = do-  let (orig_ses,updated_ses) = partitionEithers $ map unchangedRes summaries+  let (orig_ses, updated_ses) = partitionEithers $ map unchangedRes summaries   (subst_ses, res_bnds) <- runWriterT $ zipWithM substRes updated_ses substs   return (orig_ses ++ subst_ses, stmsFromList res_bnds)   where     unchangedRes summary =       case relatedUpdate summary of         Nothing -> Left $ resultSubExp summary-        Just _  -> Right $ resultSubExp summary+        Just _ -> Right $ resultSubExp summary     substRes (Var res_v) (subst_v, (_, nm, _, _))       | res_v == subst_v =         return $ Var nm     substRes res_se (_, (cs, nm, dec, is)) = do-      v' <- newIdent' (++"_updated") $ Ident nm $ typeOf dec-      tell [certify cs $ mkLet [] [v'] $ BasicOp $-            Update nm (fullSlice (typeOf dec) is) res_se]+      v' <- newIdent' (++ "_updated") $ Ident nm $ typeOf dec+      tell+        [ certify cs $+            mkLet [] [v'] $+              BasicOp $+                Update nm (fullSlice (typeOf dec) is) res_se+        ]       return $ Var $ identName v'
src/Futhark/Optimise/InPlaceLowering/SubstituteIndices.hs view
@@ -1,136 +1,176 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}+ -- | This module exports facilities for transforming array accesses in -- a list of 'Stm's (intended to be the bindings in a body).  The -- idea is that you can state that some variable @x@ is in fact an -- array indexing @v[i0,i1,...]@. module Futhark.Optimise.InPlaceLowering.SubstituteIndices-       (-         substituteIndices-       , IndexSubstitution-       , IndexSubstitutions-       ) where+  ( substituteIndices,+    IndexSubstitution,+    IndexSubstitutions,+  )+where  import Control.Monad import qualified Data.Map.Strict as M--import Futhark.IR.Prop.Aliases-import Futhark.IR import Futhark.Construct+import Futhark.IR+import Futhark.IR.Prop.Aliases import Futhark.Util  type IndexSubstitution dec = (Certificates, VName, dec, Slice SubExp)+ type IndexSubstitutions dec = [(VName, IndexSubstitution dec)] -typeEnvFromSubstitutions :: LetDec lore ~ dec =>-                            IndexSubstitutions dec -> Scope lore+typeEnvFromSubstitutions ::+  LetDec lore ~ dec =>+  IndexSubstitutions dec ->+  Scope lore typeEnvFromSubstitutions = M.fromList . map (fromSubstitution . snd)-  where fromSubstitution (_, name, t, _) =-          (name, LetName t)+  where+    fromSubstitution (_, name, t, _) =+      (name, LetName t)  -- | Perform the substitution.-substituteIndices :: (MonadFreshNames m, BinderOps lore, Bindable lore,-                      Aliased lore, LetDec lore ~ dec) =>-                     IndexSubstitutions dec -> Stms lore-                  -> m (IndexSubstitutions dec, Stms lore)+substituteIndices ::+  ( MonadFreshNames m,+    BinderOps lore,+    Bindable lore,+    Aliased lore,+    LetDec lore ~ dec+  ) =>+  IndexSubstitutions dec ->+  Stms lore ->+  m (IndexSubstitutions dec, Stms lore) substituteIndices substs bnds =   runBinderT (substituteIndicesInStms substs bnds) types-  where types = typeEnvFromSubstitutions substs+  where+    types = typeEnvFromSubstitutions substs -substituteIndicesInStms :: (MonadBinder m, Bindable (Lore m), Aliased (Lore m)) =>-                           IndexSubstitutions (LetDec (Lore m))-                        -> Stms (Lore m)-                        -> m (IndexSubstitutions (LetDec (Lore m)))+substituteIndicesInStms ::+  (MonadBinder m, Bindable (Lore m), Aliased (Lore m)) =>+  IndexSubstitutions (LetDec (Lore m)) ->+  Stms (Lore m) ->+  m (IndexSubstitutions (LetDec (Lore m))) substituteIndicesInStms = foldM substituteIndicesInStm -substituteIndicesInStm :: (MonadBinder m, Bindable (Lore m), Aliased (Lore m)) =>-                          IndexSubstitutions (LetDec (Lore m))-                       -> Stm (Lore m)-                       -> m (IndexSubstitutions (LetDec (Lore m)))+substituteIndicesInStm ::+  (MonadBinder m, Bindable (Lore m), Aliased (Lore m)) =>+  IndexSubstitutions (LetDec (Lore m)) ->+  Stm (Lore m) ->+  m (IndexSubstitutions (LetDec (Lore m))) substituteIndicesInStm substs (Let pat lore e) = do   e' <- substituteIndicesInExp substs e   (substs', pat') <- substituteIndicesInPattern substs pat   addStm $ Let pat' lore e'   return substs' -substituteIndicesInPattern :: (MonadBinder m, LetDec (Lore m) ~ dec) =>-                              IndexSubstitutions (LetDec (Lore m))-                           -> PatternT dec-                           -> m (IndexSubstitutions (LetDec (Lore m)), PatternT dec)+substituteIndicesInPattern ::+  (MonadBinder m, LetDec (Lore m) ~ dec) =>+  IndexSubstitutions (LetDec (Lore m)) ->+  PatternT dec ->+  m (IndexSubstitutions (LetDec (Lore m)), PatternT dec) substituteIndicesInPattern substs pat = do   (substs', context) <- mapAccumLM sub substs $ patternContextElements pat   (substs'', values) <- mapAccumLM sub substs' $ patternValueElements pat   return (substs'', Pattern context values)-  where sub substs' patElem = return (substs', patElem)+  where+    sub substs' patElem = return (substs', patElem) -substituteIndicesInExp :: (MonadBinder m, Bindable (Lore m), Aliased (Lore m),-                           LetDec (Lore m) ~ dec) =>-                          IndexSubstitutions (LetDec (Lore m))-                       -> Exp (Lore m)-                       -> m (Exp (Lore m))+substituteIndicesInExp ::+  ( MonadBinder m,+    Bindable (Lore m),+    Aliased (Lore m),+    LetDec (Lore m) ~ dec+  ) =>+  IndexSubstitutions (LetDec (Lore m)) ->+  Exp (Lore m) ->+  m (Exp (Lore m)) substituteIndicesInExp substs e = do   substs' <- copyAnyConsumed e-  let substitute = identityMapper { mapOnSubExp = substituteIndicesInSubExp substs'-                                  , mapOnVName  = substituteIndicesInVar substs'-                                  , mapOnBody   = const $ substituteIndicesInBody substs'-                                  }+  let substitute =+        identityMapper+          { mapOnSubExp = substituteIndicesInSubExp substs',+            mapOnVName = substituteIndicesInVar substs',+            mapOnBody = const $ substituteIndicesInBody substs'+          }    mapExpM substitute e-  where copyAnyConsumed =-          let consumingSubst substs' v-                | Just (cs2, src2, src2dec, is2) <- lookup v substs = do-                    row <- certifying cs2 $-                           letExp (baseString v ++ "_row") $-                           BasicOp $ Index src2 $ fullSlice (typeOf src2dec) is2-                    row_copy <- letExp (baseString v ++ "_row_copy") $-                                BasicOp $ Copy row-                    return $ update v v (mempty,-                                         row_copy,-                                         src2dec `setType`-                                         (typeOf src2dec `setArrayDims`-                                          sliceDims is2),-                                         []) substs'-              consumingSubst substs' _ =-                return substs'-          in foldM consumingSubst substs . namesToList . consumedInExp+  where+    copyAnyConsumed =+      let consumingSubst substs' v+            | Just (cs2, src2, src2dec, is2) <- lookup v substs = do+              row <-+                certifying cs2 $+                  letExp (baseString v ++ "_row") $+                    BasicOp $ Index src2 $ fullSlice (typeOf src2dec) is2+              row_copy <-+                letExp (baseString v ++ "_row_copy") $+                  BasicOp $ Copy row+              return $+                update+                  v+                  v+                  ( mempty,+                    row_copy,+                    src2dec+                      `setType` ( typeOf src2dec+                                    `setArrayDims` sliceDims is2+                                ),+                    []+                  )+                  substs'+          consumingSubst substs' _ =+            return substs'+       in foldM consumingSubst substs . namesToList . consumedInExp -substituteIndicesInSubExp :: MonadBinder m =>-                             IndexSubstitutions (LetDec (Lore m))-                          -> SubExp-                          -> m SubExp+substituteIndicesInSubExp ::+  MonadBinder m =>+  IndexSubstitutions (LetDec (Lore m)) ->+  SubExp ->+  m SubExp substituteIndicesInSubExp substs (Var v) =   Var <$> substituteIndicesInVar substs v substituteIndicesInSubExp _ se =   return se -substituteIndicesInVar :: MonadBinder m =>-                          IndexSubstitutions (LetDec (Lore m))-                       -> VName-                       -> m VName+substituteIndicesInVar ::+  MonadBinder m =>+  IndexSubstitutions (LetDec (Lore m)) ->+  VName ->+  m VName substituteIndicesInVar substs v   | Just (cs2, src2, _, []) <- lookup v substs =     certifying cs2 $-    letExp (baseString src2) $ BasicOp $ SubExp $ Var src2+      letExp (baseString src2) $ BasicOp $ SubExp $ Var src2   | Just (cs2, src2, src2_dec, is2) <- lookup v substs =     certifying cs2 $-    letExp "idx" $ BasicOp $ Index src2 $ fullSlice (typeOf src2_dec) is2+      letExp "idx" $ BasicOp $ Index src2 $ fullSlice (typeOf src2_dec) is2   | otherwise =     return v -substituteIndicesInBody :: (MonadBinder m, Bindable (Lore m), Aliased (Lore m)) =>-                           IndexSubstitutions (LetDec (Lore m))-                        -> Body (Lore m)-                        -> m (Body (Lore m))+substituteIndicesInBody ::+  (MonadBinder m, Bindable (Lore m), Aliased (Lore m)) =>+  IndexSubstitutions (LetDec (Lore m)) ->+  Body (Lore m) ->+  m (Body (Lore m)) substituteIndicesInBody substs (Body _ stms res) = do-  (substs', stms') <- inScopeOf stms $-    collectStms $ substituteIndicesInStms substs stms-  (res', res_stms) <- inScopeOf stms' $-    collectStms $ mapM (substituteIndicesInSubExp substs') res-  mkBodyM (stms'<>res_stms) res'+  (substs', stms') <-+    inScopeOf stms $+      collectStms $ substituteIndicesInStms substs stms+  (res', res_stms) <-+    inScopeOf stms' $+      collectStms $ mapM (substituteIndicesInSubExp substs') res+  mkBodyM (stms' <> res_stms) res' -update :: VName -> VName -> IndexSubstitution dec -> IndexSubstitutions dec-       -> IndexSubstitutions dec+update ::+  VName ->+  VName ->+  IndexSubstitution dec ->+  IndexSubstitutions dec ->+  IndexSubstitutions dec update needle name subst ((othername, othersubst) : substs)-  | needle == othername = (name, subst)           : substs-  | otherwise           = (othername, othersubst) : update needle name subst substs-update needle _    _ [] = error $ "Cannot find substitution for " ++ pretty needle+  | needle == othername = (name, subst) : substs+  | otherwise = (othername, othersubst) : update needle name subst substs+update needle _ _ [] = error $ "Cannot find substitution for " ++ pretty needle
src/Futhark/Optimise/InliningDeadFun.hs view
@@ -1,33 +1,38 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}+ -- | This module implements a compiler pass for inlining functions, -- then removing those that have become dead. module Futhark.Optimise.InliningDeadFun-  ( inlineFunctions-  , removeDeadFunctions+  ( inlineFunctions,+    removeDeadFunctions,   )-  where+where  import Control.Monad.Identity import Control.Monad.State+import Control.Parallel.Strategies import Data.List (partition)-import Data.Maybe import qualified Data.Map.Strict as M+import Data.Maybe import qualified Data.Set as S-import Control.Parallel.Strategies-+import Futhark.Analysis.CallGraph+import qualified Futhark.Analysis.SymbolTable as ST+import Futhark.Binder import Futhark.IR.SOACS import Futhark.IR.SOACS.Simplify-  (simpleSOACS, simplifyFun, simplifyConsts)+  ( simpleSOACS,+    simplifyConsts,+    simplifyFun,+  ) import Futhark.Optimise.CSE import Futhark.Optimise.Simplify.Lore (addScopeWisdom)+import Futhark.Pass import Futhark.Transform.CopyPropagate-  (copyPropagateInProg, copyPropagateInFun)-import qualified Futhark.Analysis.SymbolTable as ST+  ( copyPropagateInFun,+    copyPropagateInProg,+  ) import Futhark.Transform.Rename-import Futhark.Analysis.CallGraph-import Futhark.Binder-import Futhark.Pass  parMapM :: MonadFreshNames m => (a -> State VNameSource b) -> [a] -> m [b] -- The special-casing of [] is quite important here!  If 'as' is@@ -36,154 +41,175 @@ parMapM _ [] = pure [] parMapM f as =   modifyNameSource $ \src ->-  let f' a = runState (f a) src-      (bs, srcs) = unzip $ parMap rpar f' as-  in (bs, mconcat srcs)+    let f' a = runState (f a) src+        (bs, srcs) = unzip $ parMap rpar f' as+     in (bs, mconcat srcs)  aggInlineFunctions :: MonadFreshNames m => Prog SOACS -> m (Prog SOACS) aggInlineFunctions prog =   let Prog consts funs = prog-  in uncurry Prog . fmap (filter keep) <$>-     recurse 0 (ST.fromScope (addScopeWisdom (scopeOf consts)), consts, funs)-  where fdmap fds =-          M.fromList $ zip (map funDefName fds) fds--        cg = buildCallGraph prog-        noninlined = findNoninlined prog+   in uncurry Prog . fmap (filter keep)+        <$> recurse 0 (ST.fromScope (addScopeWisdom (scopeOf consts)), consts, funs)+  where+    fdmap fds =+      M.fromList $ zip (map funDefName fds) fds -        noCallsTo which fundec =-          not $ any (`S.member` which) $ allCalledBy (funDefName fundec) cg+    cg = buildCallGraph prog+    noninlined = findNoninlined prog -        -- The inverse rate at which we perform full simplification-        -- after inlining.  For the other steps we just do copy-        -- propagation.  The rate here has been determined-        -- heuristically and is probably not optimal for any given-        -- program.-        simplifyRate :: Int-        simplifyRate = 4+    noCallsTo which fundec =+      not $ any (`S.member` which) $ allCalledBy (funDefName fundec) cg -        -- We apply simplification after every round of inlining,-        -- because it is more efficient to shrink the program as soon-        -- as possible, rather than wait until it has balooned after-        -- full inlining.-        recurse i (vtable, consts, funs) = do-          let remaining = S.fromList $ map funDefName funs-              (to_be_inlined, maybe_inline_in) =-                partition (noCallsTo remaining) funs-              (not_to_inline_in, to_inline_in) =-                partition (noCallsTo-                           (S.fromList $ map funDefName to_be_inlined))-                maybe_inline_in-              keep_although_inlined = filter keep to_be_inlined-          if null to_be_inlined-            then return (consts, funs)-            else do+    -- The inverse rate at which we perform full simplification+    -- after inlining.  For the other steps we just do copy+    -- propagation.  The rate here has been determined+    -- heuristically and is probably not optimal for any given+    -- program.+    simplifyRate :: Int+    simplifyRate = 4 -            (vtable', consts') <--              if any ((`calledByConsts` cg) . funDefName) to_be_inlined-              then simplifyConsts . performCSEOnStms True =<<-                   inlineInStms (fdmap to_be_inlined) consts+    -- We apply simplification after every round of inlining,+    -- because it is more efficient to shrink the program as soon+    -- as possible, rather than wait until it has balooned after+    -- full inlining.+    recurse i (vtable, consts, funs) = do+      let remaining = S.fromList $ map funDefName funs+          (to_be_inlined, maybe_inline_in) =+            partition (noCallsTo remaining) funs+          (not_to_inline_in, to_inline_in) =+            partition+              ( noCallsTo+                  (S.fromList $ map funDefName to_be_inlined)+              )+              maybe_inline_in+          keep_although_inlined = filter keep to_be_inlined+      if null to_be_inlined+        then return (consts, funs)+        else do+          (vtable', consts') <-+            if any ((`calledByConsts` cg) . funDefName) to_be_inlined+              then+                simplifyConsts . performCSEOnStms True+                  =<< inlineInStms (fdmap to_be_inlined) consts               else pure (vtable, consts) -            let simplifyFun' fd-                  | i `rem` simplifyRate == 0 =-                      copyPropagateInFun simpleSOACS vtable' .-                      performCSEOnFunDef True =<<-                      simplifyFun vtable' fd-                  | otherwise =-                      copyPropagateInFun simpleSOACS vtable' fd+          let simplifyFun' fd+                | i `rem` simplifyRate == 0 =+                  copyPropagateInFun simpleSOACS vtable'+                    . performCSEOnFunDef True+                    =<< simplifyFun vtable' fd+                | otherwise =+                  copyPropagateInFun simpleSOACS vtable' fd -            let onFun = simplifyFun' <=<-                        inlineInFunDef (fdmap to_be_inlined)-            to_inline_in' <- parMapM onFun to_inline_in-            fmap (keep_although_inlined<>) <$>-              recurse (i+1)+          let onFun =+                simplifyFun'+                  <=< inlineInFunDef (fdmap to_be_inlined)+          to_inline_in' <- parMapM onFun to_inline_in+          fmap (keep_although_inlined <>)+            <$> recurse+              (i + 1)               (vtable', consts', not_to_inline_in <> to_inline_in') -        keep fd =-          isJust (funDefEntryPoint fd) ||-          funDefName fd `S.member` noninlined+    keep fd =+      isJust (funDefEntryPoint fd)+        || funDefName fd `S.member` noninlined  -- | @inlineInFunDef constf fdmap caller@ inlines in @calleer@ the -- functions in @fdmap@ that are called as @constf@. At this point the -- preconditions are that if @fdmap@ is not empty, and, more -- importantly, the functions in @fdmap@ do not call any other -- functions.-inlineInFunDef :: MonadFreshNames m =>-                  M.Map Name (FunDef SOACS) -> FunDef SOACS-               -> m (FunDef SOACS)+inlineInFunDef ::+  MonadFreshNames m =>+  M.Map Name (FunDef SOACS) ->+  FunDef SOACS ->+  m (FunDef SOACS) inlineInFunDef fdmap (FunDef entry attrs name rtp args body) =   FunDef entry attrs name rtp args <$> inlineInBody fdmap body -inlineFunction :: MonadFreshNames m =>-                  Pattern-               -> StmAux dec-               -> [(SubExp, Diet)]-               -> (Safety, SrcLoc, [SrcLoc])-               -> FunDef SOACS-               -> m [Stm]-inlineFunction pat aux args (safety,loc,locs) fun = do+inlineFunction ::+  MonadFreshNames m =>+  Pattern ->+  StmAux dec ->+  [(SubExp, Diet)] ->+  (Safety, SrcLoc, [SrcLoc]) ->+  FunDef SOACS ->+  m [Stm]+inlineFunction pat aux args (safety, loc, locs) fun = do   Body _ stms res <--    renameBody $ mkBody-    (stmsFromList param_stms <> stmsFromList body_stms)-    (bodyResult (funDefBody fun))+    renameBody $+      mkBody+        (stmsFromList param_stms <> stmsFromList body_stms)+        (bodyResult (funDefBody fun))   let res_stms =-        certify (stmAuxCerts aux) <$>-        zipWith bindSubExp (patternIdents pat) res+        certify (stmAuxCerts aux)+          <$> zipWith bindSubExp (patternIdents pat) res   pure $ stmsToList stms <> res_stms-  where param_stms =-          zipWith bindSubExp-          (map paramIdent $ funDefParams fun) (map fst args)+  where+    param_stms =+      zipWith+        bindSubExp+        (map paramIdent $ funDefParams fun)+        (map fst args) -        body_stms =-          stmsToList $-          addLocations (stmAuxAttrs aux) safety (filter notmempty (loc:locs)) $+    body_stms =+      stmsToList $+        addLocations (stmAuxAttrs aux) safety (filter notmempty (loc : locs)) $           bodyStms $ funDefBody fun -        -- Note that the sizes of arrays may not be correct at this-        -- point - it is crucial that we run copy propagation before-        -- the type checker sees this!-        bindSubExp ident se =-          mkLet [] [ident] $ BasicOp $ SubExp se+    -- Note that the sizes of arrays may not be correct at this+    -- point - it is crucial that we run copy propagation before+    -- the type checker sees this!+    bindSubExp ident se =+      mkLet [] [ident] $ BasicOp $ SubExp se -        notmempty = (/=mempty) . locOf+    notmempty = (/= mempty) . locOf -inlineInStms :: MonadFreshNames m =>-                M.Map Name (FunDef SOACS) -> Stms SOACS -> m (Stms SOACS)+inlineInStms ::+  MonadFreshNames m =>+  M.Map Name (FunDef SOACS) ->+  Stms SOACS ->+  m (Stms SOACS) inlineInStms fdmap stms =   bodyStms <$> inlineInBody fdmap (mkBody stms []) -inlineInBody :: MonadFreshNames m =>-                M.Map Name (FunDef SOACS) -> Body -> m Body+inlineInBody ::+  MonadFreshNames m =>+  M.Map Name (FunDef SOACS) ->+  Body ->+  m Body inlineInBody fdmap = onBody-  where inline (Let pat aux (Apply fname args _ what) : rest)-          | Just fd <- M.lookup fname fdmap,-            not $ "noinline" `inAttrs` funDefAttrs fd,-            not $ "noinline" `inAttrs` stmAuxAttrs aux =-              (<>) <$> inlineFunction pat aux args what fd <*> inline rest--        inline (stm : rest) =-          (:) <$> onStm stm <*> inline rest-        inline [] =-          pure mempty+  where+    inline (Let pat aux (Apply fname args _ what) : rest)+      | Just fd <- M.lookup fname fdmap,+        not $ "noinline" `inAttrs` funDefAttrs fd,+        not $ "noinline" `inAttrs` stmAuxAttrs aux =+        (<>) <$> inlineFunction pat aux args what fd <*> inline rest+    inline (stm : rest) =+      (:) <$> onStm stm <*> inline rest+    inline [] =+      pure mempty -        onBody (Body dec stms res) =-          Body dec . stmsFromList <$> inline (stmsToList stms) <*> pure res+    onBody (Body dec stms res) =+      Body dec . stmsFromList <$> inline (stmsToList stms) <*> pure res -        onStm (Let pat aux e) =-          Let pat aux <$> mapExpM inliner e+    onStm (Let pat aux e) =+      Let pat aux <$> mapExpM inliner e -        inliner =-          identityMapper { mapOnBody = const onBody-                         , mapOnOp = onSOAC-                         }+    inliner =+      identityMapper+        { mapOnBody = const onBody,+          mapOnOp = onSOAC+        } -        onSOAC =-          mapSOACM identitySOACMapper-          { mapOnSOACLambda = onLambda }+    onSOAC =+      mapSOACM+        identitySOACMapper+          { mapOnSOACLambda = onLambda+          } -        onLambda (Lambda params body ret) =-          Lambda params <$> onBody body <*> pure ret+    onLambda (Lambda params body ret) =+      Lambda params <$> onBody body <*> pure ret  -- Propagate source locations and attributes to the inlined -- statements.  Attributes are propagated only when applicable (this@@ -191,53 +217,71 @@ -- specially here). addLocations :: Attrs -> Safety -> [SrcLoc] -> Stms SOACS -> Stms SOACS addLocations attrs caller_safety more_locs = fmap onStm-  where onStm (Let pat aux (Apply fname args t (safety, loc,locs))) =-          Let pat aux' $-          Apply fname args t (min caller_safety safety, loc,locs++more_locs)-          where aux' = aux { stmAuxAttrs = attrs <> stmAuxAttrs aux }-        onStm (Let pat aux (BasicOp (Assert cond desc (loc,locs)))) =-          Let pat (withAttrs (attrsForAssert attrs) aux) $-          case caller_safety of-            Safe -> BasicOp $ Assert cond desc (loc,locs++more_locs)-            Unsafe -> BasicOp $ SubExp $ Constant Checked-        onStm (Let pat aux (Op soac)) =-          Let pat (withAttrs attrs' aux) $ Op $ runIdentity $ mapSOACM-          identitySOACMapper { mapOnSOACLambda = return . onLambda-                             } soac-          where attrs' = attrs `withoutAttrs` for_assert-                for_assert = attrsForAssert attrs-                onLambda lam =-                  lam { lambdaBody = onBody for_assert $ lambdaBody lam }-        onStm (Let pat aux e) =-          Let pat aux $ onExp e+  where+    onStm (Let pat aux (Apply fname args t (safety, loc, locs))) =+      Let pat aux' $+        Apply fname args t (min caller_safety safety, loc, locs ++ more_locs)+      where+        aux' = aux {stmAuxAttrs = attrs <> stmAuxAttrs aux}+    onStm (Let pat aux (BasicOp (Assert cond desc (loc, locs)))) =+      Let pat (withAttrs (attrsForAssert attrs) aux) $+        case caller_safety of+          Safe -> BasicOp $ Assert cond desc (loc, locs ++ more_locs)+          Unsafe -> BasicOp $ SubExp $ Constant Checked+    onStm (Let pat aux (Op soac)) =+      Let pat (withAttrs attrs' aux) $+        Op $+          runIdentity $+            mapSOACM+              identitySOACMapper+                { mapOnSOACLambda = return . onLambda+                }+              soac+      where+        attrs' = attrs `withoutAttrs` for_assert+        for_assert = attrsForAssert attrs+        onLambda lam =+          lam {lambdaBody = onBody for_assert $ lambdaBody lam}+    onStm (Let pat aux e) =+      Let pat aux $ onExp e -        onExp = mapExp identityMapper-                { mapOnBody = const $ return . onBody attrs }+    onExp =+      mapExp+        identityMapper+          { mapOnBody = const $ return . onBody attrs+          } -        withAttrs attrs' aux = aux { stmAuxAttrs = attrs' <> stmAuxAttrs aux }+    withAttrs attrs' aux = aux {stmAuxAttrs = attrs' <> stmAuxAttrs aux} -        onBody attrs' body =-          body { bodyStms = addLocations attrs' caller_safety more_locs $-                            bodyStms body }+    onBody attrs' body =+      body+        { bodyStms =+            addLocations attrs' caller_safety more_locs $+              bodyStms body+        }  -- | Inline all functions and remove the resulting dead functions. inlineFunctions :: Pass SOACS SOACS inlineFunctions =-  Pass { passName = "Inline functions"-       , passDescription = "Inline and remove resulting dead functions."-       , passFunction = copyPropagateInProg simpleSOACS <=< aggInlineFunctions-       }+  Pass+    { passName = "Inline functions",+      passDescription = "Inline and remove resulting dead functions.",+      passFunction = copyPropagateInProg simpleSOACS <=< aggInlineFunctions+    }  -- | @removeDeadFunctions prog@ removes the functions that are unreachable from -- the main function from the program. removeDeadFunctions :: Pass SOACS SOACS removeDeadFunctions =-  Pass { passName = "Remove dead functions"-       , passDescription = "Remove the functions that are unreachable from entry points"-       , passFunction = return . pass-       }-  where pass prog =-          let cg        = buildCallGraph prog-              live_funs = filter ((`isFunInCallGraph` cg) . funDefName) $-                          progFuns prog-          in prog { progFuns = live_funs }+  Pass+    { passName = "Remove dead functions",+      passDescription = "Remove the functions that are unreachable from entry points",+      passFunction = return . pass+    }+  where+    pass prog =+      let cg = buildCallGraph prog+          live_funs =+            filter ((`isFunInCallGraph` cg) . funDefName) $+              progFuns prog+       in prog {progFuns = live_funs}
src/Futhark/Optimise/Simplify.hs view
@@ -1,49 +1,51 @@-{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE TupleSections #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE Strict #-}-module Futhark.Optimise.Simplify-  ( simplifyProg-  , simplifySomething-  , simplifyFun-  , simplifyLambda-  , simplifyStms+{-# LANGUAGE TupleSections #-} -  , Engine.SimpleOps (..)-  , Engine.SimpleM-  , Engine.SimplifyOp-  , Engine.bindableSimpleOps-  , Engine.noExtraHoistBlockers-  , Engine.neverHoist-  , Engine.SimplifiableLore-  , Engine.HoistBlockers-  , RuleBook+module Futhark.Optimise.Simplify+  ( simplifyProg,+    simplifySomething,+    simplifyFun,+    simplifyLambda,+    simplifyStms,+    Engine.SimpleOps (..),+    Engine.SimpleM,+    Engine.SimplifyOp,+    Engine.bindableSimpleOps,+    Engine.noExtraHoistBlockers,+    Engine.neverHoist,+    Engine.SimplifiableLore,+    Engine.HoistBlockers,+    RuleBook,   )-  where+where  import Data.Bifunctor (second)+import qualified Futhark.Analysis.SymbolTable as ST+import qualified Futhark.Analysis.UsageTable as UT import Futhark.IR import Futhark.MonadFreshNames import qualified Futhark.Optimise.Simplify.Engine as Engine-import qualified Futhark.Analysis.SymbolTable as ST-import qualified Futhark.Analysis.UsageTable as UT-import Futhark.Optimise.Simplify.Rule import Futhark.Optimise.Simplify.Lore+import Futhark.Optimise.Simplify.Rule import Futhark.Pass  -- | Simplify the given program.  Even if the output differs from the -- output, meaningful simplification may not have taken place - the -- order of bindings may simply have been rearranged.-simplifyProg :: Engine.SimplifiableLore lore =>-                Engine.SimpleOps lore-             -> RuleBook (Engine.Wise lore)-             -> Engine.HoistBlockers lore-             -> Prog lore-             -> PassM (Prog lore)+simplifyProg ::+  Engine.SimplifiableLore lore =>+  Engine.SimpleOps lore ->+  RuleBook (Engine.Wise lore) ->+  Engine.HoistBlockers lore ->+  Prog lore ->+  PassM (Prog lore) simplifyProg simpl rules blockers (Prog consts funs) = do   (consts_vtable, consts') <--    simplifyConsts (UT.usages $ foldMap (freeIn . funDefBody) funs)-                   (mempty, consts)+    simplifyConsts+      (UT.usages $ foldMap (freeIn . funDefBody) funs)+      (mempty, consts)    funs' <- parPass (simplifyFun' consts_vtable) funs   let funs_uses = UT.usages $ foldMap (freeIn . funDefBody) funs'@@ -51,88 +53,110 @@   (_, consts'') <- simplifyConsts funs_uses (mempty, consts')    return $ Prog consts'' funs'--  where simplifyFun' consts_vtable =-          simplifySomething-          (Engine.localVtable (consts_vtable<>) . Engine.simplifyFun)-          removeFunDefWisdom-          simpl rules blockers mempty+  where+    simplifyFun' consts_vtable =+      simplifySomething+        (Engine.localVtable (consts_vtable <>) . Engine.simplifyFun)+        removeFunDefWisdom+        simpl+        rules+        blockers+        mempty -        simplifyConsts uses =-          simplifySomething (onConsts uses . snd)-          (second (removeStmWisdom<$>))-          simpl rules blockers mempty+    simplifyConsts uses =+      simplifySomething+        (onConsts uses . snd)+        (second (removeStmWisdom <$>))+        simpl+        rules+        blockers+        mempty -        onConsts uses consts' = do-          (_, consts'') <--            Engine.simplifyStms consts' (pure ((), mempty))-          (consts''', _) <--            Engine.hoistStms rules (Engine.isFalse False) mempty uses consts''-          return (ST.insertStms consts''' mempty, consts''')+    onConsts uses consts' = do+      (_, consts'') <-+        Engine.simplifyStms consts' (pure ((), mempty))+      (consts''', _) <-+        Engine.hoistStms rules (Engine.isFalse False) mempty uses consts''+      return (ST.insertStms consts''' mempty, consts''')  -- | Run a simplification operation to convergence.-simplifySomething :: (MonadFreshNames m, Engine.SimplifiableLore lore) =>-                     (a -> Engine.SimpleM lore b)-                  -> (b -> a)-                  -> Engine.SimpleOps lore-                  -> RuleBook (Wise lore)-                  -> Engine.HoistBlockers lore-                  -> ST.SymbolTable (Wise lore)-                  -> a-                  -> m a+simplifySomething ::+  (MonadFreshNames m, Engine.SimplifiableLore lore) =>+  (a -> Engine.SimpleM lore b) ->+  (b -> a) ->+  Engine.SimpleOps lore ->+  RuleBook (Wise lore) ->+  Engine.HoistBlockers lore ->+  ST.SymbolTable (Wise lore) ->+  a ->+  m a simplifySomething f g simpl rules blockers vtable x = do-  let f' x' = Engine.localVtable (vtable<>) $ f x'+  let f' x' = Engine.localVtable (vtable <>) $ f x'   loopUntilConvergence env simpl f' g x-  where env = Engine.emptyEnv rules blockers+  where+    env = Engine.emptyEnv rules blockers  -- | Simplify the given function.  Even if the output differs from the -- output, meaningful simplification may not have taken place - the -- order of bindings may simply have been rearranged.  Runs in a loop -- until convergence.-simplifyFun :: (MonadFreshNames m, Engine.SimplifiableLore lore) =>-                Engine.SimpleOps lore-             -> RuleBook (Engine.Wise lore)-             -> Engine.HoistBlockers lore-             -> ST.SymbolTable (Wise lore)-             -> FunDef lore-             -> m (FunDef lore)+simplifyFun ::+  (MonadFreshNames m, Engine.SimplifiableLore lore) =>+  Engine.SimpleOps lore ->+  RuleBook (Engine.Wise lore) ->+  Engine.HoistBlockers lore ->+  ST.SymbolTable (Wise lore) ->+  FunDef lore ->+  m (FunDef lore) simplifyFun = simplifySomething Engine.simplifyFun removeFunDefWisdom  -- | Simplify just a single t'Lambda'.-simplifyLambda :: (MonadFreshNames m, HasScope lore m,-                   Engine.SimplifiableLore lore) =>-                  Engine.SimpleOps lore-               -> RuleBook (Engine.Wise lore)-               -> Engine.HoistBlockers lore-               -> Lambda lore-               -> m (Lambda lore)+simplifyLambda ::+  ( MonadFreshNames m,+    HasScope lore m,+    Engine.SimplifiableLore lore+  ) =>+  Engine.SimpleOps lore ->+  RuleBook (Engine.Wise lore) ->+  Engine.HoistBlockers lore ->+  Lambda lore ->+  m (Lambda lore) simplifyLambda simpl rules blockers orig_lam = do   vtable <- ST.fromScope . addScopeWisdom <$> askScope-  simplifySomething Engine.simplifyLambdaNoHoisting-    removeLambdaWisdom simpl rules blockers vtable orig_lam+  simplifySomething+    Engine.simplifyLambdaNoHoisting+    removeLambdaWisdom+    simpl+    rules+    blockers+    vtable+    orig_lam  -- | Simplify a sequence of 'Stm's.-simplifyStms :: (MonadFreshNames m, Engine.SimplifiableLore lore) =>-                Engine.SimpleOps lore-             -> RuleBook (Engine.Wise lore)-             -> Engine.HoistBlockers lore-             -> Scope lore-             -> Stms lore-             -> m (ST.SymbolTable (Wise lore), Stms lore)+simplifyStms ::+  (MonadFreshNames m, Engine.SimplifiableLore lore) =>+  Engine.SimpleOps lore ->+  RuleBook (Engine.Wise lore) ->+  Engine.HoistBlockers lore ->+  Scope lore ->+  Stms lore ->+  m (ST.SymbolTable (Wise lore), Stms lore) simplifyStms simpl rules blockers scope =   simplifySomething f g simpl rules blockers vtable . (mempty,)-  where vtable = ST.fromScope $ addScopeWisdom scope-        f (_, stms) =-          Engine.simplifyStms stms ((,mempty) <$> Engine.askVtable)-        g = second $ fmap removeStmWisdom+  where+    vtable = ST.fromScope $ addScopeWisdom scope+    f (_, stms) =+      Engine.simplifyStms stms ((,mempty) <$> Engine.askVtable)+    g = second $ fmap removeStmWisdom -loopUntilConvergence :: (MonadFreshNames m, Engine.SimplifiableLore lore) =>-                        Engine.Env lore-                     -> Engine.SimpleOps lore-                     -> (a -> Engine.SimpleM lore b)-                     -> (b -> a)-                     -> a-                     -> m a+loopUntilConvergence ::+  (MonadFreshNames m, Engine.SimplifiableLore lore) =>+  Engine.Env lore ->+  Engine.SimpleOps lore ->+  (a -> Engine.SimpleM lore b) ->+  (b -> a) ->+  a ->+  m a loopUntilConvergence env simpl f g x = do   (x', changed) <- modifyNameSource $ Engine.runSimpleM (f x) simpl env   if changed then loopUntilConvergence env simpl f g (g x') else return $ g x'
src/Futhark/Optimise/Simplify/ClosedForm.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE Safe #-}+ -- | This module implements facilities for determining whether a -- reduction or fold can be expressed in a closed form (i.e. not as a -- SOAC).@@ -8,20 +9,19 @@ -- future, we would like to make it more powerful, as well as possibly -- also being able to analyse sequential loops. module Futhark.Optimise.Simplify.ClosedForm-  ( foldClosedForm-  , loopClosedForm-  , VarLookup+  ( foldClosedForm,+    loopClosedForm,+    VarLookup,   ) where  import Control.Monad-import Data.Maybe import qualified Data.Map.Strict as M-+import Data.Maybe import Futhark.Construct import Futhark.IR-import Futhark.Transform.Rename import Futhark.Optimise.Simplify.Rule+import Futhark.Transform.Rename  -- | A function that, given a variable name, returns its definition. type VarLookup lore = VName -> Maybe (Exp lore, Certificates)@@ -42,137 +42,186 @@  -- | @foldClosedForm look foldfun accargs arrargs@ determines whether -- each of the results of @foldfun@ can be expressed in a closed form.-foldClosedForm :: (ASTLore lore, BinderOps lore) =>-                  VarLookup lore-               -> Pattern lore-               -> Lambda lore-               -> [SubExp] -> [VName]-               -> RuleM lore ()-+foldClosedForm ::+  (ASTLore lore, BinderOps lore) =>+  VarLookup lore ->+  Pattern lore ->+  Lambda lore ->+  [SubExp] ->+  [VName] ->+  RuleM lore () foldClosedForm look pat lam accs arrs = do   inputsize <- arraysSize 0 <$> mapM lookupType arrs -  t <- case patternTypes pat of [Prim t] -> return t-                                _ -> cannotSimplify+  t <- case patternTypes pat of+    [Prim t] -> return t+    _ -> cannotSimplify -  closedBody <- checkResults (patternNames pat) inputsize mempty knownBnds-                (map paramName (lambdaParams lam))-                (lambdaBody lam) accs+  closedBody <-+    checkResults+      (patternNames pat)+      inputsize+      mempty+      Int64+      knownBnds+      (map paramName (lambdaParams lam))+      (lambdaBody lam)+      accs   isEmpty <- newVName "fold_input_is_empty"   letBindNames [isEmpty] $-    BasicOp $ CmpOp (CmpEq int32) inputsize (intConst Int32 0)-  letBind pat =<< (If (Var isEmpty)-                    <$> resultBodyM accs-                    <*> renameBody closedBody-                    <*> pure (IfDec [primBodyType t] IfNormal))-  where knownBnds = determineKnownBindings look lam accs arrs+    BasicOp $ CmpOp (CmpEq int64) inputsize (intConst Int64 0)+  letBind pat+    =<< ( If (Var isEmpty)+            <$> resultBodyM accs+            <*> renameBody closedBody+            <*> pure (IfDec [primBodyType t] IfNormal)+        )+  where+    knownBnds = determineKnownBindings look lam accs arrs  -- | @loopClosedForm pat respat merge bound bodys@ determines whether -- the do-loop can be expressed in a closed form.-loopClosedForm :: (ASTLore lore, BinderOps lore) =>-                  Pattern lore-               -> [(FParam lore,SubExp)]-               -> Names -> SubExp -> Body lore-               -> RuleM lore ()-loopClosedForm pat merge i bound body = do-  t <- case patternTypes pat of [Prim t] -> return t-                                _ -> cannotSimplify+loopClosedForm ::+  (ASTLore lore, BinderOps lore) =>+  Pattern lore ->+  [(FParam lore, SubExp)] ->+  Names ->+  IntType ->+  SubExp ->+  Body lore ->+  RuleM lore ()+loopClosedForm pat merge i it bound body = do+  t <- case patternTypes pat of+    [Prim t] -> return t+    _ -> cannotSimplify -  closedBody <- checkResults mergenames bound i knownBnds-                (map identName mergeidents) body mergeexp+  closedBody <-+    checkResults+      mergenames+      bound+      i+      it+      knownBnds+      (map identName mergeidents)+      body+      mergeexp   isEmpty <- newVName "bound_is_zero"   letBindNames [isEmpty] $-    BasicOp $ CmpOp (CmpSlt Int32) bound (intConst Int32 0)+    BasicOp $ CmpOp (CmpSlt it) bound (intConst it 0) -  letBind pat =<< (If (Var isEmpty)-                    <$> resultBodyM mergeexp-                    <*> renameBody closedBody-                    <*> pure (IfDec [primBodyType t] IfNormal))-  where (mergepat, mergeexp) = unzip merge-        mergeidents = map paramIdent mergepat-        mergenames = map paramName mergepat-        knownBnds = M.fromList $ zip mergenames mergeexp+  letBind pat+    =<< ( If (Var isEmpty)+            <$> resultBodyM mergeexp+            <*> renameBody closedBody+            <*> pure (IfDec [primBodyType t] IfNormal)+        )+  where+    (mergepat, mergeexp) = unzip merge+    mergeidents = map paramIdent mergepat+    mergenames = map paramName mergepat+    knownBnds = M.fromList $ zip mergenames mergeexp -checkResults :: BinderOps lore =>-                [VName]-             -> SubExp-             -> Names-             -> M.Map VName SubExp-             -> [VName] -- ^ Lambda-bound-             -> Body lore-             -> [SubExp]-             -> RuleM lore (Body lore)-checkResults pat size untouchable knownBnds params body accs = do-  ((), bnds) <- collectStms $-                zipWithM_ checkResult (zip pat res) (zip accparams accs)+checkResults ::+  BinderOps lore =>+  [VName] ->+  SubExp ->+  Names ->+  IntType ->+  M.Map VName SubExp ->+  -- | Lambda-bound+  [VName] ->+  Body lore ->+  [SubExp] ->+  RuleM lore (Body lore)+checkResults pat size untouchable it knownBnds params body accs = do+  ((), bnds) <-+    collectStms $+      zipWithM_ checkResult (zip pat res) (zip accparams accs)   mkBodyM bnds $ map Var pat--  where bndMap = makeBindMap body-        (accparams, _) = splitAt (length accs) params-        res = bodyResult body--        nonFree = boundInBody body <> namesFromList params <> untouchable+  where+    bndMap = makeBindMap body+    (accparams, _) = splitAt (length accs) params+    res = bodyResult body -        checkResult (p, Var v) (accparam, acc)-          | Just (BasicOp (BinOp bop x y)) <- M.lookup v bndMap = do-          -- One of x,y must be *this* accumulator, and the other must-          -- be something that is free in the body.-          let isThisAccum = (==Var accparam)-          (this, el) <- liftMaybe $-                        case ((asFreeSubExp x, isThisAccum y),-                              (asFreeSubExp y, isThisAccum x)) of-                          ((Just free, True), _) -> Just (acc, free)-                          (_, (Just free, True)) -> Just (acc, free)-                          _                      -> Nothing+    nonFree = boundInBody body <> namesFromList params <> untouchable -          case bop of-              LogAnd ->-                letBindNames [p] $ BasicOp $ BinOp LogAnd this el-              Add t w | Just properly_typed_size <- properIntSize t -> do-                          size' <- properly_typed_size-                          letBindNames [p] =<<-                            eBinOp (Add t w) (eSubExp this)-                            (pure $ BasicOp $ BinOp (Mul t w) el size')-              FAdd t | Just properly_typed_size <- properFloatSize t -> do-                        size' <- properly_typed_size-                        letBindNames [p] =<<-                          eBinOp (FAdd t) (eSubExp this)-                          (pure $ BasicOp $ BinOp (FMul t) el size')-              _ -> cannotSimplify -- Um... sorry.+    checkResult (p, Var v) (accparam, acc)+      | Just (BasicOp (BinOp bop x y)) <- M.lookup v bndMap = do+        -- One of x,y must be *this* accumulator, and the other must+        -- be something that is free in the body.+        let isThisAccum = (== Var accparam)+        (this, el) <- liftMaybe $+          case ( (asFreeSubExp x, isThisAccum y),+                 (asFreeSubExp y, isThisAccum x)+               ) of+            ((Just free, True), _) -> Just (acc, free)+            (_, (Just free, True)) -> Just (acc, free)+            _ -> Nothing -        checkResult _ _ = cannotSimplify+        case bop of+          LogAnd ->+            letBindNames [p] $ BasicOp $ BinOp LogAnd this el+          Add t w | Just properly_typed_size <- properIntSize t -> do+            size' <- properly_typed_size+            letBindNames [p]+              =<< eBinOp+                (Add t w)+                (eSubExp this)+                (pure $ BasicOp $ BinOp (Mul t w) el size')+          FAdd t | Just properly_typed_size <- properFloatSize t -> do+            size' <- properly_typed_size+            letBindNames [p]+              =<< eBinOp+                (FAdd t)+                (eSubExp this)+                (pure $ BasicOp $ BinOp (FMul t) el size')+          _ -> cannotSimplify -- Um... sorry.+    checkResult _ _ = cannotSimplify -        asFreeSubExp :: SubExp -> Maybe SubExp-        asFreeSubExp (Var v)-          | v `nameIn` nonFree = M.lookup v knownBnds-        asFreeSubExp se = Just se+    asFreeSubExp :: SubExp -> Maybe SubExp+    asFreeSubExp (Var v)+      | v `nameIn` nonFree = M.lookup v knownBnds+    asFreeSubExp se = Just se -        properIntSize Int32 = Just $ return size-        properIntSize t = Just $ letSubExp "converted_size" $-                          BasicOp $ ConvOp (SExt Int32 t) size+    properIntSize Int64 = Just $ return size+    properIntSize t =+      Just $+        letSubExp "converted_size" $+          BasicOp $ ConvOp (SExt it t) size -        properFloatSize t =-          Just $ letSubExp "converted_size" $-          BasicOp $ ConvOp (SIToFP Int32 t) size+    properFloatSize t =+      Just $+        letSubExp "converted_size" $+          BasicOp $ ConvOp (SIToFP it t) size -determineKnownBindings :: VarLookup lore -> Lambda lore -> [SubExp] -> [VName]-                       -> M.Map VName SubExp+determineKnownBindings ::+  VarLookup lore ->+  Lambda lore ->+  [SubExp] ->+  [VName] ->+  M.Map VName SubExp determineKnownBindings look lam accs arrs =   accBnds <> arrBnds-  where (accparams, arrparams) =-          splitAt (length accs) $ lambdaParams lam-        accBnds = M.fromList $-                  zip (map paramName accparams) accs-        arrBnds = M.fromList $ mapMaybe isReplicate $-                  zip (map paramName arrparams) arrs+  where+    (accparams, arrparams) =+      splitAt (length accs) $ lambdaParams lam+    accBnds =+      M.fromList $+        zip (map paramName accparams) accs+    arrBnds =+      M.fromList $+        mapMaybe isReplicate $+          zip (map paramName arrparams) arrs -        isReplicate (p, v)-          | Just (BasicOp (Replicate _ ve), cs) <- look v,-            cs == mempty = Just (p, ve)-        isReplicate _ = Nothing+    isReplicate (p, v)+      | Just (BasicOp (Replicate _ ve), cs) <- look v,+        cs == mempty =+        Just (p, ve)+    isReplicate _ = Nothing  makeBindMap :: Body lore -> M.Map VName (Exp lore) makeBindMap = M.fromList . mapMaybe isSingletonStm . stmsToList . bodyStms-  where isSingletonStm (Let pat _ e) = case patternNames pat of-          [v] -> Just (v,e)-          _   -> Nothing+  where+    isSingletonStm (Let pat _ e) = case patternNames pat of+      [v] -> Just (v, e)+      _ -> Nothing
src/Futhark/Optimise/Simplify/Engine.hs view
@@ -1,885 +1,1035 @@-{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, TypeFamilies, FlexibleContexts #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE Strict #-}--- |------ Perform general rule-based simplification based on data dependency--- information.  This module will:------    * Perform common-subexpression elimination (CSE).------    * Hoist expressions out of loops (including lambdas) and---    branches.  This is done as aggressively as possible.------    * Apply simplification rules (see---    "Futhark.Optimise.Simplification.Rules").------ If you just want to run the simplifier as simply as possible, you--- may prefer to use the "Futhark.Optimise.Simplify" module.----module Futhark.Optimise.Simplify.Engine-       ( -- * Monadic interface-         SimpleM-       , runSimpleM-       , SimpleOps (..)-       , SimplifyOp-       , bindableSimpleOps--       , Env (envHoistBlockers, envRules)-       , emptyEnv-       , HoistBlockers(..)-       , neverBlocks-       , noExtraHoistBlockers-       , neverHoist-       , BlockPred-       , orIf-       , hasFree-       , isConsumed-       , isFalse-       , isOp-       , isNotSafe-       , asksEngineEnv-       , askVtable-       , localVtable--         -- * Building blocks-       , SimplifiableLore-       , Simplifiable (..)-       , simplifyStms-       , simplifyFun-       , simplifyLambda-       , simplifyLambdaNoHoisting-       , bindLParams-       , simplifyBody-       , SimplifiedBody-       , ST.SymbolTable--       , hoistStms-       , blockIf--       , module Futhark.Optimise.Simplify.Lore-       ) where--import Control.Monad.Writer-import Control.Monad.Reader-import Control.Monad.State.Strict-import Data.Either-import Data.List (find, foldl', nub, mapAccumL)-import Data.Maybe--import Futhark.IR-import Futhark.IR.Prop.Aliases-import Futhark.Optimise.Simplify.Rule-import qualified Futhark.Analysis.SymbolTable as ST-import qualified Futhark.Analysis.UsageTable as UT-import Futhark.Construct-import Futhark.Optimise.Simplify.Lore-import Futhark.Util (splitFromEnd)--data HoistBlockers lore = HoistBlockers-                          { blockHoistPar :: BlockPred (Wise lore)-                            -- ^ Blocker for hoisting out of parallel loops.-                          , blockHoistSeq :: BlockPred (Wise lore)-                            -- ^ Blocker for hoisting out of sequential loops.-                          , blockHoistBranch :: BlockPred (Wise lore)-                            -- ^ Blocker for hoisting out of branches.-                          , isAllocation  :: Stm (Wise lore) -> Bool-                          }--noExtraHoistBlockers :: HoistBlockers lore-noExtraHoistBlockers =-  HoistBlockers neverBlocks neverBlocks neverBlocks (const False)--neverHoist :: HoistBlockers lore-neverHoist =-  HoistBlockers alwaysBlocks alwaysBlocks alwaysBlocks (const False)--data Env lore = Env { envRules         :: RuleBook (Wise lore)-                    , envHoistBlockers :: HoistBlockers lore-                    , envVtable        :: ST.SymbolTable (Wise lore)-                    }--emptyEnv :: RuleBook (Wise lore) -> HoistBlockers lore -> Env lore-emptyEnv rules blockers =-  Env { envRules = rules-      , envHoistBlockers = blockers-      , envVtable = mempty-      }--type Protect m = SubExp -> Pattern (Lore m) -> Op (Lore m) -> Maybe (m ())--data SimpleOps lore =-  SimpleOps { mkExpDecS :: ST.SymbolTable (Wise lore)-                         -> Pattern (Wise lore) -> Exp (Wise lore)-                         -> SimpleM lore (ExpDec (Wise lore))-            , mkBodyS :: ST.SymbolTable (Wise lore)-                      -> Stms (Wise lore) -> Result-                      -> SimpleM lore (Body (Wise lore))-            , protectHoistedOpS :: Protect (Binder (Wise lore))-              -- ^ Make a hoisted Op safe.  The SubExp is a boolean-              -- that is true when the value of the statement will-              -- actually be used.-            , opUsageS :: Op (Wise lore) -> UT.UsageTable-            , simplifyOpS :: SimplifyOp lore (Op lore)-            }--type SimplifyOp lore op = op -> SimpleM lore (OpWithWisdom op, Stms (Wise lore))--bindableSimpleOps :: (SimplifiableLore lore, Bindable lore) =>-                     SimplifyOp lore (Op lore) -> SimpleOps lore-bindableSimpleOps =-  SimpleOps mkExpDecS' mkBodyS' protectHoistedOpS' (const mempty)-  where mkExpDecS' _ pat e = return $ mkExpDec pat e-        mkBodyS' _ bnds res = return $ mkBody bnds res-        protectHoistedOpS' _ _ _ = Nothing--newtype SimpleM lore a =-  SimpleM (ReaderT (SimpleOps lore, Env lore)-           (State (VNameSource, Bool, Certificates)) a)-  deriving (Applicative, Functor, Monad,-            MonadReader (SimpleOps lore, Env lore),-            MonadState (VNameSource, Bool, Certificates))--instance MonadFreshNames (SimpleM lore) where-  putNameSource src = modify $ \(_, b, c) -> (src, b, c)-  getNameSource = gets $ \(a, _, _) -> a--instance SimplifiableLore lore => HasScope (Wise lore) (SimpleM lore) where-  askScope = ST.toScope <$> askVtable-  lookupType name = do-    vtable <- askVtable-    case ST.lookupType name vtable of-      Just t -> return t-      Nothing -> error $-                 "SimpleM.lookupType: cannot find variable " ++-                 pretty name ++ " in symbol table."--instance SimplifiableLore lore =>-         LocalScope (Wise lore) (SimpleM lore) where-  localScope types = localVtable (<>ST.fromScope types)--runSimpleM :: SimpleM lore a-           -> SimpleOps lore-           -> Env lore-           -> VNameSource-           -> ((a, Bool), VNameSource)-runSimpleM (SimpleM m) simpl env src =-  let (x, (src', b, _)) = runState (runReaderT m (simpl, env)) (src, False, mempty)-  in ((x, b), src')--askEngineEnv :: SimpleM lore (Env lore)-askEngineEnv = asks snd--asksEngineEnv :: (Env lore -> a) -> SimpleM lore a-asksEngineEnv f = f <$> askEngineEnv--askVtable :: SimpleM lore (ST.SymbolTable (Wise lore))-askVtable = asksEngineEnv envVtable--localVtable :: (ST.SymbolTable (Wise lore) -> ST.SymbolTable (Wise lore))-            -> SimpleM lore a -> SimpleM lore a-localVtable f = local $ \(ops, env) -> (ops, env { envVtable = f $ envVtable env })--collectCerts :: SimpleM lore a -> SimpleM lore (a, Certificates)-collectCerts m = do x <- m-                    (a, b, cs) <- get-                    put (a, b, mempty)-                    return (x, cs)---- | Mark that we have changed something and it would be a good idea--- to re-run the simplifier.-changed :: SimpleM lore ()-changed = modify $ \(src, _, cs) -> (src, True, cs)--usedCerts :: Certificates -> SimpleM lore ()-usedCerts cs = modify $ \(a, b, c) -> (a, b, cs <> c)--enterLoop :: SimpleM lore a -> SimpleM lore a-enterLoop = localVtable ST.deepen--bindFParams :: SimplifiableLore lore => [FParam (Wise lore)] -> SimpleM lore a -> SimpleM lore a-bindFParams params =-  localVtable $ ST.insertFParams params--bindLParams :: SimplifiableLore lore => [LParam (Wise lore)] -> SimpleM lore a -> SimpleM lore a-bindLParams params =-  localVtable $ \vtable -> foldr ST.insertLParam vtable params--bindArrayLParams :: SimplifiableLore lore => [LParam (Wise lore)] -> SimpleM lore a-                 -> SimpleM lore a-bindArrayLParams params =-  localVtable $ \vtable -> foldl' (flip ST.insertLParam) vtable params--bindLoopVar :: SimplifiableLore lore => VName -> IntType -> SubExp -> SimpleM lore a -> SimpleM lore a-bindLoopVar var it bound =-  localVtable $ ST.insertLoopVar var it bound---- | We are willing to hoist potentially unsafe statements out of--- branches, but they most be protected by adding a branch on top of--- them.  (This means such hoisting is not worth it unless they are in--- turn hoisted out of a loop somewhere.)-protectIfHoisted :: SimplifiableLore lore =>-                    SubExp -- ^ Branch condition.-                 -> Bool -- ^ Which side of the branch are we-                         -- protecting here?-                 -> SimpleM lore (a, Stms (Wise lore))-                 -> SimpleM lore (a, Stms (Wise lore))-protectIfHoisted cond side m = do-  (x, stms) <- m-  ops <- asks $ protectHoistedOpS . fst-  runBinder $ do-    if not $ all (safeExp . stmExp) stms-      then do cond' <- if side then return cond-                       else letSubExp "cond_neg" $ BasicOp $ UnOp Not cond-              mapM_ (protectIf ops unsafeOrCostly cond') stms-      else addStms stms-    return x-  where unsafeOrCostly e = not (safeExp e) || not (cheapExp e)---- | We are willing to hoist potentially unsafe statements out of--- loops, but they most be protected by adding a branch on top of--- them.-protectLoopHoisted :: SimplifiableLore lore =>-                      [(FParam (Wise lore),SubExp)]-                   -> [(FParam (Wise lore),SubExp)]-                   -> LoopForm (Wise lore)-                   -> SimpleM lore (a, Stms (Wise lore))-                   -> SimpleM lore (a, Stms (Wise lore))-protectLoopHoisted ctx val form m = do-  (x, stms) <- m-  ops <- asks $ protectHoistedOpS . fst-  runBinder $ do-    if not $ all (safeExp . stmExp) stms-      then do is_nonempty <- checkIfNonEmpty-              mapM_ (protectIf ops (not . safeExp) is_nonempty) stms-      else addStms stms-    return x-  where checkIfNonEmpty =-          case form of-            WhileLoop cond-              | Just (_, cond_init) <--                  find ((==cond) . paramName . fst) $ ctx ++ val ->-                    return cond_init-              | otherwise -> return $ constant True -- infinite loop-            ForLoop _ it bound _ ->-              letSubExp "loop_nonempty" $-              BasicOp $ CmpOp (CmpSlt it) (intConst it 0) bound--protectIf :: MonadBinder m =>-             Protect m-          -> (Exp (Lore m) -> Bool)-          -> SubExp -> Stm (Lore m) -> m ()-protectIf _ _ taken (Let pat aux-                     (If cond taken_body untaken_body (IfDec if_ts IfFallback))) = do-  cond' <- letSubExp "protect_cond_conj" $ BasicOp $ BinOp LogAnd taken cond-  auxing aux $-    letBind pat $ If cond' taken_body untaken_body $-    IfDec if_ts IfFallback-protectIf _ _ taken (Let pat aux (BasicOp (Assert cond msg loc))) = do-  not_taken <- letSubExp "loop_not_taken" $ BasicOp $ UnOp Not taken-  cond' <- letSubExp "protect_assert_disj" $ BasicOp $ BinOp LogOr not_taken cond-  auxing aux $ letBind pat $ BasicOp $ Assert cond' msg loc-protectIf protect _ taken (Let pat aux (Op op))-  | Just m <- protect taken pat op =-      auxing aux m-protectIf _ f taken (Let pat aux e)-  | f e =-      case makeSafe e of-        Just e' ->-          auxing aux $ letBind pat e'-        Nothing -> do-          taken_body <- eBody [pure e]-          untaken_body <- eBody $ map (emptyOfType $ patternContextNames pat)-                                      (patternValueTypes pat)-          if_ts <- expTypesFromPattern pat-          auxing aux $-            letBind pat $ If taken taken_body untaken_body $-            IfDec if_ts IfFallback-protectIf _ _ _ stm =-  addStm stm--makeSafe :: Exp lore -> Maybe (Exp lore)-makeSafe (BasicOp (BinOp (SDiv t _) x y)) =-  Just $ BasicOp (BinOp (SDiv t Safe) x y)-makeSafe (BasicOp (BinOp (SDivUp t _) x y)) =-  Just $ BasicOp (BinOp (SDivUp t Safe) x y)-makeSafe (BasicOp (BinOp (SQuot t _) x y)) =-  Just $ BasicOp (BinOp (SQuot t Safe) x y)-makeSafe (BasicOp (BinOp (UDiv t _) x y)) =-  Just $ BasicOp (BinOp (UDiv t Safe) x y)-makeSafe (BasicOp (BinOp (UDivUp t _) x y)) =-  Just $ BasicOp (BinOp (UDivUp t Safe) x y)-makeSafe (BasicOp (BinOp (SMod t _) x y)) =-  Just $ BasicOp (BinOp (SMod t Safe) x y)-makeSafe (BasicOp (BinOp (SRem t _) x y)) =-  Just $ BasicOp (BinOp (SRem t Safe) x y)-makeSafe (BasicOp (BinOp (UMod t _) x y)) =-  Just $ BasicOp (BinOp (UMod t Safe) x y)-makeSafe _ =-  Nothing--emptyOfType :: MonadBinder m => [VName] -> Type -> m (Exp (Lore m))-emptyOfType _ Mem{} =-  error "emptyOfType: Cannot hoist non-existential memory."-emptyOfType _ (Prim pt) =-  return $ BasicOp $ SubExp $ Constant $ blankPrimValue pt-emptyOfType ctx_names (Array pt shape _) = do-  let dims = map zeroIfContext $ shapeDims shape-  return $ BasicOp $ Scratch pt dims-  where zeroIfContext (Var v) | v `elem` ctx_names = intConst Int32 0-        zeroIfContext se = se---- | Statements that are not worth hoisting out of loops, because they--- are unsafe, and added safety (by 'protectLoopHoisted') may inhibit--- further optimisation..-notWorthHoisting :: ASTLore lore => BlockPred lore-notWorthHoisting _ _ (Let pat _ e) =-  not (safeExp e) && any ((>0) . arrayRank) (patternTypes pat)--hoistStms :: SimplifiableLore lore =>-             RuleBook (Wise lore) -> BlockPred (Wise lore)-          -> ST.SymbolTable (Wise lore) -> UT.UsageTable-          -> Stms (Wise lore)-          -> SimpleM lore (Stms (Wise lore),-                           Stms (Wise lore))-hoistStms rules block vtable uses orig_stms = do-  (blocked, hoisted) <- simplifyStmsBottomUp vtable uses orig_stms-  unless (null hoisted) changed-  return (stmsFromList blocked, stmsFromList hoisted)-  where simplifyStmsBottomUp vtable' uses' stms = do-          (_, stms') <- simplifyStmsBottomUp' vtable' uses' stms-          -- We need to do a final pass to ensure that nothing is-          -- hoisted past something that it depends on.-          let (blocked, hoisted) = partitionEithers $ blockUnhoistedDeps stms'-          return (blocked, hoisted)--        simplifyStmsBottomUp' vtable' uses' stms = do-          opUsage <- asks $ opUsageS . fst-          let usageInStm stm =-                UT.usageInStm stm <>-                case stmExp stm of-                  Op op -> opUsage op-                  _ -> mempty-          foldM (hoistable usageInStm) (uses',[]) $ reverse $ zip (stmsToList stms) vtables-            where vtables = scanl (flip ST.insertStm) vtable' $ stmsToList stms--        hoistable usageInStm (uses',stms) (stm, vtable')-          | not $ any (`UT.isUsedDirectly` uses') $ provides stm = -- Dead statement.-            return (uses', stms)-          | otherwise = do-            res <- localVtable (const vtable') $-                   bottomUpSimplifyStm rules (vtable', uses') stm-            case res of-              Nothing -- Nothing to optimise - see if hoistable.-                | block vtable' uses' stm ->-                    return (expandUsage usageInStm vtable' uses' stm-                            `UT.without` provides stm,-                            Left stm : stms)-                | otherwise ->-                    return (expandUsage usageInStm vtable' uses' stm,-                            Right stm : stms)-              Just optimstms -> do-                changed-                (uses'',stms') <- simplifyStmsBottomUp' vtable' uses' optimstms-                return (uses'', stms'++stms)--blockUnhoistedDeps :: ASTLore lore =>-                      [Either (Stm lore) (Stm lore)]-                   -> [Either (Stm lore) (Stm lore)]-blockUnhoistedDeps = snd . mapAccumL block mempty-  where block blocked (Left need) =-          (blocked <> namesFromList (provides need), Left need)-        block blocked (Right need)-          | blocked `namesIntersect` freeIn need =-            (blocked <> namesFromList (provides need), Left need)-          | otherwise =-            (blocked, Right need)--provides :: Stm lore -> [VName]-provides = patternNames . stmPattern--expandUsage :: (ASTLore lore, Aliased lore) =>-               (Stm lore -> UT.UsageTable) -> ST.SymbolTable lore -> UT.UsageTable-            -> Stm lore -> UT.UsageTable-expandUsage usageInStm vtable utable stm@(Let pat _ e) =-  UT.expand (`ST.lookupAliases` vtable) (usageInStm stm <> usageThroughAliases) <>-  (if any (`UT.isSize` utable) (patternNames pat)-   then UT.sizeUsages (freeIn e)-   else mempty) <>-  utable-  where usageThroughAliases =-          mconcat $ mapMaybe usageThroughBindeeAliases $-          zip (patternNames pat) (patternAliases pat)-        usageThroughBindeeAliases (name, aliases) = do-          uses <- UT.lookup name utable-          return $ mconcat $ map (`UT.usage` uses) $ namesToList aliases--type BlockPred lore = ST.SymbolTable lore -> UT.UsageTable -> Stm lore -> Bool--neverBlocks :: BlockPred lore-neverBlocks _ _ _ = False--alwaysBlocks :: BlockPred lore-alwaysBlocks _ _ _ = True--isFalse :: Bool -> BlockPred lore-isFalse b _ _ _ = not b--orIf :: BlockPred lore -> BlockPred lore -> BlockPred lore-orIf p1 p2 body vtable need = p1 body vtable need || p2 body vtable need--andAlso :: BlockPred lore -> BlockPred lore -> BlockPred lore-andAlso p1 p2 body vtable need = p1 body vtable need && p2 body vtable need--isConsumed :: BlockPred lore-isConsumed _ utable = any (`UT.isConsumed` utable) . patternNames . stmPattern--isOp :: BlockPred lore-isOp _ _ (Let _ _ Op{}) = True-isOp _ _ _ = False--constructBody :: SimplifiableLore lore => Stms (Wise lore) -> Result-              -> SimpleM lore (Body (Wise lore))-constructBody stms res =-  fmap fst $ runBinder $ insertStmsM $ do addStms stms-                                          resultBodyM res--type SimplifiedBody lore a = ((a, UT.UsageTable), Stms (Wise lore))--blockIf :: SimplifiableLore lore =>-           BlockPred (Wise lore)-        -> SimpleM lore (SimplifiedBody lore a)-        -> SimpleM lore ((Stms (Wise lore), a), Stms (Wise lore))-blockIf block m = do-  ((x, usages), stms) <- m-  vtable <- askVtable-  rules <- asksEngineEnv envRules-  (blocked, hoisted) <- hoistStms rules block vtable usages stms-  return ((blocked, x), hoisted)--hasFree :: ASTLore lore => Names -> BlockPred lore-hasFree ks _ _ need = ks `namesIntersect` freeIn need--isNotSafe :: ASTLore lore => BlockPred lore-isNotSafe _ _ = not . safeExp . stmExp--isInPlaceBound :: BlockPred m-isInPlaceBound _ _ = isUpdate . stmExp-  where isUpdate (BasicOp Update{}) = True-        isUpdate _ = False--isNotCheap :: ASTLore lore => BlockPred lore-isNotCheap _ _ = not . cheapStm--cheapStm :: ASTLore lore => Stm lore -> Bool-cheapStm = cheapExp . stmExp--cheapExp :: ASTLore lore => Exp lore -> Bool-cheapExp (BasicOp BinOp{})        = True-cheapExp (BasicOp SubExp{})       = True-cheapExp (BasicOp UnOp{})         = True-cheapExp (BasicOp CmpOp{})        = True-cheapExp (BasicOp ConvOp{})       = True-cheapExp (BasicOp Copy{})         = False-cheapExp (BasicOp Manifest{})     = False-cheapExp DoLoop{}                 = False-cheapExp (If _ tbranch fbranch _) = all cheapStm (bodyStms tbranch) &&-                                    all cheapStm (bodyStms fbranch)-cheapExp (Op op)                  = cheapOp op-cheapExp _                        = True -- Used to be False, but-                                         -- let's try it out.--stmIs :: (Stm lore -> Bool) -> BlockPred lore-stmIs f _ _ = f--loopInvariantStm :: ASTLore lore => ST.SymbolTable lore -> Stm lore -> Bool-loopInvariantStm vtable =-  all (`nameIn` ST.availableAtClosestLoop vtable) . namesToList . freeIn--hoistCommon :: SimplifiableLore lore =>-               SubExp -> IfSort-            -> SimplifiedBody lore Result-            -> SimplifiedBody lore Result-            -> SimpleM lore (Body (Wise lore),-                             Body (Wise lore),-                             Stms (Wise lore))-hoistCommon cond ifsort ((res1, usages1), stms1) ((res2, usages2), stms2) = do-  is_alloc_fun <- asksEngineEnv $ isAllocation  . envHoistBlockers-  branch_blocker <- asksEngineEnv $ blockHoistBranch . envHoistBlockers-  vtable <- askVtable-  let -- We are unwilling to hoist things that are unsafe or costly,-      -- *except* if they are invariant to the most enclosing loop,-      -- because in that case they will also be hoisted past that-      -- loop.-      ---      -- We also try very hard to hoist allocations or anything that-      -- contributes to memory or array size, because that will allow-      -- allocations to be hoisted.-      cond_loop_invariant =-        all (`nameIn` ST.availableAtClosestLoop vtable) $ namesToList $ freeIn cond--      desirableToHoist stm =-          is_alloc_fun stm ||-          (ST.loopDepth vtable > 0 &&-           cond_loop_invariant &&-           ifsort /= IfFallback &&-           loopInvariantStm vtable stm)--      -- No matter what, we always want to hoist constants as much as-      -- possible.-      isNotHoistableBnd _ _ (Let _ _ (BasicOp ArrayLit{})) = False-      isNotHoistableBnd _ _ (Let _ _ (BasicOp SubExp{})) = False-      isNotHoistableBnd _ usages (Let pat _ _)-        | any (`UT.isSize` usages) $ patternNames pat =-            False-      isNotHoistableBnd _ _ _ =-        -- Hoist aggressively out of versioning branches.-        ifsort /= IfEquiv--      block = branch_blocker `orIf`-              ((isNotSafe `orIf` isNotCheap) `andAlso` stmIs (not . desirableToHoist))-              `orIf` isInPlaceBound `orIf` isNotHoistableBnd--  rules <- asksEngineEnv envRules-  (body1_bnds', safe1) <- protectIfHoisted cond True $-                          hoistStms rules block vtable usages1 stms1-  (body2_bnds', safe2) <- protectIfHoisted cond False $-                          hoistStms rules block vtable usages2 stms2-  let hoistable = safe1 <> safe2-  body1' <- constructBody body1_bnds' res1-  body2' <- constructBody body2_bnds' res2-  return (body1', body2', hoistable)---- | Simplify a single body.  The @[Diet]@ only covers the value--- elements, because the context cannot be consumed.-simplifyBody :: SimplifiableLore lore =>-                [Diet] -> Body lore -> SimpleM lore (SimplifiedBody lore Result)-simplifyBody ds (Body _ bnds res) =-  simplifyStms bnds $ do res' <- simplifyResult ds res-                         return (res', mempty)---- | Simplify a single 'Result'.  The @[Diet]@ only covers the value--- elements, because the context cannot be consumed.-simplifyResult :: SimplifiableLore lore =>-                  [Diet] -> Result -> SimpleM lore (Result, UT.UsageTable)-simplifyResult ds res = do-  let (ctx_res, val_res) = splitFromEnd (length ds) res-  -- Copy propagation is a little trickier here, because there is no-  -- place to put the certificates when copy-propagating a certified-  -- statement.  However, for results in the *context*, it is OK to-  -- just throw away the certificates, because for the program to be-  -- type-correct, those statements must anyway be used (or-  -- copy-propagated into) the statements producing the value result.-  (ctx_res', _ctx_res_cs) <- collectCerts $ mapM simplify ctx_res-  val_res' <- mapM simplify' val_res--  let consumption = consumeResult $ zip ds val_res'-      res' = ctx_res' <> val_res'-  return (res', UT.usages (freeIn res') <> consumption)--  where simplify' (Var name) = do-          bnd <- ST.lookupSubExp name <$> askVtable-          case bnd of-            Just (Constant v, cs)-              | cs == mempty -> return $ Constant v-            Just (Var id', cs)-              | cs == mempty -> return $ Var id'-            _                -> return $ Var name-        simplify' (Constant v) =-          return $ Constant v--isDoLoopResult :: Result -> UT.UsageTable-isDoLoopResult = mconcat . map checkForVar-  where checkForVar (Var ident) = UT.inResultUsage ident-        checkForVar _           = mempty--simplifyStms :: SimplifiableLore lore =>-                Stms lore -> SimpleM lore (a, Stms (Wise lore))-             -> SimpleM lore (a, Stms (Wise lore))-simplifyStms stms m =-  case stmsHead stms of-    Nothing -> inspectStms mempty m-    Just (Let pat (StmAux stm_cs attrs dec) e, stms') -> do-      stm_cs' <- simplify stm_cs-      ((e', e_stms), e_cs) <- collectCerts $ simplifyExp e-      (pat', pat_cs) <- collectCerts $ simplifyPattern pat-      let cs = stm_cs'<>e_cs<>pat_cs-      inspectStms e_stms $-        inspectStm (mkWiseLetStm pat' (StmAux cs attrs dec) e') $-        simplifyStms stms' m--inspectStm :: SimplifiableLore lore =>-              Stm (Wise lore) -> SimpleM lore (a, Stms (Wise lore))-           -> SimpleM lore (a, Stms (Wise lore))-inspectStm = inspectStms . oneStm--inspectStms :: SimplifiableLore lore =>-               Stms (Wise lore)-            -> SimpleM lore (a, Stms (Wise lore))-            -> SimpleM lore (a, Stms (Wise lore))-inspectStms stms m =-  case stmsHead stms of-    Nothing -> m-    Just (stm, stms') -> do-      vtable <- askVtable-      rules <- asksEngineEnv envRules-      simplified <- topDownSimplifyStm rules vtable stm-      case simplified of-        Just newbnds -> changed >> inspectStms (newbnds <> stms') m-        Nothing      -> do (x, stms'') <- localVtable (ST.insertStm stm) $ inspectStms stms' m-                           return (x, oneStm stm <> stms'')--simplifyOp :: Op lore -> SimpleM lore (Op (Wise lore), Stms (Wise lore))-simplifyOp op = do f <- asks $ simplifyOpS . fst-                   f op--simplifyExp :: SimplifiableLore lore =>-               Exp lore -> SimpleM lore (Exp (Wise lore), Stms (Wise lore))--simplifyExp (If cond tbranch fbranch (IfDec ts ifsort)) = do-  -- Here, we have to check whether 'cond' puts a bound on some free-  -- variable, and if so, chomp it.  We should also try to do CSE-  -- across branches.-  cond' <- simplify cond-  ts' <- mapM simplify ts-  -- FIXME: we have to be conservative about the diet here, because we-  -- lack proper ifnormation.  Something is wrong with the order in-  -- which the simplifier does things - it should be purely bottom-up-  -- (or else, If expressions should indicate explicitly the diet of-  -- their return types).-  let ds = map (const Consume) ts-  tbranch' <- simplifyBody ds tbranch-  fbranch' <- simplifyBody ds fbranch-  (tbranch'',fbranch'', hoisted) <- hoistCommon cond' ifsort tbranch' fbranch'-  return (If cond' tbranch'' fbranch'' $ IfDec ts' ifsort, hoisted)--simplifyExp (DoLoop ctx val form loopbody) = do-  let (ctxparams, ctxinit) = unzip ctx-      (valparams, valinit) = unzip val-  ctxparams' <- mapM (traverse simplify) ctxparams-  ctxinit' <- mapM simplify ctxinit-  valparams' <- mapM (traverse simplify) valparams-  valinit' <- mapM simplify valinit-  let ctx' = zip ctxparams' ctxinit'-      val' = zip valparams' valinit'-      diets = map (diet . paramDeclType) valparams'-  (form', boundnames, wrapbody) <- case form of-    ForLoop loopvar it boundexp loopvars -> do-      boundexp' <- simplify boundexp-      let (loop_params, loop_arrs) = unzip loopvars-      loop_params' <- mapM (traverse simplify) loop_params-      loop_arrs' <- mapM simplify loop_arrs-      let form' = ForLoop loopvar it boundexp' (zip loop_params' loop_arrs')-      return (form',-              namesFromList (loopvar : map paramName loop_params') <> fparamnames,-              bindLoopVar loopvar it boundexp' .-              protectLoopHoisted ctx' val' form' .-              bindArrayLParams loop_params')-    WhileLoop cond -> do-      cond' <- simplify cond-      return (WhileLoop cond',-              fparamnames,-              protectLoopHoisted ctx' val' (WhileLoop cond'))-  seq_blocker <- asksEngineEnv $ blockHoistSeq . envHoistBlockers-  ((loopstms, loopres), hoisted) <--    enterLoop $ consumeMerge $-    bindFParams (ctxparams'++valparams') $ wrapbody $-    blockIf-    (hasFree boundnames `orIf` isConsumed-     `orIf` seq_blocker `orIf` notWorthHoisting) $ do-      ((res, uses), stms) <- simplifyBody diets loopbody-      return ((res, uses <> isDoLoopResult res), stms)-  loopbody' <- constructBody loopstms loopres-  return (DoLoop ctx' val' form' loopbody', hoisted)-  where fparamnames =-          namesFromList (map (paramName . fst) $ ctx++val)-        consumeMerge =-          localVtable $ flip (foldl' (flip ST.consume)) $ namesToList consumed_by_merge-        consumed_by_merge =-          freeIn $ map snd $ filter (unique . paramDeclType . fst) val--simplifyExp (Op op) = do (op', stms) <- simplifyOp op-                         return (Op op', stms)---- Special case for simplification of commutative BinOps where we--- arrange the operands in sorted order.  This can make expressions--- more identical, which helps CSE.-simplifyExp (BasicOp (BinOp op x y))-  | commutativeBinOp op = do-  x' <- simplify x-  y' <- simplify y-  return (BasicOp $ BinOp op (min x' y') (max x' y'), mempty)--simplifyExp e = do e' <- simplifyExpBase e-                   return (e', mempty)--simplifyExpBase :: SimplifiableLore lore =>-                   Exp lore -> SimpleM lore (Exp (Wise lore))-simplifyExpBase = mapExpM hoist-  where hoist = Mapper {-                -- Bodies are handled explicitly because we need to-                -- provide their result diet.-                  mapOnBody =-                  error "Unhandled body in simplification engine."-                , mapOnSubExp = simplify-                -- Lambdas are handled explicitly because we need to-                -- bind their parameters.-                , mapOnVName = simplify-                , mapOnRetType = simplify-                , mapOnBranchType = simplify-                , mapOnFParam =-                  error "Unhandled FParam in simplification engine."-                , mapOnLParam =-                  error "Unhandled LParam in simplification engine."-                , mapOnOp =-                  error "Unhandled Op in simplification engine."-                }--type SimplifiableLore lore = (ASTLore lore,-                              Simplifiable (LetDec lore),-                              Simplifiable (FParamInfo lore),-                              Simplifiable (LParamInfo lore),-                              Simplifiable (RetType lore),-                              Simplifiable (BranchType lore),-                              CanBeWise (Op lore),-                              ST.IndexOp (OpWithWisdom (Op lore)),-                              BinderOps (Wise lore),-                              IsOp (Op lore))--class Simplifiable e where-  simplify :: SimplifiableLore lore => e -> SimpleM lore e--instance (Simplifiable a, Simplifiable b) => Simplifiable (a, b) where-  simplify (x,y) = (,) <$> simplify x <*> simplify y--instance (Simplifiable a, Simplifiable b, Simplifiable c) =>-         Simplifiable (a, b, c) where-  simplify (x,y,z) = (,,) <$> simplify x <*> simplify y <*> simplify z---- Convenient for Scatter.-instance Simplifiable Int where-  simplify = pure--instance Simplifiable a => Simplifiable (Maybe a) where-  simplify Nothing = return Nothing-  simplify (Just x) = Just <$> simplify x--instance Simplifiable a => Simplifiable [a] where-  simplify = mapM simplify--instance Simplifiable SubExp where-  simplify (Var name) = do-    bnd <- ST.lookupSubExp name <$> askVtable-    case bnd of-      Just (Constant v, cs) -> do changed-                                  usedCerts cs-                                  return $ Constant v-      Just (Var id', cs) -> do changed-                               usedCerts cs-                               return $ Var id'-      _              -> return $ Var name-  simplify (Constant v) =-    return $ Constant v--simplifyPattern :: (SimplifiableLore lore, Simplifiable dec) =>-                   PatternT dec-                -> SimpleM lore (PatternT dec)-simplifyPattern pat =-  Pattern <$>-  mapM inspect (patternContextElements pat) <*>-  mapM inspect (patternValueElements pat)-  where inspect (PatElem name lore) = PatElem name <$> simplify lore--instance Simplifiable () where-  simplify = pure--instance Simplifiable VName where-  simplify v = do-    se <- ST.lookupSubExp v <$> askVtable-    case se of-      Just (Var v', cs) -> do changed-                              usedCerts cs-                              return v'-      _             -> return v--instance Simplifiable d => Simplifiable (ShapeBase d) where-  simplify = fmap Shape . simplify . shapeDims--instance Simplifiable ExtSize where-  simplify (Free se) = Free <$> simplify se-  simplify (Ext x)   = return $ Ext x--instance Simplifiable Space where-  simplify (ScalarSpace ds t) = ScalarSpace <$> simplify ds <*> pure t-  simplify s = pure s--instance Simplifiable shape => Simplifiable (TypeBase shape u) where-  simplify (Array et shape u) = do-    shape' <- simplify shape-    return $ Array et shape' u-  simplify (Mem space) =-    Mem <$> simplify space-  simplify (Prim bt) =-    return $ Prim bt--instance Simplifiable d => Simplifiable (DimIndex d) where-  simplify (DimFix i)       = DimFix <$> simplify i-  simplify (DimSlice i n s) = DimSlice <$> simplify i <*> simplify n <*> simplify s--simplifyLambda :: SimplifiableLore lore =>-                  Lambda lore-               -> SimpleM lore (Lambda (Wise lore), Stms (Wise lore))-simplifyLambda lam = do-  par_blocker <- asksEngineEnv $ blockHoistPar . envHoistBlockers-  simplifyLambdaMaybeHoist par_blocker lam--simplifyLambdaNoHoisting :: SimplifiableLore lore =>-                            Lambda lore-                         -> SimpleM lore (Lambda (Wise lore))-simplifyLambdaNoHoisting lam =-  fst <$> simplifyLambdaMaybeHoist (isFalse False) lam--simplifyLambdaMaybeHoist :: SimplifiableLore lore =>-                            BlockPred (Wise lore) -> Lambda lore-                         -> SimpleM lore (Lambda (Wise lore), Stms (Wise lore))-simplifyLambdaMaybeHoist blocked lam@(Lambda params body rettype) = do-  params' <- mapM (traverse simplify) params-  let paramnames = namesFromList $ boundByLambda lam-  ((lamstms, lamres), hoisted) <--    enterLoop $-    bindLParams params' $-    blockIf (blocked `orIf` hasFree paramnames `orIf` isConsumed) $-      simplifyBody (map (const Observe) rettype) body-  body' <- constructBody lamstms lamres-  rettype' <- simplify rettype-  return (Lambda params' body' rettype', hoisted)--consumeResult :: [(Diet, SubExp)] -> UT.UsageTable-consumeResult = mconcat . map inspect-  where inspect (Consume, se) =-          mconcat $ map UT.consumedUsage $ namesToList $ subExpAliases se-        inspect _ = mempty--instance Simplifiable Certificates where-  simplify (Certificates ocs) = Certificates . nub . concat <$> mapM check ocs-    where check idd = do-            vv <- ST.lookupSubExp idd <$> askVtable-            case vv of-              Just (Constant Checked, Certificates cs) -> return cs-              Just (Var idd', _) -> return [idd']-              _ -> return [idd]---insertAllStms :: SimplifiableLore lore =>-                 SimpleM lore (SimplifiedBody lore Result)-              -> SimpleM lore (Body (Wise lore))-insertAllStms = uncurry constructBody . fst <=< blockIf (isFalse False)---simplifyFun :: SimplifiableLore lore =>-               FunDef lore -> SimpleM lore (FunDef (Wise lore))+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Strict #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}++-- |+--+-- Perform general rule-based simplification based on data dependency+-- information.  This module will:+--+--    * Perform common-subexpression elimination (CSE).+--+--    * Hoist expressions out of loops (including lambdas) and+--    branches.  This is done as aggressively as possible.+--+--    * Apply simplification rules (see+--    "Futhark.Optimise.Simplification.Rules").+--+-- If you just want to run the simplifier as simply as possible, you+-- may prefer to use the "Futhark.Optimise.Simplify" module.+module Futhark.Optimise.Simplify.Engine+  ( -- * Monadic interface+    SimpleM,+    runSimpleM,+    SimpleOps (..),+    SimplifyOp,+    bindableSimpleOps,+    Env (envHoistBlockers, envRules),+    emptyEnv,+    HoistBlockers (..),+    neverBlocks,+    noExtraHoistBlockers,+    neverHoist,+    BlockPred,+    orIf,+    hasFree,+    isConsumed,+    isFalse,+    isOp,+    isNotSafe,+    asksEngineEnv,+    askVtable,+    localVtable,++    -- * Building blocks+    SimplifiableLore,+    Simplifiable (..),+    simplifyStms,+    simplifyFun,+    simplifyLambda,+    simplifyLambdaNoHoisting,+    bindLParams,+    simplifyBody,+    SimplifiedBody,+    ST.SymbolTable,+    hoistStms,+    blockIf,+    module Futhark.Optimise.Simplify.Lore,+  )+where++import Control.Monad.Reader+import Control.Monad.State.Strict+import Data.Either+import Data.List (find, foldl', mapAccumL, nub)+import Data.Maybe+import qualified Futhark.Analysis.SymbolTable as ST+import qualified Futhark.Analysis.UsageTable as UT+import Futhark.Construct+import Futhark.IR+import Futhark.IR.Prop.Aliases+import Futhark.Optimise.Simplify.Lore+import Futhark.Optimise.Simplify.Rule+import Futhark.Util (splitFromEnd)++data HoistBlockers lore = HoistBlockers+  { -- | Blocker for hoisting out of parallel loops.+    blockHoistPar :: BlockPred (Wise lore),+    -- | Blocker for hoisting out of sequential loops.+    blockHoistSeq :: BlockPred (Wise lore),+    -- | Blocker for hoisting out of branches.+    blockHoistBranch :: BlockPred (Wise lore),+    isAllocation :: Stm (Wise lore) -> Bool+  }++noExtraHoistBlockers :: HoistBlockers lore+noExtraHoistBlockers =+  HoistBlockers neverBlocks neverBlocks neverBlocks (const False)++neverHoist :: HoistBlockers lore+neverHoist =+  HoistBlockers alwaysBlocks alwaysBlocks alwaysBlocks (const False)++data Env lore = Env+  { envRules :: RuleBook (Wise lore),+    envHoistBlockers :: HoistBlockers lore,+    envVtable :: ST.SymbolTable (Wise lore)+  }++emptyEnv :: RuleBook (Wise lore) -> HoistBlockers lore -> Env lore+emptyEnv rules blockers =+  Env+    { envRules = rules,+      envHoistBlockers = blockers,+      envVtable = mempty+    }++type Protect m = SubExp -> Pattern (Lore m) -> Op (Lore m) -> Maybe (m ())++data SimpleOps lore = SimpleOps+  { mkExpDecS ::+      ST.SymbolTable (Wise lore) ->+      Pattern (Wise lore) ->+      Exp (Wise lore) ->+      SimpleM lore (ExpDec (Wise lore)),+    mkBodyS ::+      ST.SymbolTable (Wise lore) ->+      Stms (Wise lore) ->+      Result ->+      SimpleM lore (Body (Wise lore)),+    -- | Make a hoisted Op safe.  The SubExp is a boolean+    -- that is true when the value of the statement will+    -- actually be used.+    protectHoistedOpS :: Protect (Binder (Wise lore)),+    opUsageS :: Op (Wise lore) -> UT.UsageTable,+    simplifyOpS :: SimplifyOp lore (Op lore)+  }++type SimplifyOp lore op = op -> SimpleM lore (OpWithWisdom op, Stms (Wise lore))++bindableSimpleOps ::+  (SimplifiableLore lore, Bindable lore) =>+  SimplifyOp lore (Op lore) ->+  SimpleOps lore+bindableSimpleOps =+  SimpleOps mkExpDecS' mkBodyS' protectHoistedOpS' (const mempty)+  where+    mkExpDecS' _ pat e = return $ mkExpDec pat e+    mkBodyS' _ bnds res = return $ mkBody bnds res+    protectHoistedOpS' _ _ _ = Nothing++newtype SimpleM lore a+  = SimpleM+      ( ReaderT+          (SimpleOps lore, Env lore)+          (State (VNameSource, Bool, Certificates))+          a+      )+  deriving+    ( Applicative,+      Functor,+      Monad,+      MonadReader (SimpleOps lore, Env lore),+      MonadState (VNameSource, Bool, Certificates)+    )++instance MonadFreshNames (SimpleM lore) where+  putNameSource src = modify $ \(_, b, c) -> (src, b, c)+  getNameSource = gets $ \(a, _, _) -> a++instance SimplifiableLore lore => HasScope (Wise lore) (SimpleM lore) where+  askScope = ST.toScope <$> askVtable+  lookupType name = do+    vtable <- askVtable+    case ST.lookupType name vtable of+      Just t -> return t+      Nothing ->+        error $+          "SimpleM.lookupType: cannot find variable "+            ++ pretty name+            ++ " in symbol table."++instance+  SimplifiableLore lore =>+  LocalScope (Wise lore) (SimpleM lore)+  where+  localScope types = localVtable (<> ST.fromScope types)++runSimpleM ::+  SimpleM lore a ->+  SimpleOps lore ->+  Env lore ->+  VNameSource ->+  ((a, Bool), VNameSource)+runSimpleM (SimpleM m) simpl env src =+  let (x, (src', b, _)) = runState (runReaderT m (simpl, env)) (src, False, mempty)+   in ((x, b), src')++askEngineEnv :: SimpleM lore (Env lore)+askEngineEnv = asks snd++asksEngineEnv :: (Env lore -> a) -> SimpleM lore a+asksEngineEnv f = f <$> askEngineEnv++askVtable :: SimpleM lore (ST.SymbolTable (Wise lore))+askVtable = asksEngineEnv envVtable++localVtable ::+  (ST.SymbolTable (Wise lore) -> ST.SymbolTable (Wise lore)) ->+  SimpleM lore a ->+  SimpleM lore a+localVtable f = local $ \(ops, env) -> (ops, env {envVtable = f $ envVtable env})++collectCerts :: SimpleM lore a -> SimpleM lore (a, Certificates)+collectCerts m = do+  x <- m+  (a, b, cs) <- get+  put (a, b, mempty)+  return (x, cs)++-- | Mark that we have changed something and it would be a good idea+-- to re-run the simplifier.+changed :: SimpleM lore ()+changed = modify $ \(src, _, cs) -> (src, True, cs)++usedCerts :: Certificates -> SimpleM lore ()+usedCerts cs = modify $ \(a, b, c) -> (a, b, cs <> c)++enterLoop :: SimpleM lore a -> SimpleM lore a+enterLoop = localVtable ST.deepen++bindFParams :: SimplifiableLore lore => [FParam (Wise lore)] -> SimpleM lore a -> SimpleM lore a+bindFParams params =+  localVtable $ ST.insertFParams params++bindLParams :: SimplifiableLore lore => [LParam (Wise lore)] -> SimpleM lore a -> SimpleM lore a+bindLParams params =+  localVtable $ \vtable -> foldr ST.insertLParam vtable params++bindArrayLParams ::+  SimplifiableLore lore =>+  [LParam (Wise lore)] ->+  SimpleM lore a ->+  SimpleM lore a+bindArrayLParams params =+  localVtable $ \vtable -> foldl' (flip ST.insertLParam) vtable params++bindMerge ::+  SimplifiableLore lore =>+  [(FParam (Wise lore), SubExp, SubExp)] ->+  SimpleM lore a ->+  SimpleM lore a+bindMerge = localVtable . ST.insertLoopMerge++bindLoopVar :: SimplifiableLore lore => VName -> IntType -> SubExp -> SimpleM lore a -> SimpleM lore a+bindLoopVar var it bound =+  localVtable $ ST.insertLoopVar var it bound++-- | We are willing to hoist potentially unsafe statements out of+-- branches, but they most be protected by adding a branch on top of+-- them.  (This means such hoisting is not worth it unless they are in+-- turn hoisted out of a loop somewhere.)+protectIfHoisted ::+  SimplifiableLore lore =>+  -- | Branch condition.+  SubExp ->+  -- | Which side of the branch are we+  -- protecting here?+  Bool ->+  SimpleM lore (a, Stms (Wise lore)) ->+  SimpleM lore (a, Stms (Wise lore))+protectIfHoisted cond side m = do+  (x, stms) <- m+  ops <- asks $ protectHoistedOpS . fst+  runBinder $ do+    if not $ all (safeExp . stmExp) stms+      then do+        cond' <-+          if side+            then return cond+            else letSubExp "cond_neg" $ BasicOp $ UnOp Not cond+        mapM_ (protectIf ops unsafeOrCostly cond') stms+      else addStms stms+    return x+  where+    unsafeOrCostly e = not (safeExp e) || not (cheapExp e)++-- | We are willing to hoist potentially unsafe statements out of+-- loops, but they most be protected by adding a branch on top of+-- them.+protectLoopHoisted ::+  SimplifiableLore lore =>+  [(FParam (Wise lore), SubExp)] ->+  [(FParam (Wise lore), SubExp)] ->+  LoopForm (Wise lore) ->+  SimpleM lore (a, Stms (Wise lore)) ->+  SimpleM lore (a, Stms (Wise lore))+protectLoopHoisted ctx val form m = do+  (x, stms) <- m+  ops <- asks $ protectHoistedOpS . fst+  runBinder $ do+    if not $ all (safeExp . stmExp) stms+      then do+        is_nonempty <- checkIfNonEmpty+        mapM_ (protectIf ops (not . safeExp) is_nonempty) stms+      else addStms stms+    return x+  where+    checkIfNonEmpty =+      case form of+        WhileLoop cond+          | Just (_, cond_init) <-+              find ((== cond) . paramName . fst) $ ctx ++ val ->+            return cond_init+          | otherwise -> return $ constant True -- infinite loop+        ForLoop _ it bound _ ->+          letSubExp "loop_nonempty" $+            BasicOp $ CmpOp (CmpSlt it) (intConst it 0) bound++protectIf ::+  MonadBinder m =>+  Protect m ->+  (Exp (Lore m) -> Bool) ->+  SubExp ->+  Stm (Lore m) ->+  m ()+protectIf+  _+  _+  taken+  ( Let+      pat+      aux+      (If cond taken_body untaken_body (IfDec if_ts IfFallback))+    ) = do+    cond' <- letSubExp "protect_cond_conj" $ BasicOp $ BinOp LogAnd taken cond+    auxing aux $+      letBind pat $+        If cond' taken_body untaken_body $+          IfDec if_ts IfFallback+protectIf _ _ taken (Let pat aux (BasicOp (Assert cond msg loc))) = do+  not_taken <- letSubExp "loop_not_taken" $ BasicOp $ UnOp Not taken+  cond' <- letSubExp "protect_assert_disj" $ BasicOp $ BinOp LogOr not_taken cond+  auxing aux $ letBind pat $ BasicOp $ Assert cond' msg loc+protectIf protect _ taken (Let pat aux (Op op))+  | Just m <- protect taken pat op =+    auxing aux m+protectIf _ f taken (Let pat aux e)+  | f e =+    case makeSafe e of+      Just e' ->+        auxing aux $ letBind pat e'+      Nothing -> do+        taken_body <- eBody [pure e]+        untaken_body <-+          eBody $+            map+              (emptyOfType $ patternContextNames pat)+              (patternValueTypes pat)+        if_ts <- expTypesFromPattern pat+        auxing aux $+          letBind pat $+            If taken taken_body untaken_body $+              IfDec if_ts IfFallback+protectIf _ _ _ stm =+  addStm stm++makeSafe :: Exp lore -> Maybe (Exp lore)+makeSafe (BasicOp (BinOp (SDiv t _) x y)) =+  Just $ BasicOp (BinOp (SDiv t Safe) x y)+makeSafe (BasicOp (BinOp (SDivUp t _) x y)) =+  Just $ BasicOp (BinOp (SDivUp t Safe) x y)+makeSafe (BasicOp (BinOp (SQuot t _) x y)) =+  Just $ BasicOp (BinOp (SQuot t Safe) x y)+makeSafe (BasicOp (BinOp (UDiv t _) x y)) =+  Just $ BasicOp (BinOp (UDiv t Safe) x y)+makeSafe (BasicOp (BinOp (UDivUp t _) x y)) =+  Just $ BasicOp (BinOp (UDivUp t Safe) x y)+makeSafe (BasicOp (BinOp (SMod t _) x y)) =+  Just $ BasicOp (BinOp (SMod t Safe) x y)+makeSafe (BasicOp (BinOp (SRem t _) x y)) =+  Just $ BasicOp (BinOp (SRem t Safe) x y)+makeSafe (BasicOp (BinOp (UMod t _) x y)) =+  Just $ BasicOp (BinOp (UMod t Safe) x y)+makeSafe _ =+  Nothing++emptyOfType :: MonadBinder m => [VName] -> Type -> m (Exp (Lore m))+emptyOfType _ Mem {} =+  error "emptyOfType: Cannot hoist non-existential memory."+emptyOfType _ (Prim pt) =+  return $ BasicOp $ SubExp $ Constant $ blankPrimValue pt+emptyOfType ctx_names (Array pt shape _) = do+  let dims = map zeroIfContext $ shapeDims shape+  return $ BasicOp $ Scratch pt dims+  where+    zeroIfContext (Var v) | v `elem` ctx_names = intConst Int32 0+    zeroIfContext se = se++-- | Statements that are not worth hoisting out of loops, because they+-- are unsafe, and added safety (by 'protectLoopHoisted') may inhibit+-- further optimisation..+notWorthHoisting :: ASTLore lore => BlockPred lore+notWorthHoisting _ _ (Let pat _ e) =+  not (safeExp e) && any ((> 0) . arrayRank) (patternTypes pat)++hoistStms ::+  SimplifiableLore lore =>+  RuleBook (Wise lore) ->+  BlockPred (Wise lore) ->+  ST.SymbolTable (Wise lore) ->+  UT.UsageTable ->+  Stms (Wise lore) ->+  SimpleM+    lore+    ( Stms (Wise lore),+      Stms (Wise lore)+    )+hoistStms rules block vtable uses orig_stms = do+  (blocked, hoisted) <- simplifyStmsBottomUp vtable uses orig_stms+  unless (null hoisted) changed+  return (stmsFromList blocked, stmsFromList hoisted)+  where+    simplifyStmsBottomUp vtable' uses' stms = do+      (_, stms') <- simplifyStmsBottomUp' vtable' uses' stms+      -- We need to do a final pass to ensure that nothing is+      -- hoisted past something that it depends on.+      let (blocked, hoisted) = partitionEithers $ blockUnhoistedDeps stms'+      return (blocked, hoisted)++    simplifyStmsBottomUp' vtable' uses' stms = do+      opUsage <- asks $ opUsageS . fst+      let usageInStm stm =+            UT.usageInStm stm+              <> case stmExp stm of+                Op op -> opUsage op+                _ -> mempty+      foldM (hoistable usageInStm) (uses', []) $ reverse $ zip (stmsToList stms) vtables+      where+        vtables = scanl (flip ST.insertStm) vtable' $ stmsToList stms++    hoistable usageInStm (uses', stms) (stm, vtable')+      | not $ any (`UT.isUsedDirectly` uses') $ provides stm -- Dead statement.+        =+        return (uses', stms)+      | otherwise = do+        res <-+          localVtable (const vtable') $+            bottomUpSimplifyStm rules (vtable', uses') stm+        case res of+          Nothing -- Nothing to optimise - see if hoistable.+            | block vtable' uses' stm ->+              return+                ( expandUsage usageInStm vtable' uses' stm+                    `UT.without` provides stm,+                  Left stm : stms+                )+            | otherwise ->+              return+                ( expandUsage usageInStm vtable' uses' stm,+                  Right stm : stms+                )+          Just optimstms -> do+            changed+            (uses'', stms') <- simplifyStmsBottomUp' vtable' uses' optimstms+            return (uses'', stms' ++ stms)++blockUnhoistedDeps ::+  ASTLore lore =>+  [Either (Stm lore) (Stm lore)] ->+  [Either (Stm lore) (Stm lore)]+blockUnhoistedDeps = snd . mapAccumL block mempty+  where+    block blocked (Left need) =+      (blocked <> namesFromList (provides need), Left need)+    block blocked (Right need)+      | blocked `namesIntersect` freeIn need =+        (blocked <> namesFromList (provides need), Left need)+      | otherwise =+        (blocked, Right need)++provides :: Stm lore -> [VName]+provides = patternNames . stmPattern++expandUsage ::+  (ASTLore lore, Aliased lore) =>+  (Stm lore -> UT.UsageTable) ->+  ST.SymbolTable lore ->+  UT.UsageTable ->+  Stm lore ->+  UT.UsageTable+expandUsage usageInStm vtable utable stm@(Let pat _ e) =+  UT.expand (`ST.lookupAliases` vtable) (usageInStm stm <> usageThroughAliases)+    <> ( if any (`UT.isSize` utable) (patternNames pat)+           then UT.sizeUsages (freeIn e)+           else mempty+       )+    <> utable+  where+    usageThroughAliases =+      mconcat $+        mapMaybe usageThroughBindeeAliases $+          zip (patternNames pat) (patternAliases pat)+    usageThroughBindeeAliases (name, aliases) = do+      uses <- UT.lookup name utable+      return $ mconcat $ map (`UT.usage` uses) $ namesToList aliases++type BlockPred lore = ST.SymbolTable lore -> UT.UsageTable -> Stm lore -> Bool++neverBlocks :: BlockPred lore+neverBlocks _ _ _ = False++alwaysBlocks :: BlockPred lore+alwaysBlocks _ _ _ = True++isFalse :: Bool -> BlockPred lore+isFalse b _ _ _ = not b++orIf :: BlockPred lore -> BlockPred lore -> BlockPred lore+orIf p1 p2 body vtable need = p1 body vtable need || p2 body vtable need++andAlso :: BlockPred lore -> BlockPred lore -> BlockPred lore+andAlso p1 p2 body vtable need = p1 body vtable need && p2 body vtable need++isConsumed :: BlockPred lore+isConsumed _ utable = any (`UT.isConsumed` utable) . patternNames . stmPattern++isOp :: BlockPred lore+isOp _ _ (Let _ _ Op {}) = True+isOp _ _ _ = False++constructBody ::+  SimplifiableLore lore =>+  Stms (Wise lore) ->+  Result ->+  SimpleM lore (Body (Wise lore))+constructBody stms res =+  fmap fst $+    runBinder $+      insertStmsM $ do+        addStms stms+        resultBodyM res++type SimplifiedBody lore a = ((a, UT.UsageTable), Stms (Wise lore))++blockIf ::+  SimplifiableLore lore =>+  BlockPred (Wise lore) ->+  SimpleM lore (SimplifiedBody lore a) ->+  SimpleM lore ((Stms (Wise lore), a), Stms (Wise lore))+blockIf block m = do+  ((x, usages), stms) <- m+  vtable <- askVtable+  rules <- asksEngineEnv envRules+  (blocked, hoisted) <- hoistStms rules block vtable usages stms+  return ((blocked, x), hoisted)++hasFree :: ASTLore lore => Names -> BlockPred lore+hasFree ks _ _ need = ks `namesIntersect` freeIn need++isNotSafe :: ASTLore lore => BlockPred lore+isNotSafe _ _ = not . safeExp . stmExp++isInPlaceBound :: BlockPred m+isInPlaceBound _ _ = isUpdate . stmExp+  where+    isUpdate (BasicOp Update {}) = True+    isUpdate _ = False++isNotCheap :: ASTLore lore => BlockPred lore+isNotCheap _ _ = not . cheapStm++cheapStm :: ASTLore lore => Stm lore -> Bool+cheapStm = cheapExp . stmExp++cheapExp :: ASTLore lore => Exp lore -> Bool+cheapExp (BasicOp BinOp {}) = True+cheapExp (BasicOp SubExp {}) = True+cheapExp (BasicOp UnOp {}) = True+cheapExp (BasicOp CmpOp {}) = True+cheapExp (BasicOp ConvOp {}) = True+cheapExp (BasicOp Copy {}) = False+cheapExp (BasicOp Manifest {}) = False+cheapExp DoLoop {} = False+cheapExp (If _ tbranch fbranch _) =+  all cheapStm (bodyStms tbranch)+    && all cheapStm (bodyStms fbranch)+cheapExp (Op op) = cheapOp op+cheapExp _ = True -- Used to be False, but+-- let's try it out.++stmIs :: (Stm lore -> Bool) -> BlockPred lore+stmIs f _ _ = f++loopInvariantStm :: ASTLore lore => ST.SymbolTable lore -> Stm lore -> Bool+loopInvariantStm vtable =+  all (`nameIn` ST.availableAtClosestLoop vtable) . namesToList . freeIn++hoistCommon ::+  SimplifiableLore lore =>+  SubExp ->+  IfSort ->+  SimplifiedBody lore Result ->+  SimplifiedBody lore Result ->+  SimpleM+    lore+    ( Body (Wise lore),+      Body (Wise lore),+      Stms (Wise lore)+    )+hoistCommon cond ifsort ((res1, usages1), stms1) ((res2, usages2), stms2) = do+  is_alloc_fun <- asksEngineEnv $ isAllocation . envHoistBlockers+  branch_blocker <- asksEngineEnv $ blockHoistBranch . envHoistBlockers+  vtable <- askVtable+  let -- We are unwilling to hoist things that are unsafe or costly,++      -- because in that case they will also be hoisted past that+      -- loop.+      --+      -- We also try very hard to hoist allocations or anything that+      -- contributes to memory or array size, because that will allow+      -- allocations to be hoisted.+      cond_loop_invariant =+        all (`nameIn` ST.availableAtClosestLoop vtable) $ namesToList $ freeIn cond++      desirableToHoist stm =+        is_alloc_fun stm+          || ( ST.loopDepth vtable > 0+                 && cond_loop_invariant+                 && ifsort /= IfFallback+                 && loopInvariantStm vtable stm+             )++      -- No matter what, we always want to hoist constants as much as+      -- possible.+      isNotHoistableBnd _ _ (Let _ _ (BasicOp ArrayLit {})) = False+      isNotHoistableBnd _ _ (Let _ _ (BasicOp SubExp {})) = False+      isNotHoistableBnd _ usages (Let pat _ _)+        | any (`UT.isSize` usages) $ patternNames pat =+          False+      isNotHoistableBnd _ _ stm+        | is_alloc_fun stm = False+      isNotHoistableBnd _ _ _ =+        -- Hoist aggressively out of versioning branches.+        ifsort /= IfEquiv++      block =+        branch_blocker+          `orIf` ((isNotSafe `orIf` isNotCheap) `andAlso` stmIs (not . desirableToHoist))+          `orIf` isInPlaceBound+          `orIf` isNotHoistableBnd++  rules <- asksEngineEnv envRules+  (body1_bnds', safe1) <-+    protectIfHoisted cond True $+      hoistStms rules block vtable usages1 stms1+  (body2_bnds', safe2) <-+    protectIfHoisted cond False $+      hoistStms rules block vtable usages2 stms2+  let hoistable = safe1 <> safe2+  body1' <- constructBody body1_bnds' res1+  body2' <- constructBody body2_bnds' res2+  return (body1', body2', hoistable)++-- | Simplify a single body.  The @[Diet]@ only covers the value+-- elements, because the context cannot be consumed.+simplifyBody ::+  SimplifiableLore lore =>+  [Diet] ->+  Body lore ->+  SimpleM lore (SimplifiedBody lore Result)+simplifyBody ds (Body _ bnds res) =+  simplifyStms bnds $ do+    res' <- simplifyResult ds res+    return (res', mempty)++-- | Simplify a single 'Result'.  The @[Diet]@ only covers the value+-- elements, because the context cannot be consumed.+simplifyResult ::+  SimplifiableLore lore =>+  [Diet] ->+  Result ->+  SimpleM lore (Result, UT.UsageTable)+simplifyResult ds res = do+  let (ctx_res, val_res) = splitFromEnd (length ds) res+  -- Copy propagation is a little trickier here, because there is no+  -- place to put the certificates when copy-propagating a certified+  -- statement.  However, for results in the *context*, it is OK to+  -- just throw away the certificates, because for the program to be+  -- type-correct, those statements must anyway be used (or+  -- copy-propagated into) the statements producing the value result.+  (ctx_res', _ctx_res_cs) <- collectCerts $ mapM simplify ctx_res+  val_res' <- mapM simplify' val_res++  let consumption = consumeResult $ zip ds val_res'+      res' = ctx_res' <> val_res'+  return (res', UT.usages (freeIn res') <> consumption)+  where+    simplify' (Var name) = do+      bnd <- ST.lookupSubExp name <$> askVtable+      case bnd of+        Just (Constant v, cs)+          | cs == mempty -> return $ Constant v+        Just (Var id', cs)+          | cs == mempty -> return $ Var id'+        _ -> return $ Var name+    simplify' (Constant v) =+      return $ Constant v++isDoLoopResult :: Result -> UT.UsageTable+isDoLoopResult = mconcat . map checkForVar+  where+    checkForVar (Var ident) = UT.inResultUsage ident+    checkForVar _ = mempty++simplifyStms ::+  SimplifiableLore lore =>+  Stms lore ->+  SimpleM lore (a, Stms (Wise lore)) ->+  SimpleM lore (a, Stms (Wise lore))+simplifyStms stms m =+  case stmsHead stms of+    Nothing -> inspectStms mempty m+    Just (Let pat (StmAux stm_cs attrs dec) e, stms') -> do+      stm_cs' <- simplify stm_cs+      ((e', e_stms), e_cs) <- collectCerts $ simplifyExp e+      (pat', pat_cs) <- collectCerts $ simplifyPattern pat+      let cs = stm_cs' <> e_cs <> pat_cs+      inspectStms e_stms $+        inspectStm (mkWiseLetStm pat' (StmAux cs attrs dec) e') $+          simplifyStms stms' m++inspectStm ::+  SimplifiableLore lore =>+  Stm (Wise lore) ->+  SimpleM lore (a, Stms (Wise lore)) ->+  SimpleM lore (a, Stms (Wise lore))+inspectStm = inspectStms . oneStm++inspectStms ::+  SimplifiableLore lore =>+  Stms (Wise lore) ->+  SimpleM lore (a, Stms (Wise lore)) ->+  SimpleM lore (a, Stms (Wise lore))+inspectStms stms m =+  case stmsHead stms of+    Nothing -> m+    Just (stm, stms') -> do+      vtable <- askVtable+      rules <- asksEngineEnv envRules+      simplified <- topDownSimplifyStm rules vtable stm+      case simplified of+        Just newbnds -> changed >> inspectStms (newbnds <> stms') m+        Nothing -> do+          (x, stms'') <- localVtable (ST.insertStm stm) $ inspectStms stms' m+          return (x, oneStm stm <> stms'')++simplifyOp :: Op lore -> SimpleM lore (Op (Wise lore), Stms (Wise lore))+simplifyOp op = do+  f <- asks $ simplifyOpS . fst+  f op++simplifyExp ::+  SimplifiableLore lore =>+  Exp lore ->+  SimpleM lore (Exp (Wise lore), Stms (Wise lore))+simplifyExp (If cond tbranch fbranch (IfDec ts ifsort)) = do+  -- Here, we have to check whether 'cond' puts a bound on some free+  -- variable, and if so, chomp it.  We should also try to do CSE+  -- across branches.+  cond' <- simplify cond+  ts' <- mapM simplify ts+  -- FIXME: we have to be conservative about the diet here, because we+  -- lack proper ifnormation.  Something is wrong with the order in+  -- which the simplifier does things - it should be purely bottom-up+  -- (or else, If expressions should indicate explicitly the diet of+  -- their return types).+  let ds = map (const Consume) ts+  tbranch' <- simplifyBody ds tbranch+  fbranch' <- simplifyBody ds fbranch+  (tbranch'', fbranch'', hoisted) <- hoistCommon cond' ifsort tbranch' fbranch'+  return (If cond' tbranch'' fbranch'' $ IfDec ts' ifsort, hoisted)+simplifyExp (DoLoop ctx val form loopbody) = do+  let (ctxparams, ctxinit) = unzip ctx+      (valparams, valinit) = unzip val+  ctxparams' <- mapM (traverse simplify) ctxparams+  ctxinit' <- mapM simplify ctxinit+  valparams' <- mapM (traverse simplify) valparams+  valinit' <- mapM simplify valinit+  let ctx' = zip ctxparams' ctxinit'+      val' = zip valparams' valinit'+      diets = map (diet . paramDeclType) valparams'+  (form', boundnames, wrapbody) <- case form of+    ForLoop loopvar it boundexp loopvars -> do+      boundexp' <- simplify boundexp+      let (loop_params, loop_arrs) = unzip loopvars+      loop_params' <- mapM (traverse simplify) loop_params+      loop_arrs' <- mapM simplify loop_arrs+      let form' = ForLoop loopvar it boundexp' (zip loop_params' loop_arrs')+      return+        ( form',+          namesFromList (loopvar : map paramName loop_params') <> fparamnames,+          bindLoopVar loopvar it boundexp'+            . protectLoopHoisted ctx' val' form'+            . bindArrayLParams loop_params'+        )+    WhileLoop cond -> do+      cond' <- simplify cond+      return+        ( WhileLoop cond',+          fparamnames,+          protectLoopHoisted ctx' val' (WhileLoop cond')+        )+  seq_blocker <- asksEngineEnv $ blockHoistSeq . envHoistBlockers+  ((loopstms, loopres), hoisted) <-+    enterLoop $+      consumeMerge $+        bindMerge (zipWith withRes (ctx' ++ val') (bodyResult loopbody)) $+          wrapbody $+            blockIf+              ( hasFree boundnames `orIf` isConsumed+                  `orIf` seq_blocker+                  `orIf` notWorthHoisting+              )+              $ do+                ((res, uses), stms) <- simplifyBody diets loopbody+                return ((res, uses <> isDoLoopResult res), stms)+  loopbody' <- constructBody loopstms loopres+  return (DoLoop ctx' val' form' loopbody', hoisted)+  where+    fparamnames =+      namesFromList (map (paramName . fst) $ ctx ++ val)+    consumeMerge =+      localVtable $ flip (foldl' (flip ST.consume)) $ namesToList consumed_by_merge+    consumed_by_merge =+      freeIn $ map snd $ filter (unique . paramDeclType . fst) val+    withRes (p, x) y = (p, x, y)+simplifyExp (Op op) = do+  (op', stms) <- simplifyOp op+  return (Op op', stms)++-- Special case for simplification of commutative BinOps where we+-- arrange the operands in sorted order.  This can make expressions+-- more identical, which helps CSE.+simplifyExp (BasicOp (BinOp op x y))+  | commutativeBinOp op = do+    x' <- simplify x+    y' <- simplify y+    return (BasicOp $ BinOp op (min x' y') (max x' y'), mempty)+simplifyExp e = do+  e' <- simplifyExpBase e+  return (e', mempty)++simplifyExpBase ::+  SimplifiableLore lore =>+  Exp lore ->+  SimpleM lore (Exp (Wise lore))+simplifyExpBase = mapExpM hoist+  where+    hoist =+      Mapper+        { -- Bodies are handled explicitly because we need to+          -- provide their result diet.+          mapOnBody =+            error "Unhandled body in simplification engine.",+          mapOnSubExp = simplify,+          -- Lambdas are handled explicitly because we need to+          -- bind their parameters.+          mapOnVName = simplify,+          mapOnRetType = simplify,+          mapOnBranchType = simplify,+          mapOnFParam =+            error "Unhandled FParam in simplification engine.",+          mapOnLParam =+            error "Unhandled LParam in simplification engine.",+          mapOnOp =+            error "Unhandled Op in simplification engine."+        }++type SimplifiableLore lore =+  ( ASTLore lore,+    Simplifiable (LetDec lore),+    Simplifiable (FParamInfo lore),+    Simplifiable (LParamInfo lore),+    Simplifiable (RetType lore),+    Simplifiable (BranchType lore),+    CanBeWise (Op lore),+    ST.IndexOp (OpWithWisdom (Op lore)),+    BinderOps (Wise lore),+    IsOp (Op lore)+  )++class Simplifiable e where+  simplify :: SimplifiableLore lore => e -> SimpleM lore e++instance (Simplifiable a, Simplifiable b) => Simplifiable (a, b) where+  simplify (x, y) = (,) <$> simplify x <*> simplify y++instance+  (Simplifiable a, Simplifiable b, Simplifiable c) =>+  Simplifiable (a, b, c)+  where+  simplify (x, y, z) = (,,) <$> simplify x <*> simplify y <*> simplify z++-- Convenient for Scatter.+instance Simplifiable Int where+  simplify = pure++instance Simplifiable a => Simplifiable (Maybe a) where+  simplify Nothing = return Nothing+  simplify (Just x) = Just <$> simplify x++instance Simplifiable a => Simplifiable [a] where+  simplify = mapM simplify++instance Simplifiable SubExp where+  simplify (Var name) = do+    bnd <- ST.lookupSubExp name <$> askVtable+    case bnd of+      Just (Constant v, cs) -> do+        changed+        usedCerts cs+        return $ Constant v+      Just (Var id', cs) -> do+        changed+        usedCerts cs+        return $ Var id'+      _ -> return $ Var name+  simplify (Constant v) =+    return $ Constant v++simplifyPattern ::+  (SimplifiableLore lore, Simplifiable dec) =>+  PatternT dec ->+  SimpleM lore (PatternT dec)+simplifyPattern pat =+  Pattern+    <$> mapM inspect (patternContextElements pat)+    <*> mapM inspect (patternValueElements pat)+  where+    inspect (PatElem name lore) = PatElem name <$> simplify lore++instance Simplifiable () where+  simplify = pure++instance Simplifiable VName where+  simplify v = do+    se <- ST.lookupSubExp v <$> askVtable+    case se of+      Just (Var v', cs) -> do+        changed+        usedCerts cs+        return v'+      _ -> return v++instance Simplifiable d => Simplifiable (ShapeBase d) where+  simplify = fmap Shape . simplify . shapeDims++instance Simplifiable ExtSize where+  simplify (Free se) = Free <$> simplify se+  simplify (Ext x) = return $ Ext x++instance Simplifiable Space where+  simplify (ScalarSpace ds t) = ScalarSpace <$> simplify ds <*> pure t+  simplify s = pure s++instance Simplifiable shape => Simplifiable (TypeBase shape u) where+  simplify (Array et shape u) = do+    shape' <- simplify shape+    return $ Array et shape' u+  simplify (Mem space) =+    Mem <$> simplify space+  simplify (Prim bt) =+    return $ Prim bt++instance Simplifiable d => Simplifiable (DimIndex d) where+  simplify (DimFix i) = DimFix <$> simplify i+  simplify (DimSlice i n s) = DimSlice <$> simplify i <*> simplify n <*> simplify s++simplifyLambda ::+  SimplifiableLore lore =>+  Lambda lore ->+  SimpleM lore (Lambda (Wise lore), Stms (Wise lore))+simplifyLambda lam = do+  par_blocker <- asksEngineEnv $ blockHoistPar . envHoistBlockers+  simplifyLambdaMaybeHoist par_blocker lam++simplifyLambdaNoHoisting ::+  SimplifiableLore lore =>+  Lambda lore ->+  SimpleM lore (Lambda (Wise lore))+simplifyLambdaNoHoisting lam =+  fst <$> simplifyLambdaMaybeHoist (isFalse False) lam++simplifyLambdaMaybeHoist ::+  SimplifiableLore lore =>+  BlockPred (Wise lore) ->+  Lambda lore ->+  SimpleM lore (Lambda (Wise lore), Stms (Wise lore))+simplifyLambdaMaybeHoist blocked lam@(Lambda params body rettype) = do+  params' <- mapM (traverse simplify) params+  let paramnames = namesFromList $ boundByLambda lam+  ((lamstms, lamres), hoisted) <-+    enterLoop $+      bindLParams params' $+        blockIf (blocked `orIf` hasFree paramnames `orIf` isConsumed) $+          simplifyBody (map (const Observe) rettype) body+  body' <- constructBody lamstms lamres+  rettype' <- simplify rettype+  return (Lambda params' body' rettype', hoisted)++consumeResult :: [(Diet, SubExp)] -> UT.UsageTable+consumeResult = mconcat . map inspect+  where+    inspect (Consume, se) =+      mconcat $ map UT.consumedUsage $ namesToList $ subExpAliases se+    inspect _ = mempty++instance Simplifiable Certificates where+  simplify (Certificates ocs) = Certificates . nub . concat <$> mapM check ocs+    where+      check idd = do+        vv <- ST.lookupSubExp idd <$> askVtable+        case vv of+          Just (Constant Checked, Certificates cs) -> return cs+          Just (Var idd', _) -> return [idd']+          _ -> return [idd]++insertAllStms ::+  SimplifiableLore lore =>+  SimpleM lore (SimplifiedBody lore Result) ->+  SimpleM lore (Body (Wise lore))+insertAllStms = uncurry constructBody . fst <=< blockIf (isFalse False)++simplifyFun ::+  SimplifiableLore lore =>+  FunDef lore ->+  SimpleM lore (FunDef (Wise lore)) simplifyFun (FunDef entry attrs fname rettype params body) = do   rettype' <- simplify rettype   params' <- mapM (traverse simplify) params
src/Futhark/Optimise/Simplify/Lore.hs view
@@ -1,51 +1,63 @@-{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+ -- | Definition of the lore used by the simplification engine. module Futhark.Optimise.Simplify.Lore-       (-         Wise-       , VarWisdom (..)-       , ExpWisdom-       , removeStmWisdom-       , removeLambdaWisdom-       , removeFunDefWisdom-       , removeExpWisdom-       , removePatternWisdom-       , removeBodyWisdom-       , removeScopeWisdom-       , addScopeWisdom-       , addWisdomToPattern-       , mkWiseBody-       , mkWiseLetStm-       , mkWiseExpDec--       , CanBeWise (..)-       )-       where+  ( Wise,+    VarWisdom (..),+    ExpWisdom,+    removeStmWisdom,+    removeLambdaWisdom,+    removeFunDefWisdom,+    removeExpWisdom,+    removePatternWisdom,+    removeBodyWisdom,+    removeScopeWisdom,+    addScopeWisdom,+    addWisdomToPattern,+    mkWiseBody,+    mkWiseLetStm,+    mkWiseExpDec,+    CanBeWise (..),+  )+where +import Control.Category import Control.Monad.Identity import Control.Monad.Reader import qualified Data.Kind import qualified Data.Map.Strict as M-+import Futhark.Analysis.Rephrase+import Futhark.Binder import Futhark.IR-import Futhark.IR.Prop.Aliases import Futhark.IR.Aliases-  (unAliases, AliasDec (..), VarAliases, ConsumedInExp)+  ( AliasDec (..),+    ConsumedInExp,+    VarAliases,+    unAliases,+  ) import qualified Futhark.IR.Aliases as Aliases-import Futhark.Binder+import Futhark.IR.Prop.Aliases import Futhark.Transform.Rename import Futhark.Transform.Substitute-import Futhark.Analysis.Rephrase+import GHC.Generics (Generic)+import Language.SexpGrammar as Sexp hiding (cons)+import Language.SexpGrammar.Generic+import Prelude hiding (id, (.))  data Wise lore  -- | The wisdom of the let-bound variable.-newtype VarWisdom = VarWisdom { varWisdomAliases :: VarAliases }-                  deriving (Eq, Ord, Show)+newtype VarWisdom = VarWisdom {varWisdomAliases :: VarAliases}+  deriving (Eq, Ord, Show, Generic) +instance SexpIso VarWisdom where+  sexpIso = with $ \varwisdom -> sexpIso >>> varwisdom+ instance Rename VarWisdom where   rename = substituteRename @@ -57,11 +69,20 @@   freeIn' (VarWisdom als) = freeIn' als  -- | Wisdom about an expression.-data ExpWisdom = ExpWisdom { _expWisdomConsumed :: ConsumedInExp-                           , expWisdomFree :: AliasDec-                           }-                 deriving (Eq, Ord, Show)+data ExpWisdom = ExpWisdom+  { _expWisdomConsumed :: ConsumedInExp,+    expWisdomFree :: AliasDec+  }+  deriving (Eq, Ord, Show, Generic) +instance SexpIso ExpWisdom where+  sexpIso = with $ \expwisdom ->+    Sexp.list+      ( Sexp.el sexpIso+          >>> Sexp.el sexpIso+      )+      >>> expwisdom+ instance FreeIn ExpWisdom where   freeIn' = mempty @@ -71,28 +92,36 @@ instance Substitute ExpWisdom where   substituteNames substs (ExpWisdom cons free) =     ExpWisdom-    (substituteNames substs cons)-    (substituteNames substs free)+      (substituteNames substs cons)+      (substituteNames substs free)  instance Rename ExpWisdom where   rename = substituteRename  -- | Wisdom about a body.-data BodyWisdom = BodyWisdom { bodyWisdomAliases :: [VarAliases]-                             , bodyWisdomConsumed :: ConsumedInExp-                             , bodyWisdomFree :: AliasDec-                             }-                  deriving (Eq, Ord, Show)+data BodyWisdom = BodyWisdom+  { bodyWisdomAliases :: [VarAliases],+    bodyWisdomConsumed :: ConsumedInExp,+    bodyWisdomFree :: AliasDec+  }+  deriving (Eq, Ord, Show, Generic) +instance SexpIso BodyWisdom where+  sexpIso = with $ \bodywisdom ->+    Sexp.list+      ( Sexp.el sexpIso >>> Sexp.el sexpIso >>> Sexp.el sexpIso+      )+      >>> bodywisdom+ instance Rename BodyWisdom where   rename = substituteRename  instance Substitute BodyWisdom where   substituteNames substs (BodyWisdom als cons free) =     BodyWisdom-    (substituteNames substs als)-    (substituteNames substs cons)-    (substituteNames substs free)+      (substituteNames substs als)+      (substituteNames substs cons)+      (substituteNames substs free)  instance FreeIn BodyWisdom where   freeIn' (BodyWisdom als cons free) =@@ -101,8 +130,12 @@ instance FreeDec BodyWisdom where   precomputed = const . fvNames . unAliases . bodyWisdomFree -instance (Decorations lore,-          CanBeWise (Op lore)) => Decorations (Wise lore) where+instance+  ( Decorations lore,+    CanBeWise (Op lore)+  ) =>+  Decorations (Wise lore)+  where   type LetDec (Wise lore) = (VarWisdom, LetDec lore)   type ExpDec (Wise lore) = (ExpWisdom, ExpDec lore)   type BodyDec (Wise lore) = (BodyWisdom, BodyDec lore)@@ -112,9 +145,10 @@   type BranchType (Wise lore) = BranchType lore   type Op (Wise lore) = OpWithWisdom (Op lore) -withoutWisdom :: (HasScope (Wise lore) m, Monad m) =>-                 ReaderT (Scope lore) m a ->-                 m a+withoutWisdom ::+  (HasScope (Wise lore) m, Monad m) =>+  ReaderT (Scope lore) m a ->+  m a withoutWisdom m = do   scope <- asksScope removeScopeWisdom   runReaderT m scope@@ -137,29 +171,33 @@   consumedInBody = unAliases . bodyWisdomConsumed . fst . bodyDec  removeWisdom :: CanBeWise (Op lore) => Rephraser Identity (Wise lore) lore-removeWisdom = Rephraser { rephraseExpLore = return . snd-                         , rephraseLetBoundLore = return . snd-                         , rephraseBodyLore = return . snd-                         , rephraseFParamLore = return-                         , rephraseLParamLore = return-                         , rephraseRetType = return-                         , rephraseBranchType = return-                         , rephraseOp = return . removeOpWisdom-                         }+removeWisdom =+  Rephraser+    { rephraseExpLore = return . snd,+      rephraseLetBoundLore = return . snd,+      rephraseBodyLore = return . snd,+      rephraseFParamLore = return,+      rephraseLParamLore = return,+      rephraseRetType = return,+      rephraseBranchType = return,+      rephraseOp = return . removeOpWisdom+    }  removeScopeWisdom :: Scope (Wise lore) -> Scope lore removeScopeWisdom = M.map unAlias-  where unAlias (LetName (_, dec)) = LetName dec-        unAlias (FParamName dec) = FParamName dec-        unAlias (LParamName dec) = LParamName dec-        unAlias (IndexName it) = IndexName it+  where+    unAlias (LetName (_, dec)) = LetName dec+    unAlias (FParamName dec) = FParamName dec+    unAlias (LParamName dec) = LParamName dec+    unAlias (IndexName it) = IndexName it  addScopeWisdom :: Scope lore -> Scope (Wise lore) addScopeWisdom = M.map alias-  where alias (LetName dec) = LetName (VarWisdom mempty, dec)-        alias (FParamName dec) = FParamName dec-        alias (LParamName dec) = LParamName dec-        alias (IndexName it) = IndexName it+  where+    alias (LetName dec) = LetName (VarWisdom mempty, dec)+    alias (FParamName dec) = FParamName dec+    alias (LParamName dec) = LParamName dec+    alias (IndexName it) = IndexName it  removeFunDefWisdom :: CanBeWise (Op lore) => FunDef (Wise lore) -> FunDef lore removeFunDefWisdom = runIdentity . rephraseFunDef removeWisdom@@ -179,42 +217,64 @@ removePatternWisdom :: PatternT (VarWisdom, a) -> PatternT a removePatternWisdom = runIdentity . rephrasePattern (return . snd) -addWisdomToPattern :: (ASTLore lore, CanBeWise (Op lore)) =>-                      Pattern lore-                   -> Exp (Wise lore)-                   -> Pattern (Wise lore)+addWisdomToPattern ::+  (ASTLore lore, CanBeWise (Op lore)) =>+  Pattern lore ->+  Exp (Wise lore) ->+  Pattern (Wise lore) addWisdomToPattern pat e =   Pattern (map f ctx) (map f val)-  where (ctx, val) = Aliases.mkPatternAliases pat e-        f pe = let (als, dec) = patElemDec pe-               in pe `setPatElemLore` (VarWisdom als, dec)+  where+    (ctx, val) = Aliases.mkPatternAliases pat e+    f pe =+      let (als, dec) = patElemDec pe+       in pe `setPatElemLore` (VarWisdom als, dec) -mkWiseBody :: (ASTLore lore, CanBeWise (Op lore)) =>-              BodyDec lore -> Stms (Wise lore) -> Result -> Body (Wise lore)+mkWiseBody ::+  (ASTLore lore, CanBeWise (Op lore)) =>+  BodyDec lore ->+  Stms (Wise lore) ->+  Result ->+  Body (Wise lore) mkWiseBody innerlore bnds res =-  Body (BodyWisdom aliases consumed (AliasDec $ freeIn $ freeInStmsAndRes bnds res),-        innerlore) bnds res-  where (aliases, consumed) = Aliases.mkBodyAliases bnds res+  Body+    ( BodyWisdom aliases consumed (AliasDec $ freeIn $ freeInStmsAndRes bnds res),+      innerlore+    )+    bnds+    res+  where+    (aliases, consumed) = Aliases.mkBodyAliases bnds res -mkWiseLetStm :: (ASTLore lore, CanBeWise (Op lore)) =>-                Pattern lore-             -> StmAux (ExpDec lore) -> Exp (Wise lore)-             -> Stm (Wise lore)+mkWiseLetStm ::+  (ASTLore lore, CanBeWise (Op lore)) =>+  Pattern lore ->+  StmAux (ExpDec lore) ->+  Exp (Wise lore) ->+  Stm (Wise lore) mkWiseLetStm pat (StmAux cs attrs dec) e =   let pat' = addWisdomToPattern pat e-  in Let pat' (StmAux cs attrs $ mkWiseExpDec pat' dec e) e+   in Let pat' (StmAux cs attrs $ mkWiseExpDec pat' dec e) e -mkWiseExpDec :: (ASTLore lore, CanBeWise (Op lore)) =>-                 Pattern (Wise lore) -> ExpDec lore -> Exp (Wise lore)-              -> ExpDec (Wise lore)+mkWiseExpDec ::+  (ASTLore lore, CanBeWise (Op lore)) =>+  Pattern (Wise lore) ->+  ExpDec lore ->+  Exp (Wise lore) ->+  ExpDec (Wise lore) mkWiseExpDec pat explore e =-  (ExpWisdom-    (AliasDec $ consumedInExp e)-    (AliasDec $ freeIn pat <> freeIn explore <> freeIn e),-   explore)+  ( ExpWisdom+      (AliasDec $ consumedInExp e)+      (AliasDec $ freeIn pat <> freeIn explore <> freeIn e),+    explore+  ) -instance (Bindable lore,-          CanBeWise (Op lore)) => Bindable (Wise lore) where+instance+  ( Bindable lore,+    CanBeWise (Op lore)+  ) =>+  Bindable (Wise lore)+  where   mkExpPat ctx val e =     addWisdomToPattern (mkExpPat ctx val $ removeExpWisdom e) e @@ -229,10 +289,14 @@    mkBody bnds res =     let Body bodylore _ _ = mkBody (fmap removeStmWisdom bnds) res-    in mkWiseBody bodylore bnds res+     in mkWiseBody bodylore bnds res -class (AliasedOp (OpWithWisdom op),-       IsOp (OpWithWisdom op)) => CanBeWise op where+class+  ( AliasedOp (OpWithWisdom op),+    IsOp (OpWithWisdom op)+  ) =>+  CanBeWise op+  where   type OpWithWisdom op :: Data.Kind.Type   removeOpWisdom :: OpWithWisdom op -> op 
src/Futhark/Optimise/Simplify/Rule.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+ -- | This module defines the concept of a simplification rule for -- bindings.  The intent is that you pass some context (such as symbol -- table) and a binding, and is given back a sequence of bindings that@@ -13,57 +14,63 @@ -- fusion algorithm in @Futhark.Optimise.Fusion.Fusion@, which must be implemented -- as its own pass. module Futhark.Optimise.Simplify.Rule-       ( -- * The rule monad-         RuleM-       , cannotSimplify-       , liftMaybe+  ( -- * The rule monad+    RuleM,+    cannotSimplify,+    liftMaybe, -       -- * Rule definition-       , Rule(..)-       , SimplificationRule(..)-       , RuleGeneric-       , RuleBasicOp-       , RuleIf-       , RuleDoLoop+    -- * Rule definition+    Rule (..),+    SimplificationRule (..),+    RuleGeneric,+    RuleBasicOp,+    RuleIf,+    RuleDoLoop, -       -- * Top-down rules-       , TopDown-       , TopDownRule-       , TopDownRuleGeneric-       , TopDownRuleBasicOp-       , TopDownRuleIf-       , TopDownRuleDoLoop-       , TopDownRuleOp+    -- * Top-down rules+    TopDown,+    TopDownRule,+    TopDownRuleGeneric,+    TopDownRuleBasicOp,+    TopDownRuleIf,+    TopDownRuleDoLoop,+    TopDownRuleOp, -       -- * Bottom-up rules-       , BottomUp-       , BottomUpRule-       , BottomUpRuleGeneric-       , BottomUpRuleBasicOp-       , BottomUpRuleIf-       , BottomUpRuleDoLoop-       , BottomUpRuleOp+    -- * Bottom-up rules+    BottomUp,+    BottomUpRule,+    BottomUpRuleGeneric,+    BottomUpRuleBasicOp,+    BottomUpRuleIf,+    BottomUpRuleDoLoop,+    BottomUpRuleOp, -       -- * Assembling rules-       , RuleBook-       , ruleBook+    -- * Assembling rules+    RuleBook,+    ruleBook, -         -- * Applying rules-       , topDownSimplifyStm-       , bottomUpSimplifyStm-       ) where+    -- * Applying rules+    topDownSimplifyStm,+    bottomUpSimplifyStm,+  )+where  import Control.Monad.State- import qualified Futhark.Analysis.SymbolTable as ST import qualified Futhark.Analysis.UsageTable as UT-import Futhark.IR import Futhark.Binder+import Futhark.IR  -- | The monad in which simplification rules are evaluated. newtype RuleM lore a = RuleM (BinderT lore (StateT VNameSource Maybe) a)-  deriving (Functor, Applicative, Monad,-            MonadFreshNames, HasScope lore, LocalScope lore)+  deriving+    ( Functor,+      Applicative,+      Monad,+      MonadFreshNames,+      HasScope lore,+      LocalScope lore+    )  instance (ASTLore lore, BinderOps lore) => MonadBinder (RuleM lore) where   type Lore (RuleM lore) = lore@@ -76,8 +83,11 @@  -- | Execute a 'RuleM' action.  If succesful, returns the result and a -- list of new bindings.-simplify :: Scope lore -> VNameSource -> Rule lore-         -> Maybe (Stms lore, VNameSource)+simplify ::+  Scope lore ->+  VNameSource ->+  Rule lore ->+  Maybe (Stms lore, VNameSource) simplify _ _ Skip = Nothing simplify scope src (Simplify (RuleM m)) =   runStateT (runBinderT_ m scope) src@@ -90,43 +100,73 @@ liftMaybe (Just x) = return x  -- | An efficient way of encoding whether a simplification rule should even be attempted.-data Rule lore = Simplify (RuleM lore ()) -- ^ Give it a shot.-               | Skip -- ^ Don't bother.+data Rule lore+  = -- | Give it a shot.+    Simplify (RuleM lore ())+  | -- | Don't bother.+    Skip  type RuleGeneric lore a = a -> Stm lore -> Rule lore-type RuleBasicOp lore a = (a -> Pattern lore -> StmAux (ExpDec lore) ->-                           BasicOp -> Rule lore)-type RuleIf lore a = a -> Pattern lore -> StmAux (ExpDec lore) ->-                     (SubExp, BodyT lore, BodyT lore,-                      IfDec (BranchType lore)) ->-                     Rule lore-type RuleDoLoop lore a = a -> Pattern lore -> StmAux (ExpDec lore) ->-                         ([(FParam lore, SubExp)], [(FParam lore, SubExp)],-                          LoopForm lore, BodyT lore) ->-                         Rule lore-type RuleOp lore a = a -> Pattern lore -> StmAux (ExpDec lore) ->-                     Op lore -> Rule lore +type RuleBasicOp lore a =+  ( a ->+    Pattern lore ->+    StmAux (ExpDec lore) ->+    BasicOp ->+    Rule lore+  )++type RuleIf lore a =+  a ->+  Pattern lore ->+  StmAux (ExpDec lore) ->+  ( SubExp,+    BodyT lore,+    BodyT lore,+    IfDec (BranchType lore)+  ) ->+  Rule lore++type RuleDoLoop lore a =+  a ->+  Pattern lore ->+  StmAux (ExpDec lore) ->+  ( [(FParam lore, SubExp)],+    [(FParam lore, SubExp)],+    LoopForm lore,+    BodyT lore+  ) ->+  Rule lore++type RuleOp lore a =+  a ->+  Pattern lore ->+  StmAux (ExpDec lore) ->+  Op lore ->+  Rule lore+ -- | A simplification rule takes some argument and a statement, and -- tries to simplify the statement.-data SimplificationRule lore a = RuleGeneric (RuleGeneric lore a)-                               | RuleBasicOp (RuleBasicOp lore a)-                               | RuleIf (RuleIf lore a)-                               | RuleDoLoop (RuleDoLoop lore a)-                               | RuleOp (RuleOp lore a)+data SimplificationRule lore a+  = RuleGeneric (RuleGeneric lore a)+  | RuleBasicOp (RuleBasicOp lore a)+  | RuleIf (RuleIf lore a)+  | RuleDoLoop (RuleDoLoop lore a)+  | RuleOp (RuleOp lore a)  -- | A collection of rules grouped by which forms of statements they -- may apply to.-data Rules lore a = Rules { rulesAny :: [SimplificationRule lore a]-                          , rulesBasicOp :: [SimplificationRule lore a]-                          , rulesIf :: [SimplificationRule lore a]-                          , rulesDoLoop :: [SimplificationRule lore a]-                          , rulesOp :: [SimplificationRule lore a]-                          }+data Rules lore a = Rules+  { rulesAny :: [SimplificationRule lore a],+    rulesBasicOp :: [SimplificationRule lore a],+    rulesIf :: [SimplificationRule lore a],+    rulesDoLoop :: [SimplificationRule lore a],+    rulesOp :: [SimplificationRule lore a]+  }  instance Semigroup (Rules lore a) where   Rules as1 bs1 cs1 ds1 es1 <> Rules as2 bs2 cs2 ds2 es2 =-    Rules (as1<>as2) (bs1<>bs2) (cs1<>cs2) (ds1<>ds2) (es1<>es2)+    Rules (as1 <> as2) (bs1 <> bs2) (cs1 <> cs2) (ds1 <> ds2) (es1 <> es2)  instance Monoid (Rules lore a) where   mempty = Rules mempty mempty mempty mempty mempty@@ -136,10 +176,15 @@ type TopDown lore = ST.SymbolTable lore  type TopDownRuleGeneric lore = RuleGeneric lore (TopDown lore)+ type TopDownRuleBasicOp lore = RuleBasicOp lore (TopDown lore)+ type TopDownRuleIf lore = RuleIf lore (TopDown lore)+ type TopDownRuleDoLoop lore = RuleDoLoop lore (TopDown lore)+ type TopDownRuleOp lore = RuleOp lore (TopDown lore)+ type TopDownRule lore = SimplificationRule lore (TopDown lore)  -- | Context for a rule applied during bottom-up traversal of the@@ -147,10 +192,15 @@ type BottomUp lore = (ST.SymbolTable lore, UT.UsageTable)  type BottomUpRuleGeneric lore = RuleGeneric lore (BottomUp lore)+ type BottomUpRuleBasicOp lore = RuleBasicOp lore (BottomUp lore)+ type BottomUpRuleIf lore = RuleIf lore (BottomUp lore)+ type BottomUpRuleDoLoop lore = RuleDoLoop lore (BottomUp lore)+ type BottomUpRuleOp lore = RuleOp lore (BottomUp lore)+ type BottomUpRule lore = SimplificationRule lore (BottomUp lore)  -- | A collection of top-down rules.@@ -160,54 +210,60 @@ type BottomUpRules lore = Rules lore (BottomUp lore)  -- | A collection of both top-down and bottom-up rules.-data RuleBook lore = RuleBook { bookTopDownRules :: TopDownRules lore-                              , bookBottomUpRules :: BottomUpRules lore-                              }+data RuleBook lore = RuleBook+  { bookTopDownRules :: TopDownRules lore,+    bookBottomUpRules :: BottomUpRules lore+  }  instance Semigroup (RuleBook lore) where-  RuleBook ts1 bs1 <> RuleBook ts2 bs2 = RuleBook (ts1<>ts2) (bs1<>bs2)+  RuleBook ts1 bs1 <> RuleBook ts2 bs2 = RuleBook (ts1 <> ts2) (bs1 <> bs2)  instance Monoid (RuleBook lore) where   mempty = RuleBook mempty mempty  -- | Construct a rule book from a collection of rules.-ruleBook :: [TopDownRule m]-         -> [BottomUpRule m]-         -> RuleBook m+ruleBook ::+  [TopDownRule m] ->+  [BottomUpRule m] ->+  RuleBook m ruleBook topdowns bottomups =   RuleBook (groupRules topdowns) (groupRules bottomups)-  where groupRules :: [SimplificationRule m a] -> Rules m a-        groupRules rs = Rules rs-                              (filter forBasicOp rs)-                              (filter forIf rs)-                              (filter forDoLoop rs)-                              (filter forOp rs)+  where+    groupRules :: [SimplificationRule m a] -> Rules m a+    groupRules rs =+      Rules+        rs+        (filter forBasicOp rs)+        (filter forIf rs)+        (filter forDoLoop rs)+        (filter forOp rs) -        forBasicOp RuleBasicOp{} = True-        forBasicOp RuleGeneric{} = True-        forBasicOp _ = False+    forBasicOp RuleBasicOp {} = True+    forBasicOp RuleGeneric {} = True+    forBasicOp _ = False -        forIf RuleIf{} = True-        forIf RuleGeneric{} = True-        forIf _ = False+    forIf RuleIf {} = True+    forIf RuleGeneric {} = True+    forIf _ = False -        forDoLoop RuleDoLoop{} = True-        forDoLoop RuleGeneric{} = True-        forDoLoop _ = False+    forDoLoop RuleDoLoop {} = True+    forDoLoop RuleGeneric {} = True+    forDoLoop _ = False -        forOp RuleOp{} = True-        forOp RuleGeneric{} = True-        forOp _ = False+    forOp RuleOp {} = True+    forOp RuleGeneric {} = True+    forOp _ = False  -- | @simplifyStm lookup bnd@ performs simplification of the -- binding @bnd@.  If simplification is possible, a replacement list -- of bindings is returned, that bind at least the same names as the -- original binding (and possibly more, for intermediate results).-topDownSimplifyStm :: (MonadFreshNames m, HasScope lore m) =>-                      RuleBook lore-                   -> ST.SymbolTable lore-                   -> Stm lore-                   -> m (Maybe (Stms lore))+topDownSimplifyStm ::+  (MonadFreshNames m, HasScope lore m) =>+  RuleBook lore ->+  ST.SymbolTable lore ->+  Stm lore ->+  m (Maybe (Stms lore)) topDownSimplifyStm = applyRules . bookTopDownRules  -- | @simplifyStm uses bnd@ performs simplification of the binding@@ -215,19 +271,21 @@ -- bindings is returned, that bind at least the same names as the -- original binding (and possibly more, for intermediate results). -- The first argument is the set of names used after this binding.-bottomUpSimplifyStm :: (MonadFreshNames m, HasScope lore m) =>-                       RuleBook lore-                    -> (ST.SymbolTable lore, UT.UsageTable)-                    -> Stm lore-                    -> m (Maybe (Stms lore))+bottomUpSimplifyStm ::+  (MonadFreshNames m, HasScope lore m) =>+  RuleBook lore ->+  (ST.SymbolTable lore, UT.UsageTable) ->+  Stm lore ->+  m (Maybe (Stms lore)) bottomUpSimplifyStm = applyRules . bookBottomUpRules  rulesForStm :: Stm lore -> Rules lore a -> [SimplificationRule lore a]-rulesForStm stm = case stmExp stm of BasicOp{} -> rulesBasicOp-                                     DoLoop{} -> rulesDoLoop-                                     Op{} -> rulesOp-                                     If{} -> rulesIf-                                     _ -> rulesAny+rulesForStm stm = case stmExp stm of+  BasicOp {} -> rulesBasicOp+  DoLoop {} -> rulesDoLoop+  Op {} -> rulesOp+  If {} -> rulesIf+  _ -> rulesAny  applyRule :: SimplificationRule lore a -> a -> Stm lore -> Rule lore applyRule (RuleGeneric f) a stm = f a stm@@ -241,19 +299,21 @@ applyRule _ _ _ =   Skip -applyRules :: (MonadFreshNames m, HasScope lore m) =>-              Rules lore a -> a -> Stm lore-           -> m (Maybe (Stms lore))+applyRules ::+  (MonadFreshNames m, HasScope lore m) =>+  Rules lore a ->+  a ->+  Stm lore ->+  m (Maybe (Stms lore)) applyRules all_rules context stm = do   scope <- askScope    modifyNameSource $ \src ->-    let applyRules' []  = Nothing-        applyRules' (rule:rules) =+    let applyRules' [] = Nothing+        applyRules' (rule : rules) =           case simplify scope src (applyRule rule context stm) of             Just x -> Just x             Nothing -> applyRules' rules--    in case applyRules' $ rulesForStm stm all_rules of-         Just (stms, src') -> (Just stms, src')-         Nothing           -> (Nothing, src)+     in case applyRules' $ rulesForStm stm all_rules of+          Just (stms, src') -> (Just stms, src')+          Nothing -> (Nothing, src)
src/Futhark/Optimise/Simplify/Rules.hs view
@@ -1,1326 +1,1486 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE Safe #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE OverloadedStrings #-}--- | This module defines a collection of simplification rules, as per--- "Futhark.Optimise.Simplify.Rule".  They are used in the--- simplifier.------ For performance reasons, many sufficiently simple logically--- separate rules are merged into single "super-rules", like ruleIf--- and ruleBasicOp.  This is because it is relatively expensive to--- activate a rule just to determine that it does not apply.  Thus, it--- is more efficient to have a few very fat rules than a lot of small--- rules.  This does not affect the compiler result in any way; it is--- purely an optimisation to speed up compilation.-module Futhark.Optimise.Simplify.Rules-  ( standardRules-  , removeUnnecessaryCopy-  )-where--import Control.Monad-import Data.Either-import Data.List (find, isSuffixOf, partition, sort)-import Data.Maybe-import qualified Data.Map.Strict as M--import qualified Futhark.Analysis.SymbolTable as ST-import qualified Futhark.Analysis.UsageTable as UT-import Futhark.Analysis.DataDependencies-import Futhark.Optimise.Simplify.ClosedForm-import Futhark.Optimise.Simplify.Rule-import Futhark.Analysis.PrimExp.Convert-import Futhark.IR-import Futhark.IR.Prop.Aliases-import Futhark.Transform.Rename-import Futhark.Construct-import Futhark.Util--topDownRules :: (BinderOps lore, Aliased lore) => [TopDownRule lore]-topDownRules = [ RuleDoLoop hoistLoopInvariantMergeVariables-               , RuleDoLoop simplifyClosedFormLoop-               , RuleDoLoop simplifyKnownIterationLoop-               , RuleDoLoop simplifyLoopVariables-               , RuleGeneric constantFoldPrimFun-               , RuleIf ruleIf-               , RuleIf hoistBranchInvariant-               , RuleBasicOp ruleBasicOp-               ]--bottomUpRules :: BinderOps lore => [BottomUpRule lore]-bottomUpRules = [ RuleDoLoop removeRedundantMergeVariables-                , RuleIf removeDeadBranchResult-                , RuleBasicOp simplifyIndex-                , RuleBasicOp simplifyConcat-                ]--asInt32PrimExp :: PrimExp v -> PrimExp v-asInt32PrimExp pe-  | IntType it <- primExpType pe, it /= Int32 =-      sExt Int32 pe-  | otherwise =-      pe---- | A set of standard simplification rules.  These assume pure--- functional semantics, and so probably should not be applied after--- memory block merging.-standardRules :: (BinderOps lore, Aliased lore) => RuleBook lore-standardRules = ruleBook topDownRules bottomUpRules---- This next one is tricky - it's easy enough to determine that some--- loop result is not used after the loop, but here, we must also make--- sure that it does not affect any other values.------ I do not claim that the current implementation of this rule is--- perfect, but it should suffice for many cases, and should never--- generate wrong code.-removeRedundantMergeVariables :: BinderOps lore => BottomUpRuleDoLoop lore-removeRedundantMergeVariables (_, used) pat aux (ctx, val, form, body)-  | not $ all (usedAfterLoop . fst) val,-    null ctx = -- FIXME: things get tricky if we can remove all vals-               -- but some ctxs are still used.  We take the easy way-               -- out for now.-  let (ctx_es, val_es) = splitAt (length ctx) $ bodyResult body-      necessaryForReturned =-        findNecessaryForReturned usedAfterLoopOrInForm-        (zip (map fst $ ctx++val) $ ctx_es++val_es) (dataDependencies body)--      resIsNecessary ((v,_), _) =-        usedAfterLoop v ||-        paramName v `nameIn` necessaryForReturned ||-        referencedInPat v ||-        referencedInForm v--      (keep_ctx, discard_ctx) =-        partition resIsNecessary $ zip ctx ctx_es-      (keep_valpart, discard_valpart) =-        partition (resIsNecessary . snd) $-        zip (patternValueElements pat) $ zip val val_es--      (keep_valpatelems, keep_val) = unzip keep_valpart-      (_discard_valpatelems, discard_val) = unzip discard_valpart-      (ctx', ctx_es') = unzip keep_ctx-      (val', val_es') = unzip keep_val--      body' = body { bodyResult = ctx_es' ++ val_es' }-      free_in_keeps = freeIn keep_valpatelems--      stillUsedContext pat_elem =-        patElemName pat_elem `nameIn`-        (free_in_keeps <>-         freeIn (filter (/=pat_elem) $ patternContextElements pat))--      pat' = pat { patternValueElements = keep_valpatelems-                 , patternContextElements =-                     filter stillUsedContext $ patternContextElements pat }-  in if ctx' ++ val' == ctx ++ val-     then Skip-     else Simplify $ do-       -- We can't just remove the bindings in 'discard', since the loop-       -- body may still use their names in (now-dead) expressions.-       -- Hence, we add them inside the loop, fully aware that dead-code-       -- removal will eventually get rid of them.  Some care is-       -- necessary to handle unique bindings.-       body'' <- insertStmsM $ do-         mapM_ (uncurry letBindNames) $ dummyStms discard_ctx-         mapM_ (uncurry letBindNames) $ dummyStms discard_val-         return body'-       auxing aux $ letBind pat' $ DoLoop ctx' val' form body''-  where pat_used = map (`UT.isUsedDirectly` used) $ patternValueNames pat-        used_vals = map fst $ filter snd $ zip (map (paramName . fst) val) pat_used-        usedAfterLoop = flip elem used_vals . paramName-        usedAfterLoopOrInForm p =-          usedAfterLoop p || paramName p `nameIn` freeIn form-        patAnnotNames = freeIn $ map fst $ ctx++val-        referencedInPat = (`nameIn` patAnnotNames) . paramName-        referencedInForm = (`nameIn` freeIn form) . paramName--        dummyStms = map dummyStm-        dummyStm ((p,e), _)-          | unique (paramDeclType p),-            Var v <- e            = ([paramName p], BasicOp $ Copy v)-          | otherwise             = ([paramName p], BasicOp $ SubExp e)-removeRedundantMergeVariables _ _ _ _ =-  Skip---- We may change the type of the loop if we hoist out a shape--- annotation, in which case we also need to tweak the bound pattern.-hoistLoopInvariantMergeVariables :: BinderOps lore => TopDownRuleDoLoop lore-hoistLoopInvariantMergeVariables _ pat aux (ctx, val, form, loopbody) =-    -- Figure out which of the elements of loopresult are-    -- loop-invariant, and hoist them out.-  case foldr checkInvariance ([], explpat, [], []) $-       zip merge res of-    ([], _, _, _) ->-      -- Nothing is invariant.-      Skip-    (invariant, explpat', merge', res') -> Simplify $ do-      -- We have moved something invariant out of the loop.-      let loopbody' = loopbody { bodyResult = res' }-          invariantShape :: (a, VName) -> Bool-          invariantShape (_, shapemerge) = shapemerge `elem`-                                           map (paramName . fst) merge'-          (implpat',implinvariant) = partition invariantShape implpat-          implinvariant' = [ (patElemIdent p, Var v) | (p,v) <- implinvariant ]-          implpat'' = map fst implpat'-          explpat'' = map fst explpat'-          (ctx', val') = splitAt (length implpat') merge'-      forM_ (invariant ++ implinvariant') $ \(v1,v2) ->-        letBindNames [identName v1] $ BasicOp $ SubExp v2-      auxing aux $ letBind (Pattern implpat'' explpat'') $-        DoLoop ctx' val' form loopbody'-  where merge = ctx ++ val-        res = bodyResult loopbody--        implpat = zip (patternContextElements pat) $-                  map (paramName . fst) ctx-        explpat = zip (patternValueElements pat) $-                  map (paramName . fst) val--        namesOfMergeParams = namesFromList $ map (paramName . fst) $ ctx++val--        removeFromResult (mergeParam,mergeInit) explpat' =-          case partition ((==paramName mergeParam) . snd) explpat' of-            ([(patelem,_)], rest) ->-              (Just (patElemIdent patelem, mergeInit), rest)-            (_,      _) ->-              (Nothing, explpat')--        checkInvariance-          ((mergeParam,mergeInit), resExp)-          (invariant, explpat', merge', resExps)-          | not (unique (paramDeclType mergeParam)) ||-            arrayRank (paramDeclType mergeParam) == 1,-            isInvariant resExp,-            -- Also do not remove the condition in a while-loop.-            not $ paramName mergeParam `nameIn` freeIn form =-          let (bnd, explpat'') =-                removeFromResult (mergeParam,mergeInit) explpat'-          in (maybe id (:) bnd $ (paramIdent mergeParam, mergeInit) : invariant,-              explpat'', merge', resExps)-          where-            -- A non-unique merge variable is invariant if the corresponding-            -- subexp in the result is EITHER:-            ---            --  (0) a variable of the same name as the parameter, where-            --  all existential parameters are already known to be-            --  invariant-            isInvariant (Var v2)-              | paramName mergeParam == v2 =-                allExistentialInvariant-                (namesFromList $ map (identName . fst) invariant) mergeParam-            --  (1) or identical to the initial value of the parameter.-            isInvariant _ = mergeInit == resExp--        checkInvariance ((mergeParam,mergeInit), resExp) (invariant, explpat', merge', resExps) =-          (invariant, explpat', (mergeParam,mergeInit):merge', resExp:resExps)--        allExistentialInvariant namesOfInvariant mergeParam =-          all (invariantOrNotMergeParam namesOfInvariant) $ namesToList $-          freeIn mergeParam `namesSubtract` oneName (paramName mergeParam)-        invariantOrNotMergeParam namesOfInvariant name =-          not (name `nameIn` namesOfMergeParams) ||-          name `nameIn` namesOfInvariant---- | A function that, given a subexpression, returns its type.-type TypeLookup = SubExp -> Maybe Type---- | A simple rule is a top-down rule that can be expressed as a pure--- function.-type SimpleRule lore = VarLookup lore -> TypeLookup -> BasicOp -> Maybe (BasicOp, Certificates)--simpleRules :: [SimpleRule lore]-simpleRules = [ simplifyBinOp-              , simplifyCmpOp-              , simplifyUnOp-              , simplifyConvOp-              , simplifyAssert-              , copyScratchToScratch-              , simplifyIdentityReshape-              , simplifyReshapeReshape-              , simplifyReshapeScratch-              , simplifyReshapeReplicate-              , simplifyReshapeIota-              , improveReshape ]--simplifyClosedFormLoop :: BinderOps lore => TopDownRuleDoLoop lore-simplifyClosedFormLoop _ pat _ ([], val, ForLoop i _ bound [], body) =-  Simplify $ loopClosedForm pat val (oneName i) bound body-simplifyClosedFormLoop _ _ _ _ = Skip--simplifyLoopVariables :: (BinderOps lore, Aliased lore) => TopDownRuleDoLoop lore-simplifyLoopVariables vtable pat aux (ctx, val, form@(ForLoop i it num_iters loop_vars), body)-  | simplifiable <- map checkIfSimplifiable loop_vars,-    not $ all isNothing simplifiable = Simplify $ do-      -- Check if the simplifications throw away more information than-      -- we are comfortable with at this stage.-      (maybe_loop_vars, body_prefix_stms) <--        localScope (scopeOf form) $-        unzip <$> zipWithM onLoopVar loop_vars simplifiable-      if maybe_loop_vars == map Just loop_vars-        then cannotSimplify-        else do body' <- insertStmsM $ do-                  addStms $ mconcat body_prefix_stms-                  resultBodyM =<< bodyBind body-                auxing aux $ letBind pat $ DoLoop ctx val-                  (ForLoop i it num_iters $ catMaybes maybe_loop_vars) body'--  where seType (Var v)-          | v == i = Just $ Prim $ IntType it-          | otherwise = ST.lookupType v vtable-        seType (Constant v) = Just $ Prim $ primValueType v-        consumed_in_body = consumedInBody body--        vtable' = ST.fromScope (scopeOf form) <> vtable--        checkIfSimplifiable (p,arr) =-          simplifyIndexing vtable' seType arr-          (DimFix (Var i) : fullSlice (paramType p) []) $-          paramName p `nameIn` consumed_in_body--        -- We only want this simplification if the result does not refer-        -- to 'i' at all, or does not contain accesses.-        onLoopVar (p,arr) Nothing =-          return (Just (p,arr), mempty)-        onLoopVar (p,arr) (Just m) = do-          (x,x_stms) <- collectStms m-          case x of-            IndexResult cs arr' slice-              | not $ any ((i `nameIn`) . freeIn) x_stms,-                DimFix (Var j) : slice' <- slice,-                j == i, not $ i `nameIn` freeIn slice -> do-                  addStms x_stms-                  w <- arraySize 0 <$> lookupType arr'-                  for_in_partial <--                    certifying cs $ letExp "for_in_partial" $ BasicOp $ Index arr' $-                    DimSlice (intConst Int32 0) w (intConst Int32 1) : slice'-                  return (Just (p, for_in_partial), mempty)--            SubExpResult cs se-              | all (notIndex . stmExp) x_stms -> do-                  x_stms' <- collectStms_ $ certifying cs $ do-                    addStms x_stms-                    letBindNames [paramName p] $ BasicOp $ SubExp se-                  return (Nothing, x_stms')--            _ -> return (Just (p,arr), mempty)--        notIndex (BasicOp Index{}) = False-        notIndex _                 = True-simplifyLoopVariables _ _ _ _ = Skip--unroll :: BinderOps lore =>-          Integer-       -> [(FParam lore, SubExp)]-       -> (VName, IntType, Integer)-       -> [(LParam lore, VName)]-       -> Body lore-       -> RuleM lore [SubExp]-unroll n merge (iv, it, i) loop_vars body-  | i >= n =-      return $ map snd merge-  | otherwise = do-      iter_body <- insertStmsM $ do-        forM_ merge $ \(mergevar, mergeinit) ->-          letBindNames [paramName mergevar] $ BasicOp $ SubExp mergeinit--        letBindNames [iv] $ BasicOp $ SubExp $ intConst it i--        forM_ loop_vars $ \(p,arr) ->-          letBindNames [paramName p] $ BasicOp $ Index arr $-          DimFix (intConst Int32 i) : fullSlice (paramType p) []--        -- Some of the sizes in the types here might be temporarily wrong-        -- until copy propagation fixes it up.-        pure body--      iter_body' <- renameBody iter_body-      addStms $ bodyStms iter_body'--      let merge' = zip (map fst merge) $ bodyResult iter_body'-      unroll n merge' (iv, it, i+1) loop_vars body--simplifyKnownIterationLoop :: BinderOps lore => TopDownRuleDoLoop lore-simplifyKnownIterationLoop _ pat aux (ctx, val, ForLoop i it (Constant iters) loop_vars, body)-  | IntValue n <- iters,-    zeroIshInt n || oneIshInt n || "unroll" `inAttrs` stmAuxAttrs aux = Simplify $ do-      res <- unroll (valueIntegral n) (ctx++val) (i, it, 0) loop_vars body-      forM_ (zip (patternNames pat) res) $ \(v, se) ->-        letBindNames [v] $ BasicOp $ SubExp se--simplifyKnownIterationLoop _ _ _ _ =-  Skip---- | Turn @copy(x)@ into @x@ iff @x@ is not used after this copy--- statement and it can be consumed.------ This simplistic rule is only valid before we introduce memory.-removeUnnecessaryCopy :: BinderOps lore => BottomUpRuleBasicOp lore-removeUnnecessaryCopy (vtable,used) (Pattern [] [d]) _ (Copy v)-  | not (v `UT.isConsumed` used),-    (not (v `UT.used` used) && consumable) || not (patElemName d `UT.isConsumed` used) =-      Simplify $ letBindNames [patElemName d] $ BasicOp $ SubExp $ Var v-  where -- We need to make sure we can even consume the original.-        -- This is currently a hacky check, much too conservative,-        -- because we don't have the information conveniently-        -- available.-        consumable = case M.lookup v $ ST.toScope vtable of-                       Just (FParamName info) -> unique $ declTypeOf info-                       _ -> False-removeUnnecessaryCopy _ _ _ _ = Skip--simplifyCmpOp :: SimpleRule lore-simplifyCmpOp _ _ (CmpOp cmp e1 e2)-  | e1 == e2 = constRes $ BoolValue $-               case cmp of CmpEq{}  -> True-                           CmpSlt{} -> False-                           CmpUlt{} -> False-                           CmpSle{} -> True-                           CmpUle{} -> True-                           FCmpLt{} -> False-                           FCmpLe{} -> True-                           CmpLlt -> False-                           CmpLle -> True--simplifyCmpOp _ _ (CmpOp cmp (Constant v1) (Constant v2)) =-  constRes . BoolValue =<< doCmpOp cmp v1 v2--simplifyCmpOp look _ (CmpOp CmpEq{} (Constant (IntValue x)) (Var v))-  | Just (BasicOp (ConvOp BToI{} b), cs) <- look v =-      case valueIntegral x :: Int of-        1 -> Just (SubExp b, cs)-        0 -> Just (UnOp Not b, cs)-        _ -> Just (SubExp (Constant (BoolValue False)), cs)--simplifyCmpOp _ _ _ = Nothing--simplifyBinOp :: SimpleRule lore--simplifyBinOp _ _ (BinOp op (Constant v1) (Constant v2))-  | Just res <- doBinOp op v1 v2 =-      constRes res--simplifyBinOp look _ (BinOp Add{} e1 e2)-  | isCt0 e1 = subExpRes e2-  | isCt0 e2 = subExpRes e1--  -- x+(y-x) => y-  | Var v2 <- e2,-    Just (BasicOp (BinOp Sub{} e2_a e2_b), cs) <- look v2,-    e2_b == e1 = Just (SubExp e2_a, cs)--simplifyBinOp _ _ (BinOp FAdd{} e1 e2)-  | isCt0 e1 = subExpRes e2-  | isCt0 e2 = subExpRes e1---simplifyBinOp look _ (BinOp Sub{} e1 e2)-  | isCt0 e2 = subExpRes e1-  -- Cases for simplifying (a+b)-b and permutations.-  | Var v1 <- e1,-    Just (BasicOp (BinOp Add{} e1_a e1_b), cs) <- look v1,-    e1_a == e2 = Just (SubExp e1_b, cs)-  | Var v1 <- e1,-    Just (BasicOp (BinOp Add{} e1_a e1_b), cs) <- look v1,-    e1_b == e2 = Just (SubExp e1_a, cs)-  | Var v2 <- e2,-    Just (BasicOp (BinOp Add{} e2_a e2_b), cs) <- look v2,-    e2_a == e1 = Just (SubExp e2_b, cs)-  | Var v2 <- e1,-    Just (BasicOp (BinOp Add{} e2_a e2_b), cs) <- look v2,-    e2_b == e1 = Just (SubExp e2_a, cs)--simplifyBinOp _ _ (BinOp FSub{} e1 e2)-  | isCt0 e2 = subExpRes e1--simplifyBinOp _ _ (BinOp Mul{} e1 e2)-  | isCt0 e1 = subExpRes e1-  | isCt0 e2 = subExpRes e2-  | isCt1 e1 = subExpRes e2-  | isCt1 e2 = subExpRes e1--simplifyBinOp _ _ (BinOp FMul{} e1 e2)-  | isCt0 e1 = subExpRes e1-  | isCt0 e2 = subExpRes e2-  | isCt1 e1 = subExpRes e2-  | isCt1 e2 = subExpRes e1--simplifyBinOp look _ (BinOp (SMod t _) e1 e2)-  | isCt1 e2 = constRes $ IntValue $ intValue t (0 :: Int)-  | e1 == e2 = constRes $ IntValue $ intValue t (0 :: Int)-  | Var v1 <- e1,-    Just (BasicOp (BinOp SMod{} _ e4), v1_cs) <- look v1,-    e4 == e2 = Just (SubExp e1, v1_cs)--simplifyBinOp _ _ (BinOp SDiv{} e1 e2)-  | isCt0 e1 = subExpRes e1-  | isCt1 e2 = subExpRes e1-  | isCt0 e2 = Nothing--simplifyBinOp _ _ (BinOp SDivUp{} e1 e2)-  | isCt0 e1 = subExpRes e1-  | isCt1 e2 = subExpRes e1-  | isCt0 e2 = Nothing--simplifyBinOp _ _ (BinOp FDiv{} e1 e2)-  | isCt0 e1 = subExpRes e1-  | isCt1 e2 = subExpRes e1-  | isCt0 e2 = Nothing--simplifyBinOp _ _ (BinOp (SRem t _) e1 e2)-  | isCt1 e2 = constRes $ IntValue $ intValue t (0 :: Int)-  | e1 == e2 = constRes $ IntValue $ intValue t (1 :: Int)--simplifyBinOp _ _ (BinOp SQuot{} e1 e2)-  | isCt1 e2 = subExpRes e1-  | isCt0 e2 = Nothing--simplifyBinOp _ _ (BinOp (FPow t) e1 e2)-  | isCt0 e2 = subExpRes $ floatConst t 1-  | isCt0 e1 || isCt1 e1 || isCt1 e2 = subExpRes e1--simplifyBinOp _ _ (BinOp (Shl t) e1 e2)-  | isCt0 e2 = subExpRes e1-  | isCt0 e1 = subExpRes $ intConst t 0--simplifyBinOp _ _ (BinOp AShr{} e1 e2)-  | isCt0 e2 = subExpRes e1--simplifyBinOp _ _ (BinOp (And t) e1 e2)-  | isCt0 e1 = subExpRes $ intConst t 0-  | isCt0 e2 = subExpRes $ intConst t 0-  | e1 == e2 = subExpRes e1--simplifyBinOp _ _ (BinOp Or{} e1 e2)-  | isCt0 e1 = subExpRes e2-  | isCt0 e2 = subExpRes e1-  | e1 == e2 = subExpRes e1--simplifyBinOp _ _ (BinOp (Xor t) e1 e2)-  | isCt0 e1 = subExpRes e2-  | isCt0 e2 = subExpRes e1-  | e1 == e2 = subExpRes $ intConst t 0--simplifyBinOp defOf _ (BinOp LogAnd e1 e2)-  | isCt0 e1 = constRes $ BoolValue False-  | isCt0 e2 = constRes $ BoolValue False-  | isCt1 e1 = subExpRes e2-  | isCt1 e2 = subExpRes e1-  | Var v <- e1,-    Just (BasicOp (UnOp Not e1'), v_cs) <- defOf v,-    e1' == e2 = Just (SubExp $ Constant $ BoolValue False, v_cs)-  | Var v <- e2,-    Just (BasicOp (UnOp Not e2'), v_cs) <- defOf v,-    e2' == e1 = Just (SubExp $ Constant $ BoolValue False, v_cs)--simplifyBinOp defOf _ (BinOp LogOr e1 e2)-  | isCt0 e1 = subExpRes e2-  | isCt0 e2 = subExpRes e1-  | isCt1 e1 = constRes $ BoolValue True-  | isCt1 e2 = constRes $ BoolValue True-  | Var v <- e1,-    Just (BasicOp (UnOp Not e1'), v_cs) <- defOf v,-    e1' == e2 = Just (SubExp $ Constant $ BoolValue True, v_cs)-  | Var v <- e2,-    Just (BasicOp (UnOp Not e2'), v_cs) <- defOf v,-    e2' == e1 = Just (SubExp $ Constant $ BoolValue True, v_cs)--simplifyBinOp defOf _ (BinOp (SMax it) e1 e2)-  | e1 == e2 =-      subExpRes e1-  | Var v1 <- e1,-    Just (BasicOp (BinOp (SMax _) e1_1 e1_2), v1_cs) <- defOf v1,-    e1_1 == e2 =-      Just (BinOp (SMax it) e1_2 e2, v1_cs)-  | Var v1 <- e1,-    Just (BasicOp (BinOp (SMax _) e1_1 e1_2), v1_cs) <- defOf v1,-    e1_2 == e2 =-      Just (BinOp (SMax it) e1_1 e2, v1_cs)-  | Var v2 <- e2,-    Just (BasicOp (BinOp (SMax _) e2_1 e2_2), v2_cs) <- defOf v2,-    e2_1 == e1 =-      Just (BinOp (SMax it) e2_2 e1, v2_cs)-  | Var v2 <- e2,-    Just (BasicOp (BinOp (SMax _) e2_1 e2_2), v2_cs) <- defOf v2,-    e2_2 == e1 =-      Just (BinOp (SMax it) e2_1 e1, v2_cs)--simplifyBinOp _ _ _ = Nothing--constRes :: PrimValue -> Maybe (BasicOp, Certificates)-constRes = Just . (,mempty) . SubExp . Constant--subExpRes :: SubExp -> Maybe (BasicOp, Certificates)-subExpRes = Just . (,mempty) . SubExp--simplifyUnOp :: SimpleRule lore-simplifyUnOp _ _ (UnOp op (Constant v)) =-  constRes =<< doUnOp op v-simplifyUnOp defOf _ (UnOp Not (Var v))-  | Just (BasicOp (UnOp Not v2), v_cs) <- defOf v =-      Just (SubExp v2, v_cs)-simplifyUnOp _ _ _ =-  Nothing--simplifyConvOp :: SimpleRule lore-simplifyConvOp _ _ (ConvOp op (Constant v)) =-  constRes =<< doConvOp op v-simplifyConvOp _ _ (ConvOp op se)-  | (from, to) <- convOpType op, from == to =-  subExpRes se-simplifyConvOp lookupVar _ (ConvOp (SExt t2 t1) (Var v))-  | Just (BasicOp (ConvOp (SExt t3 _) se), v_cs) <- lookupVar v,-    t2 >= t3 =-      Just (ConvOp (SExt t3 t1) se, v_cs)-simplifyConvOp lookupVar _ (ConvOp (ZExt t2 t1) (Var v))-  | Just (BasicOp (ConvOp (ZExt t3 _) se), v_cs) <- lookupVar v,-    t2 >= t3 =-      Just (ConvOp (ZExt t3 t1) se, v_cs)-simplifyConvOp lookupVar _ (ConvOp (SIToFP t2 t1) (Var v))-  | Just (BasicOp (ConvOp (SExt t3 _) se), v_cs) <- lookupVar v,-    t2 >= t3 =-      Just (ConvOp (SIToFP t3 t1) se, v_cs)-simplifyConvOp lookupVar _ (ConvOp (UIToFP t2 t1) (Var v))-  | Just (BasicOp (ConvOp (ZExt t3 _) se), v_cs) <- lookupVar v,-    t2 >= t3 =-      Just (ConvOp (UIToFP t3 t1) se, v_cs)-simplifyConvOp lookupVar _ (ConvOp (FPConv t2 t1) (Var v))-  | Just (BasicOp (ConvOp (FPConv t3 _) se), v_cs) <- lookupVar v,-    t2 >= t3 =-      Just (ConvOp (FPConv t3 t1) se, v_cs)-simplifyConvOp _ _ _ =-  Nothing---- If expression is true then just replace assertion.-simplifyAssert :: SimpleRule lore-simplifyAssert _ _ (Assert (Constant (BoolValue True)) _ _) =-  constRes Checked-simplifyAssert _ _ _ =-  Nothing--constantFoldPrimFun :: BinderOps lore => TopDownRuleGeneric lore-constantFoldPrimFun _ (Let pat (StmAux cs attrs _) (Apply fname args _ _))-  | Just args' <- mapM (isConst . fst) args,-    Just (_, _, fun) <- M.lookup (nameToString fname) primFuns,-    Just result <- fun args' =-      Simplify $ certifying cs $ attributing attrs $-      letBind pat $ BasicOp $ SubExp $ Constant result-  where isConst (Constant v) = Just v-        isConst _ = Nothing-constantFoldPrimFun _ _ = Skip--simplifyIndex :: BinderOps lore => BottomUpRuleBasicOp lore-simplifyIndex (vtable, used) pat@(Pattern [] [pe]) (StmAux cs attrs _) (Index idd inds)-  | Just m <- simplifyIndexing vtable seType idd inds consumed = Simplify $ do-      res <- m-      attributing attrs $ case res of-        SubExpResult cs' se ->-          certifying (cs<>cs') $-          letBindNames (patternNames pat) $ BasicOp $ SubExp se-        IndexResult extra_cs idd' inds' ->-          certifying (cs<>extra_cs) $-          letBindNames (patternNames pat) $ BasicOp $ Index idd' inds'-  where consumed = patElemName pe `UT.isConsumed` used-        seType (Var v) = ST.lookupType v vtable-        seType (Constant v) = Just $ Prim $ primValueType v--simplifyIndex _ _ _ _ = Skip--data IndexResult = IndexResult Certificates VName (Slice SubExp)-                 | SubExpResult Certificates SubExp--simplifyIndexing :: MonadBinder m =>-                    ST.SymbolTable (Lore m) -> TypeLookup-                 -> VName -> Slice SubExp -> Bool-                 -> Maybe (m IndexResult)-simplifyIndexing vtable seType idd inds consuming =-  case defOf idd of-    _ | Just t <- seType (Var idd),-        inds == fullSlice t [] ->-          Just $ pure $ SubExpResult mempty $ Var idd--      | Just inds' <- sliceIndices inds,-        Just (ST.Indexed cs e) <- ST.index idd inds' vtable,-        worthInlining e,-        all (`ST.elem` vtable) (unCertificates cs) ->-          Just $ SubExpResult cs <$> toSubExp "index_primexp" e--      | Just inds' <- sliceIndices inds,-        Just (ST.IndexedArray cs arr inds'') <- ST.index idd inds' vtable,-        all worthInlining inds'',-        all (`ST.elem` vtable) (unCertificates cs) ->-          Just $ IndexResult cs arr . map DimFix <$>-          mapM (toSubExp "index_primexp") inds''--    Nothing -> Nothing--    Just (SubExp (Var v), cs) -> Just $ pure $ IndexResult cs v inds--    Just (Iota _ x s to_it, cs)-      | [DimFix ii] <- inds,-        Just (Prim (IntType from_it)) <- seType ii ->-          Just $-          fmap (SubExpResult cs) $ toSubExp "index_iota" $-          sExt to_it (primExpFromSubExp (IntType from_it) ii)-          * primExpFromSubExp (IntType to_it) s-          + primExpFromSubExp (IntType to_it) x-      | [DimSlice i_offset i_n i_stride] <- inds ->-          Just $ do-            i_offset' <- asIntS to_it i_offset-            i_stride' <- asIntS to_it i_stride-            i_offset'' <- toSubExp "iota_offset" $-                          primExpFromSubExp (IntType to_it) x +-                          primExpFromSubExp (IntType to_it) s *-                          primExpFromSubExp (IntType to_it) i_offset'-            i_stride'' <- letSubExp "iota_offset" $-                          BasicOp $ BinOp (Mul Int32 OverflowWrap) s i_stride'-            fmap (SubExpResult cs) $ letSubExp "slice_iota" $-              BasicOp $ Iota i_n i_offset'' i_stride'' to_it--    -- A rotate cannot be simplified away if we are slicing a rotated dimension.-    Just (Rotate offsets a, cs)-      | not $ or $ zipWith rotateAndSlice offsets inds -> Just $ do-      dims <- arrayDims <$> lookupType a-      let adjustI i o d = do-            i_p_o <- letSubExp "i_p_o" $ BasicOp $ BinOp (Add Int32 OverflowWrap) i o-            letSubExp "rot_i" (BasicOp $ BinOp (SMod Int32 Unsafe) i_p_o d)-          adjust (DimFix i, o, d) =-            DimFix <$> adjustI i o d-          adjust (DimSlice i n s, o, d) =-            DimSlice <$> adjustI i o d <*> pure n <*> pure s-      IndexResult cs a <$> mapM adjust (zip3 inds offsets dims)-        where rotateAndSlice r DimSlice{} = not $ isCt0 r-              rotateAndSlice _ _ = False--    Just (Index aa ais, cs) ->-      Just $ IndexResult cs aa <$>-      subExpSlice (sliceSlice (primExpSlice ais) (primExpSlice inds))--    Just (Replicate (Shape [_]) (Var vv), cs)-      | [DimFix{}]   <- inds, not consuming -> Just $ pure $ SubExpResult cs $ Var vv-      | DimFix{}:is' <- inds, not consuming -> Just $ pure $ IndexResult cs vv is'--    Just (Replicate (Shape [_]) val@(Constant _), cs)-      | [DimFix{}] <- inds, not consuming -> Just $ pure $ SubExpResult cs val--    Just (Replicate (Shape ds) v, cs)-      | (ds_inds, rest_inds) <- splitAt (length ds) inds,-        (ds', ds_inds') <- unzip $ mapMaybe index ds_inds,-        ds' /= ds ->-        Just $ do-          arr <- letExp "smaller_replicate" $ BasicOp $ Replicate (Shape ds') v-          return $ IndexResult cs arr $ ds_inds' ++ rest_inds-      where index DimFix{} = Nothing-            index (DimSlice _ n s) = Just (n, DimSlice (constant (0::Int32)) n s)--    Just (Rearrange perm src, cs)-       | rearrangeReach perm <= length (takeWhile isIndex inds) ->-         let inds' = rearrangeShape (rearrangeInverse perm) inds-         in Just $ pure $ IndexResult cs src inds'-      where isIndex DimFix{} = True-            isIndex _          = False--    Just (Copy src, cs)-      | Just dims <- arrayDims <$> seType (Var src),-        length inds == length dims,-        not consuming, ST.available src vtable ->-          Just $ pure $ IndexResult cs src inds--    Just (Reshape newshape src, cs)-      | Just newdims <- shapeCoercion newshape,-        Just olddims <- arrayDims <$> seType (Var src),-        changed_dims <- zipWith (/=) newdims olddims,-        not $ or $ drop (length inds) changed_dims ->-        Just $ pure $ IndexResult cs src inds--      | Just newdims <- shapeCoercion newshape,-        Just olddims <- arrayDims <$> seType (Var src),-        length newshape == length inds,-        length olddims == length newdims ->-        Just $ pure $ IndexResult cs src inds--    Just (Reshape [_] v2, cs)-      | Just [_] <- arrayDims <$> seType (Var v2) ->-        Just $ pure $ IndexResult cs v2 inds--    Just (Concat d x xs _, cs)-      | Just (ibef, DimFix i, iaft) <- focusNth d inds,-        Just (Prim res_t) <- (`setArrayDims` sliceDims inds) <$>-                             ST.lookupType x vtable -> Just $ do-      x_len <- arraySize d <$> lookupType x-      xs_lens <- mapM (fmap (arraySize d) . lookupType) xs--      let add n m = do-            added <- letSubExp "index_concat_add" $ BasicOp $ BinOp (Add Int32 OverflowWrap) n m-            return (added, n)-      (_, starts) <- mapAccumLM add x_len xs_lens-      let xs_and_starts = reverse $ zip xs starts--      let mkBranch [] =-            letSubExp "index_concat" $ BasicOp $ Index x $ ibef ++ DimFix i : iaft-          mkBranch ((x', start):xs_and_starts') = do-            cmp <- letSubExp "index_concat_cmp" $ BasicOp $ CmpOp (CmpSle Int32) start i-            (thisres, thisbnds) <- collectStms $ do-              i' <- letSubExp "index_concat_i" $ BasicOp $ BinOp (Sub Int32 OverflowWrap) i start-              letSubExp "index_concat" $ BasicOp $ Index x' $ ibef ++ DimFix i' : iaft-            thisbody <- mkBodyM thisbnds [thisres]-            (altres, altbnds) <- collectStms $ mkBranch xs_and_starts'-            altbody <- mkBodyM altbnds [altres]-            letSubExp "index_concat_branch" $ If cmp thisbody altbody $-              IfDec [primBodyType res_t] IfNormal-      SubExpResult cs <$> mkBranch xs_and_starts--    Just (ArrayLit ses _, cs)-      | DimFix (Constant (IntValue (Int32Value i))) : inds' <- inds,-        Just se <- maybeNth i ses ->-        case inds' of-          [] -> Just $ pure $ SubExpResult cs se-          _ | Var v2 <- se  -> Just $ pure $ IndexResult cs v2 inds'-          _ -> Nothing--    -- Indexing single-element arrays.  We know the index must be 0.-    _ | Just t <- seType $ Var idd, isCt1 $ arraySize 0 t,-        DimFix i : inds' <- inds, not $ isCt0 i ->-          Just $ pure $ IndexResult mempty idd $-          DimFix (constant (0::Int32)) : inds'--    _ -> Nothing--    where defOf v = do (BasicOp op, def_cs) <- ST.lookupExp v vtable-                       return (op, def_cs)--          -- | A crude heuristic for determining when a PrimExp is-          -- worth inlining over keeping it in an array and reading it-          -- from memory.-          worthInlining e-            | primExpSizeAtLeast 20 e = False -- totally ad-hoc.-            | otherwise = worthInlining' e-          worthInlining' (BinOpExp Pow{} _ _) = False-          worthInlining' (BinOpExp FPow{} _ _) = False-          worthInlining' (BinOpExp _ x y) = worthInlining' x && worthInlining' y-          worthInlining' (CmpOpExp _ x y) = worthInlining' x && worthInlining' y-          worthInlining' (ConvOpExp _ x) = worthInlining' x-          worthInlining' (UnOpExp _ x) = worthInlining' x-          worthInlining' FunExp{} = False-          worthInlining' _ = True--simplifyConcat :: BinderOps lore => BottomUpRuleBasicOp lore---- concat@1(transpose(x),transpose(y)) == transpose(concat@0(x,y))-simplifyConcat (vtable, _) pat _ (Concat i x xs new_d)-  | Just r <- arrayRank <$> ST.lookupType x vtable,-    let perm = [i] ++ [0..i-1] ++ [i+1..r-1],-    Just (x',x_cs) <- transposedBy perm x,-    Just (xs',xs_cs) <- unzip <$> mapM (transposedBy perm) xs = Simplify $ do-      concat_rearrange <--        certifying (x_cs<>mconcat xs_cs) $-        letExp "concat_rearrange" $ BasicOp $ Concat 0 x' xs' new_d-      letBind pat $ BasicOp $ Rearrange perm concat_rearrange-  where transposedBy perm1 v =-          case ST.lookupExp v vtable of-            Just (BasicOp (Rearrange perm2 v'), vcs)-              | perm1 == perm2 -> Just (v', vcs)-            _ -> Nothing---- concat xs (concat ys zs) == concat xs ys zs-simplifyConcat (vtable, _) pat (StmAux cs attrs _) (Concat i x xs new_d)-  | x' /= x || concat xs' /= xs = Simplify $-      certifying (cs<>x_cs<>mconcat xs_cs) $-      attributing attrs $ letBind pat $-      BasicOp $ Concat i x' (zs++concat xs') new_d-  where (x':zs, x_cs) = isConcat x-        (xs', xs_cs) = unzip $ map isConcat xs-        isConcat v = case ST.lookupBasicOp v vtable of-                       Just (Concat j y ys _, v_cs) | j == i -> (y : ys, v_cs)-                       _ -> ([v], mempty)---- If concatenating a bunch of array literals (or equivalent--- replicate), just construct the array literal instead.-simplifyConcat (vtable, _) pat aux (Concat 0 x xs _)-  | Just (vs, vcs) <- unzip <$> mapM isArrayLit (x:xs) = Simplify $ do-      rt <- rowType <$> lookupType x-      certifying (mconcat vcs) $ auxing aux $-        letBind pat $ BasicOp $ ArrayLit (concat vs) rt-      where isArrayLit v-              | Just (Replicate shape se, vcs) <- ST.lookupBasicOp v vtable,-                unitShape shape = Just ([se], vcs)-              | Just (ArrayLit ses _, vcs) <- ST.lookupBasicOp v vtable =-                  Just (ses, vcs)-              | otherwise =-                  Nothing--            unitShape = (==Shape [Constant $ IntValue $ Int32Value 1])--simplifyConcat _ _ _  _ = Skip--ruleIf :: BinderOps lore => TopDownRuleIf lore--ruleIf _ pat _ (e1, tb, fb, IfDec _ ifsort)-  | Just branch <- checkBranch,-    ifsort /= IfFallback || isCt1 e1 = Simplify $ do-  let ses = bodyResult branch-  addStms $ bodyStms branch-  sequence_ [ letBindNames [patElemName p] $ BasicOp $ SubExp se-            | (p,se) <- zip (patternElements pat) ses]--  where checkBranch-          | isCt1 e1  = Just tb-          | isCt0 e1  = Just fb-          | otherwise = Nothing---- IMPROVE: the following two rules can be generalised to work in more--- cases, especially when the branches have bindings, or return more--- than one value.------ if c then True else v == c || v-ruleIf _ pat _-  (cond, Body _ tstms [Constant (BoolValue True)],-         Body _ fstms [se], IfDec ts _)-  | null tstms, null fstms, [Prim Bool] <- map extTypeOf ts =-      Simplify $ letBind pat $ BasicOp $ BinOp LogOr cond se---- When type(x)==bool, if c then x else y == (c && x) || (!c && y)-ruleIf _ pat _ (cond, tb, fb, IfDec ts _)-  | Body _ tstms [tres] <- tb,-    Body _ fstms [fres] <- fb,-    all (safeExp . stmExp) $ tstms <> fstms,-    all ((==Prim Bool) . extTypeOf) ts = Simplify $ do-  addStms tstms-  addStms fstms-  e <- eBinOp LogOr (pure $ BasicOp $ BinOp LogAnd cond tres)-                    (eBinOp LogAnd (pure $ BasicOp $ UnOp Not cond)-                     (pure $ BasicOp $ SubExp fres))-  letBind pat e--ruleIf _ pat _ (_, tbranch, _, IfDec _ IfFallback)-  | null $ patternContextNames pat,-    all (safeExp . stmExp) $ bodyStms tbranch = Simplify $ do-      let ses = bodyResult tbranch-      addStms $ bodyStms tbranch-      sequence_ [ letBindNames [patElemName p] $ BasicOp $ SubExp se-                | (p,se) <- zip (patternElements pat) ses]--ruleIf _ pat _ (cond, tb, fb, _)-  | Body _ _ [Constant (IntValue t)] <- tb,-    Body _ _ [Constant (IntValue f)] <- fb =-      if oneIshInt t && zeroIshInt f-      then Simplify $-           letBind pat $ BasicOp $ ConvOp (BToI (intValueType t)) cond-      else if zeroIshInt t && oneIshInt f-      then Simplify $ do-        cond_neg <- letSubExp "cond_neg" $ BasicOp $ UnOp Not cond-        letBind pat $ BasicOp $ ConvOp (BToI (intValueType t)) cond_neg-      else Skip--ruleIf _ _ _ _ = Skip---- | Move out results of a conditional expression whose computation is--- either invariant to the branches (only done for results in the--- context), or the same in both branches.-hoistBranchInvariant :: BinderOps lore => TopDownRuleIf lore-hoistBranchInvariant _ pat _ (cond, tb, fb, IfDec ret ifsort) = Simplify $ do-  let tses = bodyResult tb-      fses = bodyResult fb-  (hoistings, (pes, ts, res)) <--    fmap (fmap unzip3 . partitionEithers) $ mapM branchInvariant $-      zip3 (patternElements pat)-           (map Left [0..num_ctx-1] ++ map Right ret)-           (zip tses fses)-  let ctx_fixes = catMaybes hoistings-      (tses', fses') = unzip res-      tb' = tb { bodyResult = tses' }-      fb' = fb { bodyResult = fses' }-      ret' = foldr (uncurry fixExt) (rights ts) ctx_fixes-      (ctx_pes, val_pes) = splitFromEnd (length ret') pes-  if not $ null hoistings -- Was something hoisted?-     then do -- We may have to add some reshapes if we made the type-             -- less existential.-             tb'' <- reshapeBodyResults tb' $ map extTypeOf ret'-             fb'' <- reshapeBodyResults fb' $ map extTypeOf ret'-             letBind (Pattern ctx_pes val_pes) $-               If cond tb'' fb'' (IfDec ret' ifsort)-     else cannotSimplify-  where num_ctx = length $ patternContextElements pat-        bound_in_branches = namesFromList $ concatMap (patternNames . stmPattern) $-                            bodyStms tb <> bodyStms fb-        mem_sizes = freeIn $ filter (isMem . patElemType) $ patternElements pat-        invariant Constant{} = True-        invariant (Var v) = not $ v `nameIn` bound_in_branches--        isMem Mem{} = True-        isMem _ = False-        sizeOfMem v = v `nameIn` mem_sizes--        branchInvariant (pe, t, (tse, fse))-          -- Do both branches return the same value?-          | tse == fse = do-              letBindNames [patElemName pe] $ BasicOp $ SubExp tse-              hoisted pe t--          -- Do both branches return values that are free in the-          -- branch, and are we not the only pattern element?  The-          -- latter is to avoid infinite application of this rule.-          | invariant tse, invariant fse, patternSize pat > 1,-            Prim _ <- patElemType pe, not $ sizeOfMem $ patElemName pe = do-              bt <- expTypesFromPattern $ Pattern [] [pe]-              letBindNames [patElemName pe] =<<-                (If cond <$> resultBodyM [tse]-                         <*> resultBodyM [fse]-                         <*> pure (IfDec bt ifsort))-              hoisted pe t--          | otherwise =-              return $ Right (pe, t, (tse,fse))--        hoisted pe (Left i) = return $ Left $ Just (i, Var $ patElemName pe)-        hoisted _ Right{}   = return $ Left Nothing--        reshapeBodyResults body rets = insertStmsM $ do-          ses <- bodyBind body-          let (ctx_ses, val_ses) = splitFromEnd (length rets) ses-          resultBodyM . (ctx_ses++) =<< zipWithM reshapeResult val_ses rets-        reshapeResult (Var v) t@Array{} = do-          v_t <- lookupType v-          let newshape = arrayDims $ removeExistentials t v_t-          if newshape /= arrayDims v_t-            then letSubExp "branch_ctx_reshaped" $ shapeCoerce newshape v-            else return $ Var v-        reshapeResult se _ =-          return se--simplifyIdentityReshape :: SimpleRule lore-simplifyIdentityReshape _ seType (Reshape newshape v)-  | Just t <- seType $ Var v,-    newDims newshape == arrayDims t = -- No-op reshape.-      subExpRes $ Var v-simplifyIdentityReshape _ _ _ = Nothing--simplifyReshapeReshape :: SimpleRule lore-simplifyReshapeReshape defOf _ (Reshape newshape v)-  | Just (BasicOp (Reshape oldshape v2), v_cs) <- defOf v =-    Just (Reshape (fuseReshape oldshape newshape) v2, v_cs)-simplifyReshapeReshape _ _ _ = Nothing--simplifyReshapeScratch :: SimpleRule lore-simplifyReshapeScratch defOf _ (Reshape newshape v)-  | Just (BasicOp (Scratch bt _), v_cs) <- defOf v =-    Just (Scratch bt $ newDims newshape, v_cs)-simplifyReshapeScratch _ _ _ = Nothing--simplifyReshapeReplicate :: SimpleRule lore-simplifyReshapeReplicate defOf seType (Reshape newshape v)-  | Just (BasicOp (Replicate _ se), v_cs) <- defOf v,-    Just oldshape <- arrayShape <$> seType se,-    shapeDims oldshape `isSuffixOf` newDims newshape =-      let new = take (length newshape - shapeRank oldshape) $-                newDims newshape-      in Just (Replicate (Shape new) se, v_cs)-simplifyReshapeReplicate _ _ _ = Nothing--simplifyReshapeIota :: SimpleRule lore-simplifyReshapeIota defOf _ (Reshape newshape v)-  | Just (BasicOp (Iota _ offset stride it), v_cs) <- defOf v,-    [n] <- newDims newshape =-      Just (Iota n offset stride it, v_cs)-simplifyReshapeIota _ _ _ = Nothing--improveReshape :: SimpleRule lore-improveReshape _ seType (Reshape newshape v)-  | Just t <- seType $ Var v,-    newshape' <- informReshape (arrayDims t) newshape,-    newshape' /= newshape =-      Just (Reshape newshape' v, mempty)-improveReshape _ _ _ = Nothing---- | If we are copying a scratch array (possibly indirectly), just turn it into a scratch by--- itself.-copyScratchToScratch :: SimpleRule lore-copyScratchToScratch defOf seType (Copy src) = do-  t <- seType $ Var src-  if isActuallyScratch src then-    Just (Scratch (elemType t) (arrayDims t), mempty)-    else Nothing-  where isActuallyScratch v =-          case asBasicOp . fst =<< defOf v of-            Just Scratch{} -> True-            Just (Rearrange _ v') -> isActuallyScratch v'-            Just (Reshape _ v') -> isActuallyScratch v'-            _ -> False-copyScratchToScratch _ _ _ =-  Nothing--ruleBasicOp :: BinderOps lore => TopDownRuleBasicOp lore---- Check all the simpleRules.-ruleBasicOp vtable pat aux op-  | Just (op', cs) <- msum [ rule defOf seType op | rule <- simpleRules ] =-      Simplify $ certifying (cs <> stmAuxCerts aux) $ letBind pat $ BasicOp op'-  where defOf = (`ST.lookupExp` vtable)-        seType (Var v) = ST.lookupType v vtable-        seType (Constant v) = Just $ Prim $ primValueType v--ruleBasicOp vtable pat _ (Update src _ (Var v))-  | Just (BasicOp Scratch{}, _) <- ST.lookupExp v vtable =-      Simplify $ letBind pat $ BasicOp $ SubExp $ Var src---- If we are writing a single-element slice from some array, and the--- element of that array can be computed as a PrimExp based on the--- index, let's just write that instead.-ruleBasicOp vtable pat aux (Update src [DimSlice i n s] (Var v))-  | isCt1 n, isCt1 s,-    Just (ST.Indexed cs e) <- ST.index v [intConst Int32 0] vtable =-      Simplify $ do-        e' <- toSubExp "update_elem" e-        auxing aux $ certifying cs $-          letBind pat $ BasicOp $ Update src [DimFix i] e'--ruleBasicOp vtable pat _ (Update dest destis (Var v))-  | Just (e, _) <- ST.lookupExp v vtable,-    arrayFrom e =-      Simplify $ letBind pat $ BasicOp $ SubExp $ Var dest-  where arrayFrom (BasicOp (Copy copy_v))-          | Just (e',_) <- ST.lookupExp copy_v vtable =-              arrayFrom e'-        arrayFrom (BasicOp (Index src srcis)) =-          src == dest && destis == srcis-        arrayFrom (BasicOp (Replicate v_shape v_se))-          | Just (Replicate dest_shape dest_se, _) <- ST.lookupBasicOp dest vtable,-            v_se == dest_se,-            shapeDims v_shape `isSuffixOf` shapeDims dest_shape =-              True-        arrayFrom _ =-          False---- | Turn in-place updates that replace an entire array into just--- array literals.-ruleBasicOp vtable pat _ (Update dest is se)-  | Just dest_t <- ST.lookupType dest vtable,-    isFullSlice (arrayShape dest_t) is = Simplify $-      case se of-        Var v | not $ null $ sliceDims is -> do-                  v_reshaped <- letExp (baseString v ++ "_reshaped") $-                                BasicOp $ Reshape (map DimNew $ arrayDims dest_t) v-                  letBind pat $ BasicOp $ Copy v_reshaped--        _ -> letBind pat $ BasicOp $ ArrayLit [se] $ rowType dest_t---- | Simplify a chain of in-place updates and copies.  This chain is--- often produced by in-place lowering.-ruleBasicOp vtable pat (StmAux cs1 attrs _) (Update dest1 is1 (Var v1))-  | Just (Update dest2 is2 se2, cs2) <- ST.lookupBasicOp v1 vtable,-    Just (Copy v3, cs3) <- ST.lookupBasicOp dest2 vtable,-    Just (Index v4 is4, cs4) <- ST.lookupBasicOp v3 vtable,-    is4 == is1, v4 == dest1 =-      Simplify $ certifying (cs1 <> cs2 <> cs3 <> cs4) $ do-      is5 <- subExpSlice $ sliceSlice (primExpSlice is1) (primExpSlice is2)-      attributing attrs $ letBind pat $ BasicOp $ Update dest1 is5 se2---- | If we are comparing X against the result of a branch of the form--- @if P then Y else Z@ then replace comparison with '(P && X == Y) ||--- (!P && X == Z').  This may allow us to get rid of a branch, and the--- extra comparisons may be constant-folded out.  Question: maybe we--- should have some more checks to ensure that we only do this if that--- is actually the case, such as if we will obtain at least one--- constant-to-constant comparison?-ruleBasicOp vtable pat _ (CmpOp (CmpEq t) se1 se2)-  | Just m <- simplifyWith se1 se2 = Simplify m-  | Just m <- simplifyWith se2 se1 = Simplify m-  where simplifyWith (Var v) x-          | Just bnd <- ST.lookupStm v vtable,-            If p tbranch fbranch _ <- stmExp bnd,-            Just (y, z) <--              returns v (stmPattern bnd) tbranch fbranch,-            not $ boundInBody tbranch `namesIntersect` freeIn y,-            not $ boundInBody fbranch `namesIntersect` freeIn z = Just $ do-                eq_x_y <--                  letSubExp "eq_x_y" $ BasicOp $ CmpOp (CmpEq t) x y-                eq_x_z <--                  letSubExp "eq_x_z" $ BasicOp $ CmpOp (CmpEq t) x z-                p_and_eq_x_y <--                  letSubExp "p_and_eq_x_y" $ BasicOp $ BinOp LogAnd p eq_x_y-                not_p <--                  letSubExp "not_p" $ BasicOp $ UnOp Not p-                not_p_and_eq_x_z <--                  letSubExp "p_and_eq_x_y" $ BasicOp $ BinOp LogAnd not_p eq_x_z-                letBind pat $-                  BasicOp $ BinOp LogOr p_and_eq_x_y not_p_and_eq_x_z-        simplifyWith _ _ =-          Nothing--        returns v ifpat tbranch fbranch =-          fmap snd $-          find ((==v) . patElemName . fst) $-          zip (patternValueElements ifpat) $-          zip (bodyResult tbranch) (bodyResult fbranch)--ruleBasicOp _ pat _ (Replicate (Shape []) se@Constant{}) =-  Simplify $ letBind pat $ BasicOp $ SubExp se-ruleBasicOp _ pat _ (Replicate (Shape []) (Var v)) = Simplify $ do-  v_t <- lookupType v-  letBind pat $ BasicOp $ if primType v_t-                           then SubExp $ Var v-                           else Copy v-ruleBasicOp vtable pat _  (Replicate shape (Var v))-  | Just (BasicOp (Replicate shape2 se), cs) <- ST.lookupExp v vtable =-      Simplify $ certifying cs $ letBind pat $ BasicOp $ Replicate (shape<>shape2) se---- | Turn array literals with identical elements into replicates.-ruleBasicOp _ pat _ (ArrayLit (se:ses) _)-  | all (==se) ses =-    Simplify $ let n = constant (fromIntegral (length ses) + 1 :: Int32)-               in letBind pat $ BasicOp $ Replicate (Shape [n]) se--ruleBasicOp vtable pat aux (Index idd slice)-  | Just inds <- sliceIndices slice,-    Just (BasicOp (Reshape newshape idd2), idd_cs) <- ST.lookupExp idd vtable,-    length newshape == length inds =-      Simplify $-      case shapeCoercion newshape of-        Just _ ->-          certifying idd_cs $ auxing aux $-            letBind pat $ BasicOp $ Index idd2 slice-        Nothing -> do-          -- Linearise indices and map to old index space.-          oldshape <- arrayDims <$> lookupType idd2-          let new_inds =-                reshapeIndex (map (primExpFromSubExp int32) oldshape)-                             (map (primExpFromSubExp int32) $ newDims newshape)-                             (map (primExpFromSubExp int32) inds)-          new_inds' <--            mapM (toSubExp "new_index" . asInt32PrimExp) new_inds-          certifying idd_cs $ auxing aux $-            letBind pat $ BasicOp $ Index idd2 $ map DimFix new_inds'--ruleBasicOp _ pat _ (BinOp (Pow t) e1 e2)-  | e1 == intConst t 2 =-      Simplify $ letBind pat $ BasicOp $ BinOp (Shl t) (intConst t 1) e2---- Handle identity permutation.-ruleBasicOp _ pat _ (Rearrange perm v)-  | sort perm == perm =-      Simplify $ letBind pat $ BasicOp $ SubExp $ Var v--ruleBasicOp vtable pat aux (Rearrange perm v)-  | Just (BasicOp (Rearrange perm2 e), v_cs) <- ST.lookupExp v vtable =-      -- Rearranging a rearranging: compose the permutations.-      Simplify $ certifying v_cs $ auxing aux $-      letBind pat $ BasicOp $ Rearrange (perm `rearrangeCompose` perm2) e--ruleBasicOp vtable pat aux (Rearrange perm v)-  | Just (BasicOp (Rotate offsets v2), v_cs) <- ST.lookupExp v vtable,-    Just (BasicOp (Rearrange perm3 v3), v2_cs) <- ST.lookupExp v2 vtable = Simplify $ do-      let offsets' = rearrangeShape (rearrangeInverse perm3) offsets-      rearrange_rotate <- letExp "rearrange_rotate" $ BasicOp $ Rotate offsets' v3-      certifying (v_cs<>v2_cs) $ auxing aux $-        letBind pat $ BasicOp $ Rearrange (perm `rearrangeCompose` perm3) rearrange_rotate---- Rearranging a replicate where the outer dimension is left untouched.-ruleBasicOp vtable pat aux (Rearrange perm v1)-  | Just (BasicOp (Replicate dims (Var v2)), v1_cs) <- ST.lookupExp v1 vtable,-    num_dims <- shapeRank dims,-    (rep_perm, rest_perm) <- splitAt num_dims perm,-    not $ null rest_perm,-    rep_perm == [0..length rep_perm-1] =-      Simplify $ certifying v1_cs $ auxing aux $ do-      v <- letSubExp "rearrange_replicate" $-           BasicOp $ Rearrange (map (subtract num_dims) rest_perm) v2-      letBind pat $ BasicOp $ Replicate dims v---- A zero-rotation is identity.-ruleBasicOp _ pat _ (Rotate offsets v)-  | all isCt0 offsets = Simplify $ letBind pat $ BasicOp $ SubExp $ Var v--ruleBasicOp vtable pat aux (Rotate offsets v)-  | Just (BasicOp (Rearrange perm v2), v_cs) <- ST.lookupExp v vtable,-    Just (BasicOp (Rotate offsets2 v3), v2_cs) <- ST.lookupExp v2 vtable = Simplify $ do-      let offsets2' = rearrangeShape (rearrangeInverse perm) offsets2-          addOffsets x y = letSubExp "summed_offset" $ BasicOp $ BinOp (Add Int32 OverflowWrap) x y-      offsets' <- zipWithM addOffsets offsets offsets2'-      rotate_rearrange <--        auxing aux $ letExp "rotate_rearrange" $ BasicOp $ Rearrange perm v3-      certifying (v_cs <> v2_cs) $-        letBind pat $ BasicOp $ Rotate offsets' rotate_rearrange---- Combining Rotates.-ruleBasicOp vtable pat aux (Rotate offsets1 v)-  | Just (BasicOp (Rotate offsets2 v2), v_cs) <- ST.lookupExp v vtable = Simplify $ do-      offsets <- zipWithM add offsets1 offsets2-      certifying v_cs $ auxing aux $-        letBind pat $ BasicOp $ Rotate offsets v2-        where add x y = letSubExp "offset" $ BasicOp $ BinOp (Add Int32 OverflowWrap) x y---- If we see an Update with a scalar where the value to be written is--- the result of indexing some other array, then we convert it into an--- Update with a slice of that array.  This matters when the arrays--- are far away (on the GPU, say), because it avoids a copy of the--- scalar to and from the host.-ruleBasicOp vtable pat aux (Update arr_x slice_x (Var v))-  | Just _ <- sliceIndices slice_x,-    Just (Index arr_y slice_y, cs_y) <- ST.lookupBasicOp v vtable,-    ST.available arr_y vtable,-    -- XXX: we should check for proper aliasing here instead.-    arr_y /= arr_x,-    Just (slice_x_bef, DimFix i, []) <- focusNth (length slice_x - 1) slice_x,-    Just (slice_y_bef, DimFix j, []) <- focusNth (length slice_y - 1) slice_y = Simplify $ do-      let slice_x' = slice_x_bef ++ [DimSlice i (intConst Int32 1) (intConst Int32 1)]-          slice_y' = slice_y_bef ++ [DimSlice j (intConst Int32 1) (intConst Int32 1)]-      v' <- letExp (baseString v ++ "_slice") $ BasicOp $ Index arr_y slice_y'-      certifying cs_y $ auxing aux $-        letBind pat $ BasicOp $ Update arr_x slice_x' $ Var v'---- Simplify away 0<=i when 'i' is from a loop of form 'for i < n'.-ruleBasicOp vtable pat aux (CmpOp CmpSle{} x y)-  | Constant (IntValue (Int32Value 0)) <- x,-    Var v <- y,-    Just _ <- ST.lookupLoopVar v vtable =-      Simplify $ auxing aux $ letBind pat $ BasicOp $ SubExp $ constant True---- Simplify away i<n when 'i' is from a loop of form 'for i < n'.-ruleBasicOp vtable pat aux (CmpOp CmpSlt{} x y)-  | Var v <- x,-    Just n <- ST.lookupLoopVar v vtable,-    n == y =-      Simplify $ auxing aux $ letBind pat $ BasicOp $ SubExp $ constant True---- Simplify away x<0 when 'x' has been used as array size.-ruleBasicOp vtable pat aux (CmpOp CmpSlt{} (Var x) y)-  | isCt0 y,-    maybe False ST.entryIsSize $ ST.lookup x vtable =-      Simplify $ auxing aux $ letBind pat $ BasicOp $ SubExp $ constant False--ruleBasicOp _ _ _ _ =-  Skip---- | Remove the return values of a branch, that are not actually used--- after a branch.  Standard dead code removal can remove the branch--- if *none* of the return values are used, but this rule is more--- precise.-removeDeadBranchResult :: BinderOps lore => BottomUpRuleIf lore-removeDeadBranchResult (_, used) pat _ (e1, tb, fb, IfDec rettype ifsort)-  | -- Only if there is no existential context...-    patternSize pat == length rettype,-    -- Figure out which of the names in 'pat' are used...-    patused <- map (`UT.isUsedDirectly` used) $ patternNames pat,-    -- If they are not all used, then this rule applies.-    not (and patused) =-  -- Remove the parts of the branch-results that correspond to dead-  -- return value bindings.  Note that this leaves dead code in the-  -- branch bodies, but that will be removed later.-  let tses = bodyResult tb-      fses = bodyResult fb-      pick :: [a] -> [a]-      pick = map snd . filter fst . zip patused-      tb' = tb { bodyResult = pick tses }-      fb' = fb { bodyResult = pick fses }-      pat' = pick $ patternElements pat-      rettype' = pick rettype-  in Simplify $ letBind (Pattern [] pat') $ If e1 tb' fb' $ IfDec rettype' ifsort--  | otherwise = Skip-+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}++-- | This module defines a collection of simplification rules, as per+-- "Futhark.Optimise.Simplify.Rule".  They are used in the+-- simplifier.+--+-- For performance reasons, many sufficiently simple logically+-- separate rules are merged into single "super-rules", like ruleIf+-- and ruleBasicOp.  This is because it is relatively expensive to+-- activate a rule just to determine that it does not apply.  Thus, it+-- is more efficient to have a few very fat rules than a lot of small+-- rules.  This does not affect the compiler result in any way; it is+-- purely an optimisation to speed up compilation.+module Futhark.Optimise.Simplify.Rules+  ( standardRules,+    removeUnnecessaryCopy,+  )+where++import Control.Monad+import Data.Either+import Data.List (find, foldl', isSuffixOf, partition, sort)+import qualified Data.Map.Strict as M+import Data.Maybe+import Futhark.Analysis.DataDependencies+import Futhark.Analysis.PrimExp.Convert+import qualified Futhark.Analysis.SymbolTable as ST+import qualified Futhark.Analysis.UsageTable as UT+import Futhark.Construct+import Futhark.IR+import Futhark.IR.Prop.Aliases+import Futhark.Optimise.Simplify.ClosedForm+import Futhark.Optimise.Simplify.Rule+import Futhark.Transform.Rename+import Futhark.Util++topDownRules :: (BinderOps lore, Aliased lore) => [TopDownRule lore]+topDownRules =+  [ RuleDoLoop hoistLoopInvariantMergeVariables,+    RuleDoLoop simplifyClosedFormLoop,+    RuleDoLoop simplifyKnownIterationLoop,+    RuleDoLoop simplifyLoopVariables,+    RuleDoLoop narrowLoopType,+    RuleGeneric constantFoldPrimFun,+    RuleIf ruleIf,+    RuleIf hoistBranchInvariant,+    RuleBasicOp ruleBasicOp+  ]++bottomUpRules :: BinderOps lore => [BottomUpRule lore]+bottomUpRules =+  [ RuleDoLoop removeRedundantMergeVariables,+    RuleIf removeDeadBranchResult,+    RuleBasicOp simplifyIndex,+    RuleBasicOp simplifyConcat+  ]++-- | A set of standard simplification rules.  These assume pure+-- functional semantics, and so probably should not be applied after+-- memory block merging.+standardRules :: (BinderOps lore, Aliased lore) => RuleBook lore+standardRules = ruleBook topDownRules bottomUpRules++-- This next one is tricky - it's easy enough to determine that some+-- loop result is not used after the loop, but here, we must also make+-- sure that it does not affect any other values.+--+-- I do not claim that the current implementation of this rule is+-- perfect, but it should suffice for many cases, and should never+-- generate wrong code.+removeRedundantMergeVariables :: BinderOps lore => BottomUpRuleDoLoop lore+removeRedundantMergeVariables (_, used) pat aux (ctx, val, form, body)+  | not $ all (usedAfterLoop . fst) val,+    null ctx -- FIXME: things get tricky if we can remove all vals+    -- but some ctxs are still used.  We take the easy way+    -- out for now.+    =+    let (ctx_es, val_es) = splitAt (length ctx) $ bodyResult body+        necessaryForReturned =+          findNecessaryForReturned+            usedAfterLoopOrInForm+            (zip (map fst $ ctx ++ val) $ ctx_es ++ val_es)+            (dataDependencies body)++        resIsNecessary ((v, _), _) =+          usedAfterLoop v+            || paramName v `nameIn` necessaryForReturned+            || referencedInPat v+            || referencedInForm v++        (keep_ctx, discard_ctx) =+          partition resIsNecessary $ zip ctx ctx_es+        (keep_valpart, discard_valpart) =+          partition (resIsNecessary . snd) $+            zip (patternValueElements pat) $ zip val val_es++        (keep_valpatelems, keep_val) = unzip keep_valpart+        (_discard_valpatelems, discard_val) = unzip discard_valpart+        (ctx', ctx_es') = unzip keep_ctx+        (val', val_es') = unzip keep_val++        body' = body {bodyResult = ctx_es' ++ val_es'}+        free_in_keeps = freeIn keep_valpatelems++        stillUsedContext pat_elem =+          patElemName pat_elem+            `nameIn` ( free_in_keeps+                         <> freeIn (filter (/= pat_elem) $ patternContextElements pat)+                     )++        pat' =+          pat+            { patternValueElements = keep_valpatelems,+              patternContextElements =+                filter stillUsedContext $ patternContextElements pat+            }+     in if ctx' ++ val' == ctx ++ val+          then Skip+          else Simplify $ do+            -- We can't just remove the bindings in 'discard', since the loop+            -- body may still use their names in (now-dead) expressions.+            -- Hence, we add them inside the loop, fully aware that dead-code+            -- removal will eventually get rid of them.  Some care is+            -- necessary to handle unique bindings.+            body'' <- insertStmsM $ do+              mapM_ (uncurry letBindNames) $ dummyStms discard_ctx+              mapM_ (uncurry letBindNames) $ dummyStms discard_val+              return body'+            auxing aux $ letBind pat' $ DoLoop ctx' val' form body''+  where+    pat_used = map (`UT.isUsedDirectly` used) $ patternValueNames pat+    used_vals = map fst $ filter snd $ zip (map (paramName . fst) val) pat_used+    usedAfterLoop = flip elem used_vals . paramName+    usedAfterLoopOrInForm p =+      usedAfterLoop p || paramName p `nameIn` freeIn form+    patAnnotNames = freeIn $ map fst $ ctx ++ val+    referencedInPat = (`nameIn` patAnnotNames) . paramName+    referencedInForm = (`nameIn` freeIn form) . paramName++    dummyStms = map dummyStm+    dummyStm ((p, e), _)+      | unique (paramDeclType p),+        Var v <- e =+        ([paramName p], BasicOp $ Copy v)+      | otherwise = ([paramName p], BasicOp $ SubExp e)+removeRedundantMergeVariables _ _ _ _ =+  Skip++-- We may change the type of the loop if we hoist out a shape+-- annotation, in which case we also need to tweak the bound pattern.+hoistLoopInvariantMergeVariables :: BinderOps lore => TopDownRuleDoLoop lore+hoistLoopInvariantMergeVariables vtable pat aux (ctx, val, form, loopbody) =+  -- Figure out which of the elements of loopresult are+  -- loop-invariant, and hoist them out.+  case foldr checkInvariance ([], explpat, [], []) $+    zip3 (patternNames pat) merge res of+    ([], _, _, _) ->+      -- Nothing is invariant.+      Skip+    (invariant, explpat', merge', res') -> Simplify $ do+      -- We have moved something invariant out of the loop.+      let loopbody' = loopbody {bodyResult = res'}+          invariantShape :: (a, VName) -> Bool+          invariantShape (_, shapemerge) =+            shapemerge+              `elem` map (paramName . fst) merge'+          (implpat', implinvariant) = partition invariantShape implpat+          implinvariant' = [(patElemIdent p, Var v) | (p, v) <- implinvariant]+          implpat'' = map fst implpat'+          explpat'' = map fst explpat'+          (ctx', val') = splitAt (length implpat') merge'+      forM_ (invariant ++ implinvariant') $ \(v1, v2) ->+        letBindNames [identName v1] $ BasicOp $ SubExp v2+      auxing aux $+        letBind (Pattern implpat'' explpat'') $+          DoLoop ctx' val' form loopbody'+  where+    merge = ctx ++ val+    res = bodyResult loopbody++    implpat =+      zip (patternContextElements pat) $+        map (paramName . fst) ctx+    explpat =+      zip (patternValueElements pat) $+        map (paramName . fst) val++    namesOfMergeParams = namesFromList $ map (paramName . fst) $ ctx ++ val++    removeFromResult (mergeParam, mergeInit) explpat' =+      case partition ((== paramName mergeParam) . snd) explpat' of+        ([(patelem, _)], rest) ->+          (Just (patElemIdent patelem, mergeInit), rest)+        (_, _) ->+          (Nothing, explpat')++    checkInvariance+      (pat_name, (mergeParam, mergeInit), resExp)+      (invariant, explpat', merge', resExps)+        | not (unique (paramDeclType mergeParam))+            || arrayRank (paramDeclType mergeParam) == 1,+          isInvariant,+          -- Also do not remove the condition in a while-loop.+          not $ paramName mergeParam `nameIn` freeIn form =+          let (bnd, explpat'') =+                removeFromResult (mergeParam, mergeInit) explpat'+           in ( maybe id (:) bnd $ (paramIdent mergeParam, mergeInit) : invariant,+                explpat'',+                merge',+                resExps+              )+        where+          -- A non-unique merge variable is invariant if one of the+          -- following is true:+          --+          -- (0) The result is a variable of the same name as the+          -- parameter, where all existential parameters are already+          -- known to be invariant+          isInvariant+            | Var v2 <- resExp,+              paramName mergeParam == v2 =+              allExistentialInvariant+                (namesFromList $ map (identName . fst) invariant)+                mergeParam+            -- (1) The result is identical to the initial parameter value.+            | mergeInit == resExp = True+            -- (2) The initial parameter value is equal to an outer+            -- loop parameter 'P', where the initial value of 'P' is+            -- equal to 'resExp', AND 'resExp' ultimately becomes the+            -- new value of 'P'.  XXX: it's a bit clumsy that this+            -- only works for one level of nesting, and I think it+            -- would not be too hard to generalise.+            | Var init_v <- mergeInit,+              Just (p_init, p_res) <- ST.lookupLoopParam init_v vtable,+              p_init == resExp,+              p_res == Var pat_name =+              True+            | otherwise = False+    checkInvariance+      (_pat_name, (mergeParam, mergeInit), resExp)+      (invariant, explpat', merge', resExps) =+        (invariant, explpat', (mergeParam, mergeInit) : merge', resExp : resExps)++    allExistentialInvariant namesOfInvariant mergeParam =+      all (invariantOrNotMergeParam namesOfInvariant) $+        namesToList $+          freeIn mergeParam `namesSubtract` oneName (paramName mergeParam)+    invariantOrNotMergeParam namesOfInvariant name =+      not (name `nameIn` namesOfMergeParams)+        || name `nameIn` namesOfInvariant++-- | A function that, given a subexpression, returns its type.+type TypeLookup = SubExp -> Maybe Type++-- | A simple rule is a top-down rule that can be expressed as a pure+-- function.+type SimpleRule lore = VarLookup lore -> TypeLookup -> BasicOp -> Maybe (BasicOp, Certificates)++simpleRules :: [SimpleRule lore]+simpleRules =+  [ simplifyBinOp,+    simplifyCmpOp,+    simplifyUnOp,+    simplifyConvOp,+    simplifyAssert,+    copyScratchToScratch,+    simplifyIdentityReshape,+    simplifyReshapeReshape,+    simplifyReshapeScratch,+    simplifyReshapeReplicate,+    simplifyReshapeIota,+    improveReshape+  ]++simplifyClosedFormLoop :: BinderOps lore => TopDownRuleDoLoop lore+simplifyClosedFormLoop _ pat _ ([], val, ForLoop i it bound [], body) =+  Simplify $ loopClosedForm pat val (oneName i) it bound body+simplifyClosedFormLoop _ _ _ _ = Skip++simplifyLoopVariables :: (BinderOps lore, Aliased lore) => TopDownRuleDoLoop lore+simplifyLoopVariables vtable pat aux (ctx, val, form@(ForLoop i it num_iters loop_vars), body)+  | simplifiable <- map checkIfSimplifiable loop_vars,+    not $ all isNothing simplifiable = Simplify $ do+    -- Check if the simplifications throw away more information than+    -- we are comfortable with at this stage.+    (maybe_loop_vars, body_prefix_stms) <-+      localScope (scopeOf form) $+        unzip <$> zipWithM onLoopVar loop_vars simplifiable+    if maybe_loop_vars == map Just loop_vars+      then cannotSimplify+      else do+        body' <- insertStmsM $ do+          addStms $ mconcat body_prefix_stms+          resultBodyM =<< bodyBind body+        auxing aux $+          letBind pat $+            DoLoop+              ctx+              val+              (ForLoop i it num_iters $ catMaybes maybe_loop_vars)+              body'+  where+    seType (Var v)+      | v == i = Just $ Prim $ IntType it+      | otherwise = ST.lookupType v vtable+    seType (Constant v) = Just $ Prim $ primValueType v+    consumed_in_body = consumedInBody body++    vtable' = ST.fromScope (scopeOf form) <> vtable++    checkIfSimplifiable (p, arr) =+      simplifyIndexing+        vtable'+        seType+        arr+        (DimFix (Var i) : fullSlice (paramType p) [])+        $ paramName p `nameIn` consumed_in_body++    -- We only want this simplification if the result does not refer+    -- to 'i' at all, or does not contain accesses.+    onLoopVar (p, arr) Nothing =+      return (Just (p, arr), mempty)+    onLoopVar (p, arr) (Just m) = do+      (x, x_stms) <- collectStms m+      case x of+        IndexResult cs arr' slice+          | not $ any ((i `nameIn`) . freeIn) x_stms,+            DimFix (Var j) : slice' <- slice,+            j == i,+            not $ i `nameIn` freeIn slice -> do+            addStms x_stms+            w <- arraySize 0 <$> lookupType arr'+            for_in_partial <-+              certifying cs $+                letExp "for_in_partial" $+                  BasicOp $+                    Index arr' $+                      DimSlice (intConst Int64 0) w (intConst Int64 1) : slice'+            return (Just (p, for_in_partial), mempty)+        SubExpResult cs se+          | all (notIndex . stmExp) x_stms -> do+            x_stms' <- collectStms_ $+              certifying cs $ do+                addStms x_stms+                letBindNames [paramName p] $ BasicOp $ SubExp se+            return (Nothing, x_stms')+        _ -> return (Just (p, arr), mempty)++    notIndex (BasicOp Index {}) = False+    notIndex _ = True+simplifyLoopVariables _ _ _ _ = Skip++-- If a for-loop with no loop variables has a counter of type Int64,+-- and the bound is just a constant or sign-extended integer of+-- smaller type, then change the loop to iterate over the smaller type+-- instead.  We then move the sign extension inside the loop instead.+-- This addresses loops of the form @for i in x..<y@ in the source+-- language.+narrowLoopType :: (BinderOps lore) => TopDownRuleDoLoop lore+narrowLoopType vtable pat aux (ctx, val, ForLoop i Int64 n [], body)+  | Just (n', it', cs) <- smallerType =+    Simplify $ do+      i' <- newVName $ baseString i+      let form' = ForLoop i' it' n' []+      body' <- insertStmsM $+        inScopeOf form' $ do+          letBindNames [i] $ BasicOp $ ConvOp (SExt it' Int64) (Var i')+          pure body+      auxing aux $+        certifying cs $+          letBind pat $ DoLoop ctx val form' body'+  where+    smallerType+      | Var n' <- n,+        Just (ConvOp (SExt it' _) n'', cs) <- ST.lookupBasicOp n' vtable =+        Just (n'', it', cs)+      | Constant (IntValue (Int64Value n')) <- n,+        toInteger n' <= toInteger (maxBound :: Int32) =+        Just (intConst Int32 (toInteger n'), Int32, mempty)+      | otherwise =+        Nothing+narrowLoopType _ _ _ _ = Skip++unroll ::+  BinderOps lore =>+  Integer ->+  [(FParam lore, SubExp)] ->+  (VName, IntType, Integer) ->+  [(LParam lore, VName)] ->+  Body lore ->+  RuleM lore [SubExp]+unroll n merge (iv, it, i) loop_vars body+  | i >= n =+    return $ map snd merge+  | otherwise = do+    iter_body <- insertStmsM $ do+      forM_ merge $ \(mergevar, mergeinit) ->+        letBindNames [paramName mergevar] $ BasicOp $ SubExp mergeinit++      letBindNames [iv] $ BasicOp $ SubExp $ intConst it i++      forM_ loop_vars $ \(p, arr) ->+        letBindNames [paramName p] $+          BasicOp $+            Index arr $+              DimFix (intConst Int64 i) : fullSlice (paramType p) []++      -- Some of the sizes in the types here might be temporarily wrong+      -- until copy propagation fixes it up.+      pure body++    iter_body' <- renameBody iter_body+    addStms $ bodyStms iter_body'++    let merge' = zip (map fst merge) $ bodyResult iter_body'+    unroll n merge' (iv, it, i + 1) loop_vars body++simplifyKnownIterationLoop :: BinderOps lore => TopDownRuleDoLoop lore+simplifyKnownIterationLoop _ pat aux (ctx, val, ForLoop i it (Constant iters) loop_vars, body)+  | IntValue n <- iters,+    zeroIshInt n || oneIshInt n || "unroll" `inAttrs` stmAuxAttrs aux = Simplify $ do+    res <- unroll (valueIntegral n) (ctx ++ val) (i, it, 0) loop_vars body+    forM_ (zip (patternNames pat) res) $ \(v, se) ->+      letBindNames [v] $ BasicOp $ SubExp se+simplifyKnownIterationLoop _ _ _ _ =+  Skip++-- | Turn @copy(x)@ into @x@ iff @x@ is not used after this copy+-- statement and it can be consumed.+--+-- This simplistic rule is only valid before we introduce memory.+removeUnnecessaryCopy :: BinderOps lore => BottomUpRuleBasicOp lore+removeUnnecessaryCopy (vtable, used) (Pattern [] [d]) _ (Copy v)+  | not (v `UT.isConsumed` used),+    (not (v `UT.used` used) && consumable) || not (patElemName d `UT.isConsumed` used) =+    Simplify $ letBindNames [patElemName d] $ BasicOp $ SubExp $ Var v+  where+    -- We need to make sure we can even consume the original.+    -- This is currently a hacky check, much too conservative,+    -- because we don't have the information conveniently+    -- available.+    consumable = case M.lookup v $ ST.toScope vtable of+      Just (FParamName info) -> unique $ declTypeOf info+      _ -> False+removeUnnecessaryCopy _ _ _ _ = Skip++simplifyCmpOp :: SimpleRule lore+simplifyCmpOp _ _ (CmpOp cmp e1 e2)+  | e1 == e2 = constRes $+    BoolValue $+      case cmp of+        CmpEq {} -> True+        CmpSlt {} -> False+        CmpUlt {} -> False+        CmpSle {} -> True+        CmpUle {} -> True+        FCmpLt {} -> False+        FCmpLe {} -> True+        CmpLlt -> False+        CmpLle -> True+simplifyCmpOp _ _ (CmpOp cmp (Constant v1) (Constant v2)) =+  constRes . BoolValue =<< doCmpOp cmp v1 v2+simplifyCmpOp look _ (CmpOp CmpEq {} (Constant (IntValue x)) (Var v))+  | Just (BasicOp (ConvOp BToI {} b), cs) <- look v =+    case valueIntegral x :: Int of+      1 -> Just (SubExp b, cs)+      0 -> Just (UnOp Not b, cs)+      _ -> Just (SubExp (Constant (BoolValue False)), cs)+simplifyCmpOp _ _ _ = Nothing++simplifyBinOp :: SimpleRule lore+simplifyBinOp _ _ (BinOp op (Constant v1) (Constant v2))+  | Just res <- doBinOp op v1 v2 =+    constRes res+simplifyBinOp look _ (BinOp Add {} e1 e2)+  | isCt0 e1 = subExpRes e2+  | isCt0 e2 = subExpRes e1+  -- x+(y-x) => y+  | Var v2 <- e2,+    Just (BasicOp (BinOp Sub {} e2_a e2_b), cs) <- look v2,+    e2_b == e1 =+    Just (SubExp e2_a, cs)+simplifyBinOp _ _ (BinOp FAdd {} e1 e2)+  | isCt0 e1 = subExpRes e2+  | isCt0 e2 = subExpRes e1+simplifyBinOp look _ (BinOp Sub {} e1 e2)+  | isCt0 e2 = subExpRes e1+  -- Cases for simplifying (a+b)-b and permutations.+  | Var v1 <- e1,+    Just (BasicOp (BinOp Add {} e1_a e1_b), cs) <- look v1,+    e1_a == e2 =+    Just (SubExp e1_b, cs)+  | Var v1 <- e1,+    Just (BasicOp (BinOp Add {} e1_a e1_b), cs) <- look v1,+    e1_b == e2 =+    Just (SubExp e1_a, cs)+  | Var v2 <- e2,+    Just (BasicOp (BinOp Add {} e2_a e2_b), cs) <- look v2,+    e2_a == e1 =+    Just (SubExp e2_b, cs)+  | Var v2 <- e1,+    Just (BasicOp (BinOp Add {} e2_a e2_b), cs) <- look v2,+    e2_b == e1 =+    Just (SubExp e2_a, cs)+simplifyBinOp _ _ (BinOp FSub {} e1 e2)+  | isCt0 e2 = subExpRes e1+simplifyBinOp _ _ (BinOp Mul {} e1 e2)+  | isCt0 e1 = subExpRes e1+  | isCt0 e2 = subExpRes e2+  | isCt1 e1 = subExpRes e2+  | isCt1 e2 = subExpRes e1+simplifyBinOp _ _ (BinOp FMul {} e1 e2)+  | isCt0 e1 = subExpRes e1+  | isCt0 e2 = subExpRes e2+  | isCt1 e1 = subExpRes e2+  | isCt1 e2 = subExpRes e1+simplifyBinOp look _ (BinOp (SMod t _) e1 e2)+  | isCt1 e2 = constRes $ IntValue $ intValue t (0 :: Int)+  | e1 == e2 = constRes $ IntValue $ intValue t (0 :: Int)+  | Var v1 <- e1,+    Just (BasicOp (BinOp SMod {} _ e4), v1_cs) <- look v1,+    e4 == e2 =+    Just (SubExp e1, v1_cs)+simplifyBinOp _ _ (BinOp SDiv {} e1 e2)+  | isCt0 e1 = subExpRes e1+  | isCt1 e2 = subExpRes e1+  | isCt0 e2 = Nothing+simplifyBinOp _ _ (BinOp SDivUp {} e1 e2)+  | isCt0 e1 = subExpRes e1+  | isCt1 e2 = subExpRes e1+  | isCt0 e2 = Nothing+simplifyBinOp _ _ (BinOp FDiv {} e1 e2)+  | isCt0 e1 = subExpRes e1+  | isCt1 e2 = subExpRes e1+  | isCt0 e2 = Nothing+simplifyBinOp _ _ (BinOp (SRem t _) e1 e2)+  | isCt1 e2 = constRes $ IntValue $ intValue t (0 :: Int)+  | e1 == e2 = constRes $ IntValue $ intValue t (1 :: Int)+simplifyBinOp _ _ (BinOp SQuot {} e1 e2)+  | isCt1 e2 = subExpRes e1+  | isCt0 e2 = Nothing+simplifyBinOp _ _ (BinOp (FPow t) e1 e2)+  | isCt0 e2 = subExpRes $ floatConst t 1+  | isCt0 e1 || isCt1 e1 || isCt1 e2 = subExpRes e1+simplifyBinOp _ _ (BinOp (Shl t) e1 e2)+  | isCt0 e2 = subExpRes e1+  | isCt0 e1 = subExpRes $ intConst t 0+simplifyBinOp _ _ (BinOp AShr {} e1 e2)+  | isCt0 e2 = subExpRes e1+simplifyBinOp _ _ (BinOp (And t) e1 e2)+  | isCt0 e1 = subExpRes $ intConst t 0+  | isCt0 e2 = subExpRes $ intConst t 0+  | e1 == e2 = subExpRes e1+simplifyBinOp _ _ (BinOp Or {} e1 e2)+  | isCt0 e1 = subExpRes e2+  | isCt0 e2 = subExpRes e1+  | e1 == e2 = subExpRes e1+simplifyBinOp _ _ (BinOp (Xor t) e1 e2)+  | isCt0 e1 = subExpRes e2+  | isCt0 e2 = subExpRes e1+  | e1 == e2 = subExpRes $ intConst t 0+simplifyBinOp defOf _ (BinOp LogAnd e1 e2)+  | isCt0 e1 = constRes $ BoolValue False+  | isCt0 e2 = constRes $ BoolValue False+  | isCt1 e1 = subExpRes e2+  | isCt1 e2 = subExpRes e1+  | Var v <- e1,+    Just (BasicOp (UnOp Not e1'), v_cs) <- defOf v,+    e1' == e2 =+    Just (SubExp $ Constant $ BoolValue False, v_cs)+  | Var v <- e2,+    Just (BasicOp (UnOp Not e2'), v_cs) <- defOf v,+    e2' == e1 =+    Just (SubExp $ Constant $ BoolValue False, v_cs)+simplifyBinOp defOf _ (BinOp LogOr e1 e2)+  | isCt0 e1 = subExpRes e2+  | isCt0 e2 = subExpRes e1+  | isCt1 e1 = constRes $ BoolValue True+  | isCt1 e2 = constRes $ BoolValue True+  | Var v <- e1,+    Just (BasicOp (UnOp Not e1'), v_cs) <- defOf v,+    e1' == e2 =+    Just (SubExp $ Constant $ BoolValue True, v_cs)+  | Var v <- e2,+    Just (BasicOp (UnOp Not e2'), v_cs) <- defOf v,+    e2' == e1 =+    Just (SubExp $ Constant $ BoolValue True, v_cs)+simplifyBinOp defOf _ (BinOp (SMax it) e1 e2)+  | e1 == e2 =+    subExpRes e1+  | Var v1 <- e1,+    Just (BasicOp (BinOp (SMax _) e1_1 e1_2), v1_cs) <- defOf v1,+    e1_1 == e2 =+    Just (BinOp (SMax it) e1_2 e2, v1_cs)+  | Var v1 <- e1,+    Just (BasicOp (BinOp (SMax _) e1_1 e1_2), v1_cs) <- defOf v1,+    e1_2 == e2 =+    Just (BinOp (SMax it) e1_1 e2, v1_cs)+  | Var v2 <- e2,+    Just (BasicOp (BinOp (SMax _) e2_1 e2_2), v2_cs) <- defOf v2,+    e2_1 == e1 =+    Just (BinOp (SMax it) e2_2 e1, v2_cs)+  | Var v2 <- e2,+    Just (BasicOp (BinOp (SMax _) e2_1 e2_2), v2_cs) <- defOf v2,+    e2_2 == e1 =+    Just (BinOp (SMax it) e2_1 e1, v2_cs)+simplifyBinOp _ _ _ = Nothing++constRes :: PrimValue -> Maybe (BasicOp, Certificates)+constRes = Just . (,mempty) . SubExp . Constant++subExpRes :: SubExp -> Maybe (BasicOp, Certificates)+subExpRes = Just . (,mempty) . SubExp++simplifyUnOp :: SimpleRule lore+simplifyUnOp _ _ (UnOp op (Constant v)) =+  constRes =<< doUnOp op v+simplifyUnOp defOf _ (UnOp Not (Var v))+  | Just (BasicOp (UnOp Not v2), v_cs) <- defOf v =+    Just (SubExp v2, v_cs)+simplifyUnOp _ _ _ =+  Nothing++simplifyConvOp :: SimpleRule lore+simplifyConvOp _ _ (ConvOp op (Constant v)) =+  constRes =<< doConvOp op v+simplifyConvOp _ _ (ConvOp op se)+  | (from, to) <- convOpType op,+    from == to =+    subExpRes se+simplifyConvOp lookupVar _ (ConvOp (SExt t2 t1) (Var v))+  | Just (BasicOp (ConvOp (SExt t3 _) se), v_cs) <- lookupVar v,+    t2 >= t3 =+    Just (ConvOp (SExt t3 t1) se, v_cs)+simplifyConvOp lookupVar _ (ConvOp (ZExt t2 t1) (Var v))+  | Just (BasicOp (ConvOp (ZExt t3 _) se), v_cs) <- lookupVar v,+    t2 >= t3 =+    Just (ConvOp (ZExt t3 t1) se, v_cs)+simplifyConvOp lookupVar _ (ConvOp (SIToFP t2 t1) (Var v))+  | Just (BasicOp (ConvOp (SExt t3 _) se), v_cs) <- lookupVar v,+    t2 >= t3 =+    Just (ConvOp (SIToFP t3 t1) se, v_cs)+simplifyConvOp lookupVar _ (ConvOp (UIToFP t2 t1) (Var v))+  | Just (BasicOp (ConvOp (ZExt t3 _) se), v_cs) <- lookupVar v,+    t2 >= t3 =+    Just (ConvOp (UIToFP t3 t1) se, v_cs)+simplifyConvOp lookupVar _ (ConvOp (FPConv t2 t1) (Var v))+  | Just (BasicOp (ConvOp (FPConv t3 _) se), v_cs) <- lookupVar v,+    t2 >= t3 =+    Just (ConvOp (FPConv t3 t1) se, v_cs)+simplifyConvOp _ _ _ =+  Nothing++-- If expression is true then just replace assertion.+simplifyAssert :: SimpleRule lore+simplifyAssert _ _ (Assert (Constant (BoolValue True)) _ _) =+  constRes Checked+simplifyAssert _ _ _ =+  Nothing++constantFoldPrimFun :: BinderOps lore => TopDownRuleGeneric lore+constantFoldPrimFun _ (Let pat (StmAux cs attrs _) (Apply fname args _ _))+  | Just args' <- mapM (isConst . fst) args,+    Just (_, _, fun) <- M.lookup (nameToString fname) primFuns,+    Just result <- fun args' =+    Simplify $+      certifying cs $+        attributing attrs $+          letBind pat $ BasicOp $ SubExp $ Constant result+  where+    isConst (Constant v) = Just v+    isConst _ = Nothing+constantFoldPrimFun _ _ = Skip++simplifyIndex :: BinderOps lore => BottomUpRuleBasicOp lore+simplifyIndex (vtable, used) pat@(Pattern [] [pe]) (StmAux cs attrs _) (Index idd inds)+  | Just m <- simplifyIndexing vtable seType idd inds consumed = Simplify $ do+    res <- m+    attributing attrs $ case res of+      SubExpResult cs' se ->+        certifying (cs <> cs') $+          letBindNames (patternNames pat) $ BasicOp $ SubExp se+      IndexResult extra_cs idd' inds' ->+        certifying (cs <> extra_cs) $+          letBindNames (patternNames pat) $ BasicOp $ Index idd' inds'+  where+    consumed = patElemName pe `UT.isConsumed` used+    seType (Var v) = ST.lookupType v vtable+    seType (Constant v) = Just $ Prim $ primValueType v+simplifyIndex _ _ _ _ = Skip++data IndexResult+  = IndexResult Certificates VName (Slice SubExp)+  | SubExpResult Certificates SubExp++simplifyIndexing ::+  MonadBinder m =>+  ST.SymbolTable (Lore m) ->+  TypeLookup ->+  VName ->+  Slice SubExp ->+  Bool ->+  Maybe (m IndexResult)+simplifyIndexing vtable seType idd inds consuming =+  case defOf idd of+    _+      | Just t <- seType (Var idd),+        inds == fullSlice t [] ->+        Just $ pure $ SubExpResult mempty $ Var idd+      | Just inds' <- sliceIndices inds,+        Just (ST.Indexed cs e) <- ST.index idd inds' vtable,+        worthInlining e,+        all (`ST.elem` vtable) (unCertificates cs) ->+        Just $ SubExpResult cs <$> toSubExp "index_primexp" e+      | Just inds' <- sliceIndices inds,+        Just (ST.IndexedArray cs arr inds'') <- ST.index idd inds' vtable,+        all (worthInlining . untyped) inds'',+        all (`ST.elem` vtable) (unCertificates cs) ->+        Just $+          IndexResult cs arr . map DimFix+            <$> mapM (toSubExp "index_primexp") inds''+    Nothing -> Nothing+    Just (SubExp (Var v), cs) -> Just $ pure $ IndexResult cs v inds+    Just (Iota _ x s to_it, cs)+      | [DimFix ii] <- inds,+        Just (Prim (IntType from_it)) <- seType ii ->+        Just $+          let mul = BinOpExp $ Mul to_it OverflowWrap+              add = BinOpExp $ Add to_it OverflowWrap+           in fmap (SubExpResult cs) $+                toSubExp "index_iota" $+                  ( sExt to_it (primExpFromSubExp (IntType from_it) ii)+                      `mul` primExpFromSubExp (IntType to_it) s+                  )+                    `add` primExpFromSubExp (IntType to_it) x+      | [DimSlice i_offset i_n i_stride] <- inds ->+        Just $ do+          i_offset' <- asIntS to_it i_offset+          i_stride' <- asIntS to_it i_stride+          let mul = BinOpExp $ Mul to_it OverflowWrap+              add = BinOpExp $ Add to_it OverflowWrap+          i_offset'' <-+            toSubExp "iota_offset" $+              ( primExpFromSubExp (IntType to_it) x+                  `mul` primExpFromSubExp (IntType to_it) s+              )+                `add` primExpFromSubExp (IntType to_it) i_offset'+          i_stride'' <-+            letSubExp "iota_offset" $+              BasicOp $ BinOp (Mul Int64 OverflowWrap) s i_stride'+          fmap (SubExpResult cs) $+            letSubExp "slice_iota" $+              BasicOp $ Iota i_n i_offset'' i_stride'' to_it++    -- A rotate cannot be simplified away if we are slicing a rotated dimension.+    Just (Rotate offsets a, cs)+      | not $ or $ zipWith rotateAndSlice offsets inds -> Just $ do+        dims <- arrayDims <$> lookupType a+        let adjustI i o d = do+              i_p_o <- letSubExp "i_p_o" $ BasicOp $ BinOp (Add Int64 OverflowWrap) i o+              letSubExp "rot_i" (BasicOp $ BinOp (SMod Int64 Unsafe) i_p_o d)+            adjust (DimFix i, o, d) =+              DimFix <$> adjustI i o d+            adjust (DimSlice i n s, o, d) =+              DimSlice <$> adjustI i o d <*> pure n <*> pure s+        IndexResult cs a <$> mapM adjust (zip3 inds offsets dims)+      where+        rotateAndSlice r DimSlice {} = not $ isCt0 r+        rotateAndSlice _ _ = False+    Just (Index aa ais, cs) ->+      Just $+        IndexResult cs aa+          <$> subExpSlice (sliceSlice (primExpSlice ais) (primExpSlice inds))+    Just (Replicate (Shape [_]) (Var vv), cs)+      | [DimFix {}] <- inds, not consuming -> Just $ pure $ SubExpResult cs $ Var vv+      | DimFix {} : is' <- inds, not consuming -> Just $ pure $ IndexResult cs vv is'+    Just (Replicate (Shape [_]) val@(Constant _), cs)+      | [DimFix {}] <- inds, not consuming -> Just $ pure $ SubExpResult cs val+    Just (Replicate (Shape ds) v, cs)+      | (ds_inds, rest_inds) <- splitAt (length ds) inds,+        (ds', ds_inds') <- unzip $ mapMaybe index ds_inds,+        ds' /= ds ->+        Just $ do+          arr <- letExp "smaller_replicate" $ BasicOp $ Replicate (Shape ds') v+          return $ IndexResult cs arr $ ds_inds' ++ rest_inds+      where+        index DimFix {} = Nothing+        index (DimSlice _ n s) = Just (n, DimSlice (constant (0 :: Int64)) n s)+    Just (Rearrange perm src, cs)+      | rearrangeReach perm <= length (takeWhile isIndex inds) ->+        let inds' = rearrangeShape (rearrangeInverse perm) inds+         in Just $ pure $ IndexResult cs src inds'+      where+        isIndex DimFix {} = True+        isIndex _ = False+    Just (Copy src, cs)+      | Just dims <- arrayDims <$> seType (Var src),+        length inds == length dims,+        not consuming,+        ST.available src vtable ->+        Just $ pure $ IndexResult cs src inds+    Just (Reshape newshape src, cs)+      | Just newdims <- shapeCoercion newshape,+        Just olddims <- arrayDims <$> seType (Var src),+        changed_dims <- zipWith (/=) newdims olddims,+        not $ or $ drop (length inds) changed_dims ->+        Just $ pure $ IndexResult cs src inds+      | Just newdims <- shapeCoercion newshape,+        Just olddims <- arrayDims <$> seType (Var src),+        length newshape == length inds,+        length olddims == length newdims ->+        Just $ pure $ IndexResult cs src inds+    Just (Reshape [_] v2, cs)+      | Just [_] <- arrayDims <$> seType (Var v2) ->+        Just $ pure $ IndexResult cs v2 inds+    Just (Concat d x xs _, cs)+      | -- HACK: simplifying the indexing of an N-array concatenation+        -- is going to produce an N-deep if expression, which is bad+        -- when N is large.  To try to avoid that, we use the+        -- heuristic not to simplify as long as any of the operands+        -- are themselves Concats.  The hops it that this will give+        -- simplification some time to cut down the concatenation to+        -- something smaller, before we start inlining.+        not $ any isConcat $ x : xs,+        Just (ibef, DimFix i, iaft) <- focusNth d inds,+        Just (Prim res_t) <-+          (`setArrayDims` sliceDims inds)+            <$> ST.lookupType x vtable -> Just $ do+        x_len <- arraySize d <$> lookupType x+        xs_lens <- mapM (fmap (arraySize d) . lookupType) xs++        let add n m = do+              added <- letSubExp "index_concat_add" $ BasicOp $ BinOp (Add Int64 OverflowWrap) n m+              return (added, n)+        (_, starts) <- mapAccumLM add x_len xs_lens+        let xs_and_starts = reverse $ zip xs starts++        let mkBranch [] =+              letSubExp "index_concat" $ BasicOp $ Index x $ ibef ++ DimFix i : iaft+            mkBranch ((x', start) : xs_and_starts') = do+              cmp <- letSubExp "index_concat_cmp" $ BasicOp $ CmpOp (CmpSle Int64) start i+              (thisres, thisbnds) <- collectStms $ do+                i' <- letSubExp "index_concat_i" $ BasicOp $ BinOp (Sub Int64 OverflowWrap) i start+                letSubExp "index_concat" $ BasicOp $ Index x' $ ibef ++ DimFix i' : iaft+              thisbody <- mkBodyM thisbnds [thisres]+              (altres, altbnds) <- collectStms $ mkBranch xs_and_starts'+              altbody <- mkBodyM altbnds [altres]+              letSubExp "index_concat_branch" $+                If cmp thisbody altbody $+                  IfDec [primBodyType res_t] IfNormal+        SubExpResult cs <$> mkBranch xs_and_starts+    Just (ArrayLit ses _, cs)+      | DimFix (Constant (IntValue (Int64Value i))) : inds' <- inds,+        Just se <- maybeNth i ses ->+        case inds' of+          [] -> Just $ pure $ SubExpResult cs se+          _ | Var v2 <- se -> Just $ pure $ IndexResult cs v2 inds'+          _ -> Nothing+    -- Indexing single-element arrays.  We know the index must be 0.+    _+      | Just t <- seType $ Var idd,+        isCt1 $ arraySize 0 t,+        DimFix i : inds' <- inds,+        not $ isCt0 i ->+        Just $+          pure $+            IndexResult mempty idd $+              DimFix (constant (0 :: Int64)) : inds'+    _ -> Nothing+  where+    defOf v = do+      (BasicOp op, def_cs) <- ST.lookupExp v vtable+      return (op, def_cs)+    worthInlining e+      | primExpSizeAtLeast 20 e = False -- totally ad-hoc.+      | otherwise = worthInlining' e+    worthInlining' (BinOpExp Pow {} _ _) = False+    worthInlining' (BinOpExp FPow {} _ _) = False+    worthInlining' (BinOpExp _ x y) = worthInlining' x && worthInlining' y+    worthInlining' (CmpOpExp _ x y) = worthInlining' x && worthInlining' y+    worthInlining' (ConvOpExp _ x) = worthInlining' x+    worthInlining' (UnOpExp _ x) = worthInlining' x+    worthInlining' FunExp {} = False+    worthInlining' _ = True++    isConcat v+      | Just (Concat {}, _) <- defOf v =+        True+      | otherwise =+        False++data ConcatArg+  = ArgArrayLit [SubExp]+  | ArgReplicate [SubExp] SubExp+  | ArgVar VName++toConcatArg :: ST.SymbolTable lore -> VName -> (ConcatArg, Certificates)+toConcatArg vtable v =+  case ST.lookupBasicOp v vtable of+    Just (ArrayLit ses _, cs) ->+      (ArgArrayLit ses, cs)+    Just (Replicate shape se, cs) ->+      (ArgReplicate [shapeSize 0 shape] se, cs)+    _ ->+      (ArgVar v, mempty)++fromConcatArg ::+  MonadBinder m =>+  Type ->+  (ConcatArg, Certificates) ->+  m VName+fromConcatArg t (ArgArrayLit ses, cs) =+  certifying cs $ letExp "concat_lit" $ BasicOp $ ArrayLit ses $ rowType t+fromConcatArg elem_type (ArgReplicate ws se, cs) = do+  let elem_shape = arrayShape elem_type+  certifying cs $ do+    w <- letSubExp "concat_rep_w" =<< toExp (sum $ map pe64 ws)+    letExp "concat_rep" $ BasicOp $ Replicate (setDim 0 elem_shape w) se+fromConcatArg _ (ArgVar v, _) =+  pure v++fuseConcatArg ::+  [(ConcatArg, Certificates)] ->+  (ConcatArg, Certificates) ->+  [(ConcatArg, Certificates)]+fuseConcatArg xs (ArgArrayLit [], _) =+  xs+fuseConcatArg xs (ArgReplicate [w] se, cs)+  | isCt0 w =+    xs+  | isCt1 w =+    fuseConcatArg xs (ArgArrayLit [se], cs)+fuseConcatArg ((ArgArrayLit x_ses, x_cs) : xs) (ArgArrayLit y_ses, y_cs) =+  (ArgArrayLit (x_ses ++ y_ses), x_cs <> y_cs) : xs+fuseConcatArg ((ArgReplicate x_ws x_se, x_cs) : xs) (ArgReplicate y_ws y_se, y_cs)+  | x_se == y_se =+    (ArgReplicate (x_ws ++ y_ws) x_se, x_cs <> y_cs) : xs+fuseConcatArg xs y =+  y : xs++simplifyConcat :: BinderOps lore => BottomUpRuleBasicOp lore+-- concat@1(transpose(x),transpose(y)) == transpose(concat@0(x,y))+simplifyConcat (vtable, _) pat _ (Concat i x xs new_d)+  | Just r <- arrayRank <$> ST.lookupType x vtable,+    let perm = [i] ++ [0 .. i -1] ++ [i + 1 .. r -1],+    Just (x', x_cs) <- transposedBy perm x,+    Just (xs', xs_cs) <- unzip <$> mapM (transposedBy perm) xs = Simplify $ do+    concat_rearrange <-+      certifying (x_cs <> mconcat xs_cs) $+        letExp "concat_rearrange" $ BasicOp $ Concat 0 x' xs' new_d+    letBind pat $ BasicOp $ Rearrange perm concat_rearrange+  where+    transposedBy perm1 v =+      case ST.lookupExp v vtable of+        Just (BasicOp (Rearrange perm2 v'), vcs)+          | perm1 == perm2 -> Just (v', vcs)+        _ -> Nothing++-- Removing a concatenation that involves only a single array.  This+-- may be produced as a result of other simplification rules.+simplifyConcat _ pat aux (Concat _ x [] _) =+  Simplify $+    -- Still need a copy because Concat produces a fresh array.+    auxing aux $ letBind pat $ BasicOp $ Copy x+-- concat xs (concat ys zs) == concat xs ys zs+simplifyConcat (vtable, _) pat (StmAux cs attrs _) (Concat i x xs new_d)+  | x' /= x || concat xs' /= xs =+    Simplify $+      certifying (cs <> x_cs <> mconcat xs_cs) $+        attributing attrs $+          letBind pat $+            BasicOp $ Concat i x' (zs ++ concat xs') new_d+  where+    (x' : zs, x_cs) = isConcat x+    (xs', xs_cs) = unzip $ map isConcat xs+    isConcat v = case ST.lookupBasicOp v vtable of+      Just (Concat j y ys _, v_cs) | j == i -> (y : ys, v_cs)+      _ -> ([v], mempty)++-- Fusing arguments to the concat when possible.  Only done when+-- concatenating along the outer dimension for now.+simplifyConcat (vtable, _) pat aux (Concat 0 x xs outer_w)+  | -- We produce the to-be-concatenated arrays in reverse order, so+    -- reverse them back.+    y : ys <-+      reverse $+        foldl' fuseConcatArg mempty $+          map (toConcatArg vtable) $ x : xs,+    length xs /= length ys =+    Simplify $ do+      elem_type <- lookupType x+      y' <- fromConcatArg elem_type y+      ys' <- mapM (fromConcatArg elem_type) ys+      auxing aux $ letBind pat $ BasicOp $ Concat 0 y' ys' outer_w+simplifyConcat _ _ _ _ = Skip++ruleIf :: BinderOps lore => TopDownRuleIf lore+ruleIf _ pat _ (e1, tb, fb, IfDec _ ifsort)+  | Just branch <- checkBranch,+    ifsort /= IfFallback || isCt1 e1 = Simplify $ do+    let ses = bodyResult branch+    addStms $ bodyStms branch+    sequence_+      [ letBindNames [patElemName p] $ BasicOp $ SubExp se+        | (p, se) <- zip (patternElements pat) ses+      ]+  where+    checkBranch+      | isCt1 e1 = Just tb+      | isCt0 e1 = Just fb+      | otherwise = Nothing++-- IMPROVE: the following two rules can be generalised to work in more+-- cases, especially when the branches have bindings, or return more+-- than one value.+--+-- if c then True else v == c || v+ruleIf+  _+  pat+  _+  ( cond,+    Body _ tstms [Constant (BoolValue True)],+    Body _ fstms [se],+    IfDec ts _+    )+    | null tstms,+      null fstms,+      [Prim Bool] <- map extTypeOf ts =+      Simplify $ letBind pat $ BasicOp $ BinOp LogOr cond se+-- When type(x)==bool, if c then x else y == (c && x) || (!c && y)+ruleIf _ pat _ (cond, tb, fb, IfDec ts _)+  | Body _ tstms [tres] <- tb,+    Body _ fstms [fres] <- fb,+    all (safeExp . stmExp) $ tstms <> fstms,+    all ((== Prim Bool) . extTypeOf) ts = Simplify $ do+    addStms tstms+    addStms fstms+    e <-+      eBinOp+        LogOr+        (pure $ BasicOp $ BinOp LogAnd cond tres)+        ( eBinOp+            LogAnd+            (pure $ BasicOp $ UnOp Not cond)+            (pure $ BasicOp $ SubExp fres)+        )+    letBind pat e+ruleIf _ pat _ (_, tbranch, _, IfDec _ IfFallback)+  | null $ patternContextNames pat,+    all (safeExp . stmExp) $ bodyStms tbranch = Simplify $ do+    let ses = bodyResult tbranch+    addStms $ bodyStms tbranch+    sequence_+      [ letBindNames [patElemName p] $ BasicOp $ SubExp se+        | (p, se) <- zip (patternElements pat) ses+      ]+ruleIf _ pat _ (cond, tb, fb, _)+  | Body _ _ [Constant (IntValue t)] <- tb,+    Body _ _ [Constant (IntValue f)] <- fb =+    if oneIshInt t && zeroIshInt f+      then+        Simplify $+          letBind pat $ BasicOp $ ConvOp (BToI (intValueType t)) cond+      else+        if zeroIshInt t && oneIshInt f+          then Simplify $ do+            cond_neg <- letSubExp "cond_neg" $ BasicOp $ UnOp Not cond+            letBind pat $ BasicOp $ ConvOp (BToI (intValueType t)) cond_neg+          else Skip+ruleIf _ _ _ _ = Skip++-- | Move out results of a conditional expression whose computation is+-- either invariant to the branches (only done for results in the+-- context), or the same in both branches.+hoistBranchInvariant :: BinderOps lore => TopDownRuleIf lore+hoistBranchInvariant _ pat _ (cond, tb, fb, IfDec ret ifsort) = Simplify $ do+  let tses = bodyResult tb+      fses = bodyResult fb+  (hoistings, (pes, ts, res)) <-+    fmap (fmap unzip3 . partitionEithers) $+      mapM branchInvariant $+        zip3+          (patternElements pat)+          (map Left [0 .. num_ctx -1] ++ map Right ret)+          (zip tses fses)+  let ctx_fixes = catMaybes hoistings+      (tses', fses') = unzip res+      tb' = tb {bodyResult = tses'}+      fb' = fb {bodyResult = fses'}+      ret' = foldr (uncurry fixExt) (rights ts) ctx_fixes+      (ctx_pes, val_pes) = splitFromEnd (length ret') pes+  if not $ null hoistings -- Was something hoisted?+    then do+      -- We may have to add some reshapes if we made the type+      -- less existential.+      tb'' <- reshapeBodyResults tb' $ map extTypeOf ret'+      fb'' <- reshapeBodyResults fb' $ map extTypeOf ret'+      letBind (Pattern ctx_pes val_pes) $+        If cond tb'' fb'' (IfDec ret' ifsort)+    else cannotSimplify+  where+    num_ctx = length $ patternContextElements pat+    bound_in_branches =+      namesFromList $+        concatMap (patternNames . stmPattern) $+          bodyStms tb <> bodyStms fb+    mem_sizes = freeIn $ filter (isMem . patElemType) $ patternElements pat+    invariant Constant {} = True+    invariant (Var v) = not $ v `nameIn` bound_in_branches++    isMem Mem {} = True+    isMem _ = False+    sizeOfMem v = v `nameIn` mem_sizes++    branchInvariant (pe, t, (tse, fse))+      -- Do both branches return the same value?+      | tse == fse = do+        letBindNames [patElemName pe] $ BasicOp $ SubExp tse+        hoisted pe t++      -- Do both branches return values that are free in the+      -- branch, and are we not the only pattern element?  The+      -- latter is to avoid infinite application of this rule.+      | invariant tse,+        invariant fse,+        patternSize pat > 1,+        Prim _ <- patElemType pe,+        not $ sizeOfMem $ patElemName pe = do+        bt <- expTypesFromPattern $ Pattern [] [pe]+        letBindNames [patElemName pe]+          =<< ( If cond <$> resultBodyM [tse]+                  <*> resultBodyM [fse]+                  <*> pure (IfDec bt ifsort)+              )+        hoisted pe t+      | otherwise =+        return $ Right (pe, t, (tse, fse))++    hoisted pe (Left i) = return $ Left $ Just (i, Var $ patElemName pe)+    hoisted _ Right {} = return $ Left Nothing++    reshapeBodyResults body rets = insertStmsM $ do+      ses <- bodyBind body+      let (ctx_ses, val_ses) = splitFromEnd (length rets) ses+      resultBodyM . (ctx_ses ++) =<< zipWithM reshapeResult val_ses rets+    reshapeResult (Var v) t@Array {} = do+      v_t <- lookupType v+      let newshape = arrayDims $ removeExistentials t v_t+      if newshape /= arrayDims v_t+        then letSubExp "branch_ctx_reshaped" $ shapeCoerce newshape v+        else return $ Var v+    reshapeResult se _ =+      return se++simplifyIdentityReshape :: SimpleRule lore+simplifyIdentityReshape _ seType (Reshape newshape v)+  | Just t <- seType $ Var v,+    newDims newshape == arrayDims t -- No-op reshape.+    =+    subExpRes $ Var v+simplifyIdentityReshape _ _ _ = Nothing++simplifyReshapeReshape :: SimpleRule lore+simplifyReshapeReshape defOf _ (Reshape newshape v)+  | Just (BasicOp (Reshape oldshape v2), v_cs) <- defOf v =+    Just (Reshape (fuseReshape oldshape newshape) v2, v_cs)+simplifyReshapeReshape _ _ _ = Nothing++simplifyReshapeScratch :: SimpleRule lore+simplifyReshapeScratch defOf _ (Reshape newshape v)+  | Just (BasicOp (Scratch bt _), v_cs) <- defOf v =+    Just (Scratch bt $ newDims newshape, v_cs)+simplifyReshapeScratch _ _ _ = Nothing++simplifyReshapeReplicate :: SimpleRule lore+simplifyReshapeReplicate defOf seType (Reshape newshape v)+  | Just (BasicOp (Replicate _ se), v_cs) <- defOf v,+    Just oldshape <- arrayShape <$> seType se,+    shapeDims oldshape `isSuffixOf` newDims newshape =+    let new =+          take (length newshape - shapeRank oldshape) $+            newDims newshape+     in Just (Replicate (Shape new) se, v_cs)+simplifyReshapeReplicate _ _ _ = Nothing++simplifyReshapeIota :: SimpleRule lore+simplifyReshapeIota defOf _ (Reshape newshape v)+  | Just (BasicOp (Iota _ offset stride it), v_cs) <- defOf v,+    [n] <- newDims newshape =+    Just (Iota n offset stride it, v_cs)+simplifyReshapeIota _ _ _ = Nothing++improveReshape :: SimpleRule lore+improveReshape _ seType (Reshape newshape v)+  | Just t <- seType $ Var v,+    newshape' <- informReshape (arrayDims t) newshape,+    newshape' /= newshape =+    Just (Reshape newshape' v, mempty)+improveReshape _ _ _ = Nothing++-- | If we are copying a scratch array (possibly indirectly), just turn it into a scratch by+-- itself.+copyScratchToScratch :: SimpleRule lore+copyScratchToScratch defOf seType (Copy src) = do+  t <- seType $ Var src+  if isActuallyScratch src+    then Just (Scratch (elemType t) (arrayDims t), mempty)+    else Nothing+  where+    isActuallyScratch v =+      case asBasicOp . fst =<< defOf v of+        Just Scratch {} -> True+        Just (Rearrange _ v') -> isActuallyScratch v'+        Just (Reshape _ v') -> isActuallyScratch v'+        _ -> False+copyScratchToScratch _ _ _ =+  Nothing++ruleBasicOp :: BinderOps lore => TopDownRuleBasicOp lore+-- Check all the simpleRules.+ruleBasicOp vtable pat aux op+  | Just (op', cs) <- msum [rule defOf seType op | rule <- simpleRules] =+    Simplify $ certifying (cs <> stmAuxCerts aux) $ letBind pat $ BasicOp op'+  where+    defOf = (`ST.lookupExp` vtable)+    seType (Var v) = ST.lookupType v vtable+    seType (Constant v) = Just $ Prim $ primValueType v+ruleBasicOp vtable pat _ (Update src _ (Var v))+  | Just (BasicOp Scratch {}, _) <- ST.lookupExp v vtable =+    Simplify $ letBind pat $ BasicOp $ SubExp $ Var src+-- If we are writing a single-element slice from some array, and the+-- element of that array can be computed as a PrimExp based on the+-- index, let's just write that instead.+ruleBasicOp vtable pat aux (Update src [DimSlice i n s] (Var v))+  | isCt1 n,+    isCt1 s,+    Just (ST.Indexed cs e) <- ST.index v [intConst Int64 0] vtable =+    Simplify $ do+      e' <- toSubExp "update_elem" e+      auxing aux $+        certifying cs $+          letBind pat $ BasicOp $ Update src [DimFix i] e'+ruleBasicOp vtable pat _ (Update dest destis (Var v))+  | Just (e, _) <- ST.lookupExp v vtable,+    arrayFrom e =+    Simplify $ letBind pat $ BasicOp $ SubExp $ Var dest+  where+    arrayFrom (BasicOp (Copy copy_v))+      | Just (e', _) <- ST.lookupExp copy_v vtable =+        arrayFrom e'+    arrayFrom (BasicOp (Index src srcis)) =+      src == dest && destis == srcis+    arrayFrom (BasicOp (Replicate v_shape v_se))+      | Just (Replicate dest_shape dest_se, _) <- ST.lookupBasicOp dest vtable,+        v_se == dest_se,+        shapeDims v_shape `isSuffixOf` shapeDims dest_shape =+        True+    arrayFrom _ =+      False+ruleBasicOp vtable pat _ (Update dest is se)+  | Just dest_t <- ST.lookupType dest vtable,+    isFullSlice (arrayShape dest_t) is = Simplify $+    case se of+      Var v | not $ null $ sliceDims is -> do+        v_reshaped <-+          letExp (baseString v ++ "_reshaped") $+            BasicOp $ Reshape (map DimNew $ arrayDims dest_t) v+        letBind pat $ BasicOp $ Copy v_reshaped+      _ -> letBind pat $ BasicOp $ ArrayLit [se] $ rowType dest_t+ruleBasicOp vtable pat (StmAux cs1 attrs _) (Update dest1 is1 (Var v1))+  | Just (Update dest2 is2 se2, cs2) <- ST.lookupBasicOp v1 vtable,+    Just (Copy v3, cs3) <- ST.lookupBasicOp dest2 vtable,+    Just (Index v4 is4, cs4) <- ST.lookupBasicOp v3 vtable,+    is4 == is1,+    v4 == dest1 =+    Simplify $+      certifying (cs1 <> cs2 <> cs3 <> cs4) $ do+        is5 <- subExpSlice $ sliceSlice (primExpSlice is1) (primExpSlice is2)+        attributing attrs $ letBind pat $ BasicOp $ Update dest1 is5 se2+ruleBasicOp vtable pat _ (CmpOp (CmpEq t) se1 se2)+  | Just m <- simplifyWith se1 se2 = Simplify m+  | Just m <- simplifyWith se2 se1 = Simplify m+  where+    simplifyWith (Var v) x+      | Just bnd <- ST.lookupStm v vtable,+        If p tbranch fbranch _ <- stmExp bnd,+        Just (y, z) <-+          returns v (stmPattern bnd) tbranch fbranch,+        not $ boundInBody tbranch `namesIntersect` freeIn y,+        not $ boundInBody fbranch `namesIntersect` freeIn z = Just $ do+        eq_x_y <-+          letSubExp "eq_x_y" $ BasicOp $ CmpOp (CmpEq t) x y+        eq_x_z <-+          letSubExp "eq_x_z" $ BasicOp $ CmpOp (CmpEq t) x z+        p_and_eq_x_y <-+          letSubExp "p_and_eq_x_y" $ BasicOp $ BinOp LogAnd p eq_x_y+        not_p <-+          letSubExp "not_p" $ BasicOp $ UnOp Not p+        not_p_and_eq_x_z <-+          letSubExp "p_and_eq_x_y" $ BasicOp $ BinOp LogAnd not_p eq_x_z+        letBind pat $+          BasicOp $ BinOp LogOr p_and_eq_x_y not_p_and_eq_x_z+    simplifyWith _ _ =+      Nothing++    returns v ifpat tbranch fbranch =+      fmap snd $+        find ((== v) . patElemName . fst) $+          zip (patternValueElements ifpat) $+            zip (bodyResult tbranch) (bodyResult fbranch)+ruleBasicOp _ pat _ (Replicate (Shape []) se@Constant {}) =+  Simplify $ letBind pat $ BasicOp $ SubExp se+ruleBasicOp _ pat _ (Replicate (Shape []) (Var v)) = Simplify $ do+  v_t <- lookupType v+  letBind pat $+    BasicOp $+      if primType v_t+        then SubExp $ Var v+        else Copy v+ruleBasicOp vtable pat _ (Replicate shape (Var v))+  | Just (BasicOp (Replicate shape2 se), cs) <- ST.lookupExp v vtable =+    Simplify $ certifying cs $ letBind pat $ BasicOp $ Replicate (shape <> shape2) se+ruleBasicOp _ pat _ (ArrayLit (se : ses) _)+  | all (== se) ses =+    Simplify $+      let n = constant (fromIntegral (length ses) + 1 :: Int64)+       in letBind pat $ BasicOp $ Replicate (Shape [n]) se+ruleBasicOp vtable pat aux (Index idd slice)+  | Just inds <- sliceIndices slice,+    Just (BasicOp (Reshape newshape idd2), idd_cs) <- ST.lookupExp idd vtable,+    length newshape == length inds =+    Simplify $+      case shapeCoercion newshape of+        Just _ ->+          certifying idd_cs $+            auxing aux $+              letBind pat $ BasicOp $ Index idd2 slice+        Nothing -> do+          -- Linearise indices and map to old index space.+          oldshape <- arrayDims <$> lookupType idd2+          let new_inds =+                reshapeIndex+                  (map pe64 oldshape)+                  (map pe64 $ newDims newshape)+                  (map pe64 inds)+          new_inds' <-+            mapM (toSubExp "new_index") new_inds+          certifying idd_cs $+            auxing aux $+              letBind pat $ BasicOp $ Index idd2 $ map DimFix new_inds'+ruleBasicOp _ pat _ (BinOp (Pow t) e1 e2)+  | e1 == intConst t 2 =+    Simplify $ letBind pat $ BasicOp $ BinOp (Shl t) (intConst t 1) e2+-- Handle identity permutation.+ruleBasicOp _ pat _ (Rearrange perm v)+  | sort perm == perm =+    Simplify $ letBind pat $ BasicOp $ SubExp $ Var v+ruleBasicOp vtable pat aux (Rearrange perm v)+  | Just (BasicOp (Rearrange perm2 e), v_cs) <- ST.lookupExp v vtable =+    -- Rearranging a rearranging: compose the permutations.+    Simplify $+      certifying v_cs $+        auxing aux $+          letBind pat $ BasicOp $ Rearrange (perm `rearrangeCompose` perm2) e+ruleBasicOp vtable pat aux (Rearrange perm v)+  | Just (BasicOp (Rotate offsets v2), v_cs) <- ST.lookupExp v vtable,+    Just (BasicOp (Rearrange perm3 v3), v2_cs) <- ST.lookupExp v2 vtable = Simplify $ do+    let offsets' = rearrangeShape (rearrangeInverse perm3) offsets+    rearrange_rotate <- letExp "rearrange_rotate" $ BasicOp $ Rotate offsets' v3+    certifying (v_cs <> v2_cs) $+      auxing aux $+        letBind pat $ BasicOp $ Rearrange (perm `rearrangeCompose` perm3) rearrange_rotate++-- Rearranging a replicate where the outer dimension is left untouched.+ruleBasicOp vtable pat aux (Rearrange perm v1)+  | Just (BasicOp (Replicate dims (Var v2)), v1_cs) <- ST.lookupExp v1 vtable,+    num_dims <- shapeRank dims,+    (rep_perm, rest_perm) <- splitAt num_dims perm,+    not $ null rest_perm,+    rep_perm == [0 .. length rep_perm -1] =+    Simplify $+      certifying v1_cs $+        auxing aux $ do+          v <-+            letSubExp "rearrange_replicate" $+              BasicOp $ Rearrange (map (subtract num_dims) rest_perm) v2+          letBind pat $ BasicOp $ Replicate dims v++-- A zero-rotation is identity.+ruleBasicOp _ pat _ (Rotate offsets v)+  | all isCt0 offsets = Simplify $ letBind pat $ BasicOp $ SubExp $ Var v+ruleBasicOp vtable pat aux (Rotate offsets v)+  | Just (BasicOp (Rearrange perm v2), v_cs) <- ST.lookupExp v vtable,+    Just (BasicOp (Rotate offsets2 v3), v2_cs) <- ST.lookupExp v2 vtable = Simplify $ do+    let offsets2' = rearrangeShape (rearrangeInverse perm) offsets2+        addOffsets x y = letSubExp "summed_offset" $ BasicOp $ BinOp (Add Int64 OverflowWrap) x y+    offsets' <- zipWithM addOffsets offsets offsets2'+    rotate_rearrange <-+      auxing aux $ letExp "rotate_rearrange" $ BasicOp $ Rearrange perm v3+    certifying (v_cs <> v2_cs) $+      letBind pat $ BasicOp $ Rotate offsets' rotate_rearrange++-- Combining Rotates.+ruleBasicOp vtable pat aux (Rotate offsets1 v)+  | Just (BasicOp (Rotate offsets2 v2), v_cs) <- ST.lookupExp v vtable = Simplify $ do+    offsets <- zipWithM add offsets1 offsets2+    certifying v_cs $+      auxing aux $+        letBind pat $ BasicOp $ Rotate offsets v2+  where+    add x y = letSubExp "offset" $ BasicOp $ BinOp (Add Int64 OverflowWrap) x y++-- If we see an Update with a scalar where the value to be written is+-- the result of indexing some other array, then we convert it into an+-- Update with a slice of that array.  This matters when the arrays+-- are far away (on the GPU, say), because it avoids a copy of the+-- scalar to and from the host.+ruleBasicOp vtable pat aux (Update arr_x slice_x (Var v))+  | Just _ <- sliceIndices slice_x,+    Just (Index arr_y slice_y, cs_y) <- ST.lookupBasicOp v vtable,+    ST.available arr_y vtable,+    -- XXX: we should check for proper aliasing here instead.+    arr_y /= arr_x,+    Just (slice_x_bef, DimFix i, []) <- focusNth (length slice_x - 1) slice_x,+    Just (slice_y_bef, DimFix j, []) <- focusNth (length slice_y - 1) slice_y = Simplify $ do+    let slice_x' = slice_x_bef ++ [DimSlice i (intConst Int64 1) (intConst Int64 1)]+        slice_y' = slice_y_bef ++ [DimSlice j (intConst Int64 1) (intConst Int64 1)]+    v' <- letExp (baseString v ++ "_slice") $ BasicOp $ Index arr_y slice_y'+    certifying cs_y $+      auxing aux $+        letBind pat $ BasicOp $ Update arr_x slice_x' $ Var v'++-- Simplify away 0<=i when 'i' is from a loop of form 'for i < n'.+ruleBasicOp vtable pat aux (CmpOp CmpSle {} x y)+  | Constant (IntValue (Int64Value 0)) <- x,+    Var v <- y,+    Just _ <- ST.lookupLoopVar v vtable =+    Simplify $ auxing aux $ letBind pat $ BasicOp $ SubExp $ constant True+-- Simplify away i<n when 'i' is from a loop of form 'for i < n'.+ruleBasicOp vtable pat aux (CmpOp CmpSlt {} x y)+  | Var v <- x,+    Just n <- ST.lookupLoopVar v vtable,+    n == y =+    Simplify $ auxing aux $ letBind pat $ BasicOp $ SubExp $ constant True+-- Simplify away x<0 when 'x' has been used as array size.+ruleBasicOp vtable pat aux (CmpOp CmpSlt {} (Var x) y)+  | isCt0 y,+    maybe False ST.entryIsSize $ ST.lookup x vtable =+    Simplify $ auxing aux $ letBind pat $ BasicOp $ SubExp $ constant False+ruleBasicOp _ _ _ _ =+  Skip++-- | Remove the return values of a branch, that are not actually used+-- after a branch.  Standard dead code removal can remove the branch+-- if *none* of the return values are used, but this rule is more+-- precise.+removeDeadBranchResult :: BinderOps lore => BottomUpRuleIf lore+removeDeadBranchResult (_, used) pat _ (e1, tb, fb, IfDec rettype ifsort)+  | -- Only if there is no existential context...+    patternSize pat == length rettype,+    -- Figure out which of the names in 'pat' are used...+    patused <- map (`UT.isUsedDirectly` used) $ patternNames pat,+    -- If they are not all used, then this rule applies.+    not (and patused) =+    -- Remove the parts of the branch-results that correspond to dead+    -- return value bindings.  Note that this leaves dead code in the+    -- branch bodies, but that will be removed later.+    let tses = bodyResult tb+        fses = bodyResult fb+        pick :: [a] -> [a]+        pick = map snd . filter fst . zip patused+        tb' = tb {bodyResult = pick tses}+        fb' = fb {bodyResult = pick fses}+        pat' = pick $ patternElements pat+        rettype' = pick rettype+     in Simplify $ letBind (Pattern [] pat') $ If e1 tb' fb' $ IfDec rettype' ifsort+  | otherwise = Skip  -- Some helper functions 
src/Futhark/Optimise/Sink.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}+ -- | "Sinking" is conceptually the opposite of hoisting.  The idea is -- to take code that looks like this: --@@ -41,14 +42,12 @@ -- This pass is defined on the Kernels representation.  This is not -- because we do anything kernel-specific here, but simply because -- more explicit indexing is going on after SOACs are gone.- module Futhark.Optimise.Sink (sink) where  import Control.Monad.State import Data.List (foldl') import qualified Data.Map as M import qualified Data.Set as S- import qualified Futhark.Analysis.Alias as Alias import qualified Futhark.Analysis.SymbolTable as ST import Futhark.IR.Aliases@@ -56,8 +55,11 @@ import Futhark.Pass  type SinkLore = Aliases Kernels+ type SymbolTable = ST.SymbolTable SinkLore+ type Sinking = M.Map VName (Stm SinkLore)+ type Sunk = S.Set VName  -- | Given a statement, compute how often each of its free variables@@ -68,120 +70,141 @@   case stmExp stm of     If cond tbranch fbranch _ ->       free cond 1 `comb` free tbranch 1 `comb` free fbranch 1-    Op{} -> free stm 2-    DoLoop{} -> free stm 2+    Op {} -> free stm 2+    DoLoop {} -> free stm 2     _ -> free stm 1-  where free x k = M.fromList $ zip (namesToList $ freeIn x) $ repeat k-        comb = M.unionWith (+)+  where+    free x k = M.fromList $ zip (namesToList $ freeIn x) $ repeat k+    comb = M.unionWith (+) -optimiseBranch :: SymbolTable -> Sinking -> Body SinkLore-               -> (Body SinkLore, Sunk)+optimiseBranch ::+  SymbolTable ->+  Sinking ->+  Body SinkLore ->+  (Body SinkLore, Sunk) optimiseBranch vtable sinking (Body dec stms res) =   let (stms', stms_sunk) = optimiseStms vtable sinking' stms $ freeIn res-  in (Body dec (sunk_stms <> stms') res,-      sunk <> stms_sunk)-  where free_in_stms = freeIn stms <> freeIn res-        (sinking_here, sinking') = M.partitionWithKey sunkHere sinking-        sunk_stms = stmsFromList $ M.elems sinking_here-        sunkHere v stm =-          v `nameIn` free_in_stms &&-          all (`ST.available` vtable) (namesToList (freeIn stm))-        sunk = S.fromList $ concatMap (patternNames . stmPattern) sunk_stms+   in ( Body dec (sunk_stms <> stms') res,+        sunk <> stms_sunk+      )+  where+    free_in_stms = freeIn stms <> freeIn res+    (sinking_here, sinking') = M.partitionWithKey sunkHere sinking+    sunk_stms = stmsFromList $ M.elems sinking_here+    sunkHere v stm =+      v `nameIn` free_in_stms+        && all (`ST.available` vtable) (namesToList (freeIn stm))+    sunk = S.fromList $ concatMap (patternNames . stmPattern) sunk_stms -optimiseStms :: SymbolTable -> Sinking -> Stms SinkLore -> Names-             -> (Stms SinkLore, Sunk)+optimiseStms ::+  SymbolTable ->+  Sinking ->+  Stms SinkLore ->+  Names ->+  (Stms SinkLore, Sunk) optimiseStms init_vtable init_sinking all_stms free_in_res =   let (all_stms', sunk) =         optimiseStms' init_vtable init_sinking $ stmsToList all_stms-  in (stmsFromList all_stms', sunk)+   in (stmsFromList all_stms', sunk)   where-    multiplicities = foldl' (M.unionWith (+))-                     (M.fromList (zip (namesToList free_in_res) (repeat 1)))-                     (map multiplicity $ stmsToList all_stms)+    multiplicities =+      foldl'+        (M.unionWith (+))+        (M.fromList (zip (namesToList free_in_res) (repeat 1)))+        (map multiplicity $ stmsToList all_stms)      optimiseStms' _ _ [] = ([], mempty)-     optimiseStms' vtable sinking (stm : stms)-      | BasicOp Index{} <- stmExp stm,+      | BasicOp Index {} <- stmExp stm,         [pe] <- patternElements (stmPattern stm),         primType $ patElemType pe,-        maybe True (==1) $ M.lookup (patElemName pe) multiplicities =-          let (stms', sunk) =-                optimiseStms' vtable' (M.insert (patElemName pe) stm sinking) stms-          in if patElemName pe `S.member` sunk-             then (stms', sunk)-             else (stm : stms', sunk)-+        maybe True (== 1) $ M.lookup (patElemName pe) multiplicities =+        let (stms', sunk) =+              optimiseStms' vtable' (M.insert (patElemName pe) stm sinking) stms+         in if patElemName pe `S.member` sunk+              then (stms', sunk)+              else (stm : stms', sunk)       | If cond tbranch fbranch ret <- stmExp stm =-          let (tbranch', tsunk) = optimiseBranch vtable sinking tbranch-              (fbranch', fsunk) = optimiseBranch vtable sinking fbranch-              (stms', sunk) = optimiseStms' vtable' sinking stms-          in (stm { stmExp = If cond tbranch' fbranch' ret } : stms',-              tsunk <> fsunk <> sunk)-+        let (tbranch', tsunk) = optimiseBranch vtable sinking tbranch+            (fbranch', fsunk) = optimiseBranch vtable sinking fbranch+            (stms', sunk) = optimiseStms' vtable' sinking stms+         in ( stm {stmExp = If cond tbranch' fbranch' ret} : stms',+              tsunk <> fsunk <> sunk+            )       | Op (SegOp op) <- stmExp stm =-          let scope = scopeOfSegSpace $ segSpace op-              (stms', stms_sunk) = optimiseStms' vtable' sinking stms-              (op', op_sunk) = runState (mapSegOpM (opMapper scope) op) mempty-          in (stm { stmExp = Op (SegOp op') } : stms',-              stms_sunk <> op_sunk)-+        let scope = scopeOfSegSpace $ segSpace op+            (stms', stms_sunk) = optimiseStms' vtable' sinking stms+            (op', op_sunk) = runState (mapSegOpM (opMapper scope) op) mempty+         in ( stm {stmExp = Op (SegOp op')} : stms',+              stms_sunk <> op_sunk+            )       | otherwise =-          let (stms', stms_sunk) = optimiseStms' vtable' sinking stms-              (e', stm_sunk) = runState (mapExpM mapper (stmExp stm)) mempty-          in (stm { stmExp = e' } : stms',-              stm_sunk <> stms_sunk)--      where vtable' = ST.insertStm stm vtable-            mapper =-              identityMapper-              { mapOnBody = \scope body -> do-                  let (body', sunk) =-                        optimiseBody (ST.fromScope scope <> vtable) sinking body-                  modify (<>sunk)-                  return body'-              }+        let (stms', stms_sunk) = optimiseStms' vtable' sinking stms+            (e', stm_sunk) = runState (mapExpM mapper (stmExp stm)) mempty+         in ( stm {stmExp = e'} : stms',+              stm_sunk <> stms_sunk+            )+      where+        vtable' = ST.insertStm stm vtable+        mapper =+          identityMapper+            { mapOnBody = \scope body -> do+                let (body', sunk) =+                      optimiseBody (ST.fromScope scope <> vtable) sinking body+                modify (<> sunk)+                return body'+            } -            opMapper scope =-              identitySegOpMapper-              { mapOnSegOpLambda = \lam -> do-                  let (body, sunk) =-                        optimiseBody op_vtable sinking $+        opMapper scope =+          identitySegOpMapper+            { mapOnSegOpLambda = \lam -> do+                let (body, sunk) =+                      optimiseBody op_vtable sinking $                         lambdaBody lam-                  modify (<>sunk)-                  return lam { lambdaBody = body }--              , mapOnSegOpBody = \body -> do-                  let (body', sunk) =-                        optimiseKernelBody op_vtable sinking body-                  modify (<>sunk)-                  return body'-              }-              where op_vtable = ST.fromScope scope <> vtable+                modify (<> sunk)+                return lam {lambdaBody = body},+              mapOnSegOpBody = \body -> do+                let (body', sunk) =+                      optimiseKernelBody op_vtable sinking body+                modify (<> sunk)+                return body'+            }+          where+            op_vtable = ST.fromScope scope <> vtable -optimiseBody :: SymbolTable -> Sinking -> Body SinkLore-             -> (Body SinkLore, Sunk)+optimiseBody ::+  SymbolTable ->+  Sinking ->+  Body SinkLore ->+  (Body SinkLore, Sunk) optimiseBody vtable sinking (Body dec stms res) =   let (stms', sunk) = optimiseStms vtable sinking stms $ freeIn res-  in (Body dec stms' res, sunk)+   in (Body dec stms' res, sunk) -optimiseKernelBody :: SymbolTable -> Sinking -> KernelBody SinkLore-                   -> (KernelBody SinkLore, Sunk)+optimiseKernelBody ::+  SymbolTable ->+  Sinking ->+  KernelBody SinkLore ->+  (KernelBody SinkLore, Sunk) optimiseKernelBody vtable sinking (KernelBody dec stms res) =   let (stms', sunk) = optimiseStms vtable sinking stms $ freeIn res-  in (KernelBody dec stms' res, sunk)+   in (KernelBody dec stms' res, sunk)  -- | The pass definition. sink :: Pass Kernels Kernels-sink = Pass "sink" "move memory loads closer to their uses" $-       fmap removeProgAliases .-       intraproceduralTransformationWithConsts onConsts onFun .-       Alias.aliasAnalysis-  where onFun _ fd = do-          let vtable = ST.insertFParams (funDefParams fd) mempty-              (body, _) = optimiseBody vtable mempty $ funDefBody fd-          return fd { funDefBody = body }+sink =+  Pass "sink" "move memory loads closer to their uses" $+    fmap removeProgAliases+      . intraproceduralTransformationWithConsts onConsts onFun+      . Alias.aliasAnalysis+  where+    onFun _ fd = do+      let vtable = ST.insertFParams (funDefParams fd) mempty+          (body, _) = optimiseBody vtable mempty $ funDefBody fd+      return fd {funDefBody = body} -        onConsts consts =-          pure $ fst $ optimiseStms mempty mempty consts $-          namesFromList $ M.keys $ scopeOf consts+    onConsts consts =+      pure $+        fst $+          optimiseStms mempty mempty consts $+            namesFromList $ M.keys $ scopeOf consts
src/Futhark/Optimise/TileLoops.hs view
@@ -1,930 +1,1271 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}--- | Perform a restricted form of loop tiling within SegMaps.  We only--- tile primitive types, to avoid excessive local memory use.-module Futhark.Optimise.TileLoops-       ( tileLoops )-       where--import Control.Monad.State-import Control.Monad.Reader-import qualified Data.Sequence as Seq-import qualified Data.Map.Strict as M-import Data.List (foldl')--import Prelude hiding (quot)--import Futhark.MonadFreshNames-import Futhark.IR.Kernels-import Futhark.Transform.Rename-import Futhark.Pass-import Futhark.Tools---- | The pass definition.-tileLoops :: Pass Kernels Kernels-tileLoops = Pass "tile loops" "Tile stream loops inside kernels" $-            \(Prog consts funs) ->-              Prog consts <$> mapM optimiseFunDef funs--optimiseFunDef :: MonadFreshNames m => FunDef Kernels -> m (FunDef Kernels)-optimiseFunDef fundec = do-  body' <- modifyNameSource $ runState $-           runReaderT m (scopeOfFParams (funDefParams fundec))-  return fundec { funDefBody = body' }-  where m = optimiseBody $ funDefBody fundec--type TileM = ReaderT (Scope Kernels) (State VNameSource)--optimiseBody :: Body Kernels -> TileM (Body Kernels)-optimiseBody (Body () bnds res) = localScope (scopeOf bnds) $-  Body () <$> (mconcat <$> mapM optimiseStm (stmsToList bnds)) <*> pure res--optimiseStm :: Stm Kernels -> TileM (Stms Kernels)-optimiseStm (Let pat aux (Op (SegOp (SegMap lvl@SegThread{} space ts kbody)))) = do-  (host_stms, (lvl', space', kbody')) <- tileInKernelBody mempty initial_variance lvl space ts kbody-  return $ host_stms <>-    oneStm (Let pat aux $ Op $ SegOp $ SegMap lvl' space' ts kbody')-  where initial_variance = M.map mempty $ scopeOfSegSpace space--optimiseStm (Let pat aux e) =-  pure <$> (Let pat aux <$> mapExpM optimise e)-  where optimise = identityMapper { mapOnBody = \scope -> localScope scope . optimiseBody }--tileInKernelBody :: Names -> VarianceTable-                 -> SegLevel -> SegSpace -> [Type] -> KernelBody Kernels-                 -> TileM (Stms Kernels, (SegLevel, SegSpace, KernelBody Kernels))-tileInKernelBody branch_variant initial_variance lvl initial_kspace ts kbody-  | Just kbody_res <- mapM isSimpleResult $ kernelBodyResult kbody = do-      maybe_tiled <--        tileInBody branch_variant mempty initial_variance lvl initial_kspace ts $-        Body () (kernelBodyStms kbody) kbody_res-      case maybe_tiled of-        Just (host_stms, tiling, tiledBody) -> do-          (res', stms') <--            runBinder $ mapM (tilingTileReturns tiling) =<< tiledBody mempty-          return (host_stms, (tilingLevel tiling,-                              tilingSpace tiling,-                              KernelBody () stms' res'))-        Nothing ->-          return (mempty, (lvl, initial_kspace, kbody))-  | otherwise =-      return (mempty, (lvl, initial_kspace, kbody))-  where isSimpleResult (Returns _ se) = Just se-        isSimpleResult _ = Nothing--tileInBody :: Names -> Names -> VarianceTable-           -> SegLevel -> SegSpace -> [Type] -> Body Kernels-           -> TileM (Maybe (Stms Kernels, Tiling, TiledBody))-tileInBody branch_variant private initial_variance initial_lvl initial_space res_ts (Body () initial_kstms stms_res) =-  descend mempty $ stmsToList initial_kstms-  where-    variance = varianceInStms initial_variance initial_kstms--    descend _ [] =-      return Nothing--    descend prestms (stm_to_tile:poststms)--      -- 1D tiling of redomap.-      | (gtid, kdim) : top_space_rev <- reverse $ unSegSpace initial_space,-        Just (w, arrs, form) <- tileable stm_to_tile,-        not $ any (nameIn gtid .-                   flip (M.findWithDefault mempty) variance) arrs,-        not $ gtid `nameIn` branch_variant,-        (prestms', poststms') <--          preludeToPostlude variance prestms stm_to_tile (stmsFromList poststms),-        used <- freeIn stm_to_tile <> freeIn stms_res =--          Just . injectPrelude initial_space private variance prestms' used <$>-          tileGeneric (tiling1d $ reverse top_space_rev)-          initial_lvl res_ts (stmPattern stm_to_tile)-          gtid kdim-          w form (zip arrs $ repeat [0]) poststms' stms_res--      -- 2D tiling of redomap.-      | (gtids, kdims) <- unzip $ unSegSpace initial_space,-        Just (w, arrs, form) <- tileable stm_to_tile,-        Just inner_perm <- mapM (invariantToOneOfTwoInnerDims branch_variant variance gtids) arrs,-        gtid_y : gtid_x : top_gtids_rev <- reverse gtids,-        kdim_y : kdim_x : top_kdims_rev <- reverse kdims,-        (prestms', poststms') <--          preludeToPostlude variance prestms stm_to_tile (stmsFromList poststms),-        used <- freeIn stm_to_tile <> freeIn stms_res =--          Just . injectPrelude initial_space private variance prestms' used <$>-          tileGeneric (tiling2d $ reverse $ zip top_gtids_rev top_kdims_rev)-          initial_lvl res_ts (stmPattern stm_to_tile)-          (gtid_x, gtid_y) (kdim_x, kdim_y)-          w form (zip arrs inner_perm) poststms' stms_res--      -- Tiling inside for-loop.-      | DoLoop [] merge (ForLoop i it bound []) loopbody <- stmExp stm_to_tile,-        (prestms', poststms') <--          preludeToPostlude variance prestms stm_to_tile (stmsFromList poststms) = do--          let branch_variant' =-                branch_variant <>-                mconcat (map (flip (M.findWithDefault mempty) variance)-                         (namesToList (freeIn bound)))-              merge_params = map fst merge-              private' = namesFromList $ map paramName merge_params--          maybe_tiled <--            localScope (M.insert i (IndexName it) $ scopeOfFParams merge_params) $-            tileInBody branch_variant' private' variance initial_lvl initial_space-            (map paramType merge_params) $ mkBody (bodyStms loopbody) (bodyResult loopbody)--          case maybe_tiled of-            Nothing -> next-            Just tiled ->-              Just <$> tileDoLoop initial_space variance prestms'-              (freeIn loopbody <> freeIn merge) tiled-              res_ts (stmPattern stm_to_tile) (stmAux stm_to_tile)-              merge i it bound poststms' stms_res--      | otherwise = next--      where next = localScope (scopeOf stm_to_tile) $-                   descend (prestms <> oneStm stm_to_tile) poststms---- | Move statements from prelude to postlude if they are not used in--- the tiled statement anyway.-preludeToPostlude :: VarianceTable-                  -> Stms Kernels -> Stm Kernels -> Stms Kernels-                  -> (Stms Kernels, Stms Kernels)-preludeToPostlude variance prelude stm_to_tile postlude =-  (prelude_used, prelude_not_used <> postlude)-  where used_in_tiled = freeIn stm_to_tile--        used_in_stm_variant =-          (used_in_tiled<>) $ mconcat $-          map (flip (M.findWithDefault mempty) variance) $-          namesToList used_in_tiled--        used stm = any (`nameIn` used_in_stm_variant) $-                   patternNames $ stmPattern stm--        (prelude_used, prelude_not_used) =-          Seq.partition used prelude---- | Partition prelude statements preceding a tiled loop (or something--- containing a tiled loop) into three categories:------ 1) Group-level statements that are invariant to the threads in the group.------ 2) Thread-variant statements that should be computed once with a segmap_thread_scalar.------ 3) Thread-variant statements that should be recomputed whenever--- they are needed.------ The third category duplicates computation, so we only want to do it--- when absolutely necessary.  Currently, this is necessary for--- results that are views of an array (slicing, rotate, etc), because--- these cannot be efficiently represented by a scalar segmap (they'll--- be manifested in memory).-partitionPrelude :: VarianceTable -> Stms Kernels -> Names-                 -> (Stms Kernels, Stms Kernels, Stms Kernels)-partitionPrelude variance prestms private =-  (invariant_prestms, precomputed_variant_prestms, recomputed_variant_prestms)-  where-    invariantTo names stm =-      case patternNames (stmPattern stm) of-        [] -> True -- Does not matter.-        v:_ -> not $ any (`nameIn` names) $ namesToList $-               M.findWithDefault mempty v variance-    (invariant_prestms, variant_prestms) =-      Seq.partition (invariantTo private) prestms--    mustBeInlinedExp (BasicOp (Index _ slice)) = not $ null $ sliceDims slice-    mustBeInlinedExp (BasicOp Rotate{}) = True-    mustBeInlinedExp (BasicOp Rearrange{}) = True-    mustBeInlinedExp (BasicOp Reshape{}) = True-    mustBeInlinedExp _ = False-    mustBeInlined = mustBeInlinedExp . stmExp--    must_be_inlined = namesFromList $ concatMap (patternNames . stmPattern) $-                      stmsToList $ Seq.filter mustBeInlined variant_prestms-    recompute stm =-      any (`nameIn` must_be_inlined) (patternNames (stmPattern stm)) ||-      not (invariantTo must_be_inlined stm)-    (recomputed_variant_prestms, precomputed_variant_prestms) =-      Seq.partition recompute variant_prestms---- Anything that is variant to the "private" names should be--- considered thread-local.-injectPrelude :: SegSpace -> Names -> VarianceTable-              -> Stms Kernels -> Names-              -> (Stms Kernels, Tiling, TiledBody)-              -> (Stms Kernels, Tiling, TiledBody)-injectPrelude initial_space private variance prestms used (host_stms, tiling, tiledBody) =-  (host_stms, tiling, tiledBody')-  where private' = private <> namesFromList-                   (map fst $-                    filter (`notElem` unSegSpace (tilingSpace tiling)) $-                    unSegSpace initial_space)--        tiledBody' privstms = do-          let (invariant_prestms,-               precomputed_variant_prestms,-               recomputed_variant_prestms) =-                partitionPrelude variance prestms private'--          addStms invariant_prestms--          let live_set = namesToList $ liveSet precomputed_variant_prestms $-                         used <> freeIn recomputed_variant_prestms-          prelude_arrs <- inScopeOf precomputed_variant_prestms $-                          doPrelude tiling precomputed_variant_prestms live_set--          let prelude_privstms =-                PrivStms recomputed_variant_prestms $-                mkReadPreludeValues prelude_arrs live_set--          tiledBody (prelude_privstms <> privstms)--tileDoLoop :: SegSpace -> VarianceTable-           -> Stms Kernels -> Names-           -> (Stms Kernels, Tiling, TiledBody)-           -> [Type] -> Pattern Kernels -> StmAux (ExpDec Kernels)-           -> [(FParam Kernels, SubExp)] -> VName -> IntType -> SubExp-           -> Stms Kernels -> Result-           -> TileM (Stms Kernels, Tiling, TiledBody)-tileDoLoop initial_space variance prestms used_in_body (host_stms, tiling, tiledBody) res_ts pat aux merge i it bound poststms poststms_res = do--  let (invariant_prestms,-       precomputed_variant_prestms,-       recomputed_variant_prestms) =-        partitionPrelude variance prestms tiled_kdims--  let (mergeparams, mergeinits) = unzip merge--      -- Expand the loop merge parameters to be arrays.-      tileDim t = arrayOf t (tilingTileShape tiling) $ uniqueness t--      tiledBody' privstms = inScopeOf host_stms $ do-        addStms invariant_prestms--        let live_set = namesToList $ liveSet precomputed_variant_prestms used_in_body-        prelude_arrs <- inScopeOf precomputed_variant_prestms $-                        doPrelude tiling precomputed_variant_prestms live_set--        mergeparams' <- forM mergeparams $ \(Param pname pt) ->-          Param <$> newVName (baseString pname ++ "_group") <*> pure (tileDim pt)--        let merge_ts = map paramType mergeparams--        let inloop_privstms =-              PrivStms recomputed_variant_prestms $-              mkReadPreludeValues prelude_arrs live_set--        mergeinit' <--          fmap (map Var) $ certifying (stmAuxCerts aux) $-          tilingSegMap tiling "tiled_loopinit" (scalarLevel tiling) ResultPrivate $-          \in_bounds slice ->-            fmap (map Var) $ protectOutOfBounds "loopinit" in_bounds merge_ts $ do-            addPrivStms slice inloop_privstms-            addPrivStms slice privstms-            return mergeinits--        let merge' = zip mergeparams' mergeinit'--        let indexMergeParams slice =-              localScope (scopeOfFParams mergeparams') $-              forM_ (zip mergeparams mergeparams') $ \(to, from) ->-              letBindNames [paramName to] $ BasicOp $ Index (paramName from) $-              fullSlice (paramType from) slice--        loopbody' <- runBodyBinder $ resultBody . map Var <$>-                     tiledBody (PrivStms mempty indexMergeParams <> privstms <> inloop_privstms)-        accs' <- letTupExp "tiled_inside_loop" $-                 DoLoop [] merge' (ForLoop i it bound []) loopbody'--        postludeGeneric tiling privstms pat accs' poststms poststms_res res_ts--  return (host_stms, tiling, tiledBody')--  where tiled_kdims = namesFromList $ map fst $-                      filter (`notElem` unSegSpace (tilingSpace tiling)) $-                      unSegSpace initial_space--doPrelude :: Tiling -> Stms Kernels -> [VName] -> Binder Kernels [VName]-doPrelude tiling prestms prestms_live =-  -- Create a SegMap that takes care of the prelude for every thread.-  tilingSegMap tiling "prelude" (scalarLevel tiling) ResultPrivate $-  \in_bounds _slice -> do-    ts <- mapM lookupType prestms_live-    fmap (map Var) $ letTupExp "pre" =<<-      eIf (toExp in_bounds)-      (do addStms prestms-          resultBodyM $ map Var prestms_live)-      (eBody $ map eBlank ts)--liveSet :: FreeIn a => Stms Kernels -> a -> Names-liveSet stms after =-  namesFromList (concatMap (patternNames . stmPattern) stms) `namesIntersection`-  freeIn after--tileable :: Stm Kernels-         -> Maybe (SubExp, [VName],-                   (Commutativity, Lambda Kernels, [SubExp], Lambda Kernels))-tileable stm-  | Op (OtherOp (Screma w form arrs)) <- stmExp stm,-    Just (reds, map_lam) <- isRedomapSOAC form,-    Reduce red_comm red_lam red_nes <- singleReduce reds,-    lambdaReturnType map_lam == lambdaReturnType red_lam, -- No mapout arrays.-    not $ null arrs,-    all primType $ lambdaReturnType map_lam,-    all (primType . paramType) $ lambdaParams map_lam =-      Just (w, arrs, (red_comm, red_lam, red_nes, map_lam))-  | otherwise =-      Nothing---- | Statements that we insert directly into every thread-private--- SegMaps.  This is for things that cannot efficiently be computed--- once in advance in the prelude SegMap, primarily (exclusively?)--- array slicing operations.-data PrivStms = PrivStms (Stms Kernels) ReadPrelude--privStms :: Stms Kernels -> PrivStms-privStms stms = PrivStms stms $ const $ return ()--addPrivStms :: Slice SubExp -> PrivStms -> Binder Kernels ()-addPrivStms local_slice (PrivStms stms readPrelude) = do-  readPrelude local_slice-  addStms stms--instance Semigroup PrivStms where-  PrivStms stms_x readPrelude_x <> PrivStms stms_y readPrelude_y =-    PrivStms stms_z readPrelude_z-    where stms_z = stms_x <> stms_y-          readPrelude_z slice = readPrelude_x slice >> readPrelude_y slice--instance Monoid PrivStms where-  mempty = privStms mempty--type ReadPrelude = Slice SubExp -> Binder Kernels ()---- | Information about a loop that has been tiled inside a kernel, as--- well as the kinds of changes that we would then like to perform on--- the kernel.-data Tiling =-  Tiling-  { tilingSegMap :: String -> SegLevel -> ResultManifest-                 -> (PrimExp VName -> Slice SubExp -> Binder Kernels [SubExp])-                 -> Binder Kernels [VName]-    -- The boolean PrimExp indicates whether they are in-bounds.--  , tilingReadTile :: TileKind -> PrivStms-                   -> SubExp -> [(VName, [Int])]-                   -> Binder Kernels [VName]--  , tilingProcessTile :: PrivStms-                      -> Commutativity -> Lambda Kernels -> Lambda Kernels-                      -> [(VName, [Int])] -> [VName]-                      -> Binder Kernels [VName]--  , tilingProcessResidualTile :: PrivStms-                              -> Commutativity -> Lambda Kernels -> Lambda Kernels-                              -> SubExp -> [VName] -> SubExp-                              -> [(VName, [Int])]-                              -> Binder Kernels [VName]--  , tilingTileReturns :: VName -> Binder Kernels KernelResult--  , tilingSpace :: SegSpace--  , tilingTileShape :: Shape--  , tilingLevel :: SegLevel--  , tilingNumWholeTiles :: Binder Kernels SubExp-  }--type DoTiling gtids kdims =-  SegLevel -> gtids -> kdims -> SubExp -> Binder Kernels Tiling--scalarLevel :: Tiling -> SegLevel-scalarLevel tiling =-  SegThread (segNumGroups lvl) (segGroupSize lvl) SegNoVirt-  where lvl = tilingLevel tiling--protectOutOfBounds :: String -> PrimExp VName -> [Type] -> Binder Kernels [SubExp]-                   -> Binder Kernels [VName]-protectOutOfBounds desc in_bounds ts m =-  letTupExp desc =<< eIf (toExp in_bounds) (resultBody <$> m) (eBody $ map eBlank ts)--postludeGeneric :: Tiling -> PrivStms-                -> Pattern Kernels -> [VName]-                -> Stms Kernels -> Result -> [Type]-                -> Binder Kernels [VName]-postludeGeneric tiling privstms pat accs' poststms poststms_res res_ts =-  tilingSegMap tiling "thread_res" (scalarLevel tiling) ResultPrivate $ \in_bounds slice -> do-    -- Read our per-thread result from the tiled loop.-    forM_ (zip (patternNames pat) accs') $ \(us, everyone) -> do-      everyone_t <- lookupType everyone-      letBindNames [us] $ BasicOp $ Index everyone $ fullSlice everyone_t slice--    if poststms == mempty-      then do -- The privstms may still be necessary for the result.-      addPrivStms slice privstms-      return poststms_res--      else-      fmap (map Var) $ protectOutOfBounds "postlude" in_bounds res_ts $ do-      addPrivStms slice privstms-      addStms poststms-      return poststms_res--type TiledBody = PrivStms -> Binder Kernels [VName]--tileGeneric :: DoTiling gtids kdims-            -> SegLevel-            -> [Type]-            -> Pattern Kernels-            -> gtids-            -> kdims-            -> SubExp-            -> (Commutativity, Lambda Kernels, [SubExp], Lambda Kernels)-            -> [(VName, [Int])]-            -> Stms Kernels -> Result-            -> TileM (Stms Kernels, Tiling, TiledBody)-tileGeneric doTiling initial_lvl res_ts pat gtids kdims w form arrs_and_perms poststms poststms_res = do--  (tiling, tiling_stms) <- runBinder $ doTiling initial_lvl gtids kdims w--  return (tiling_stms, tiling, tiledBody tiling)--  where-    (red_comm, red_lam, red_nes, map_lam) = form--    tiledBody :: Tiling -> PrivStms -> Binder Kernels [VName]-    tiledBody tiling privstms = do-      let tile_shape = tilingTileShape tiling--      num_whole_tiles <- tilingNumWholeTiles tiling--      -- We don't use a Replicate here, because we want to enforce a-      -- scalar memory space.-      mergeinits <- tilingSegMap tiling "mergeinit" (scalarLevel tiling) ResultPrivate $ \in_bounds slice ->-        -- Constant neutral elements (a common case) do not need protection from OOB.-        if freeIn red_nes == mempty-          then return red_nes-          else fmap (map Var) $ protectOutOfBounds "neutral" in_bounds (lambdaReturnType red_lam) $ do-          addPrivStms slice privstms-          return red_nes--      merge <- forM (zip (lambdaParams red_lam) mergeinits) $ \(p, mergeinit) ->-        (,) <$>-        newParam (baseString (paramName p) ++ "_merge")-        (paramType p `arrayOfShape` tile_shape `toDecl` Unique) <*>-        pure (Var mergeinit)--      tile_id <- newVName "tile_id"-      let loopform = ForLoop tile_id Int32 num_whole_tiles []-      loopbody <- renameBody <=< runBodyBinder $ inScopeOf loopform $-                  localScope (scopeOfFParams $ map fst merge) $ do--        -- Collectively read a tile.-        tile <- tilingReadTile tiling TilePartial privstms (Var tile_id) arrs_and_perms--        -- Now each thread performs a traversal of the tile and-        -- updates its accumulator.-        resultBody . map Var <$>-          tilingProcessTile tiling privstms-          red_comm red_lam map_lam-          (zip tile (map snd arrs_and_perms)) (map (paramName . fst) merge)--      accs <- letTupExp "accs" $ DoLoop [] merge loopform loopbody--      -- We possibly have to traverse a residual tile.-      red_lam' <- renameLambda red_lam-      map_lam' <- renameLambda map_lam-      accs' <- tilingProcessResidualTile tiling privstms-               red_comm red_lam' map_lam'-               num_whole_tiles accs w arrs_and_perms--      -- Create a SegMap that takes care of the postlude for every thread.-      postludeGeneric tiling privstms pat accs' poststms poststms_res res_ts--data TileKind = TilePartial | TileFull--mkReadPreludeValues :: [VName] -> [VName] -> ReadPrelude-mkReadPreludeValues prestms_live_arrs prestms_live slice =-  fmap mconcat $ forM (zip prestms_live_arrs prestms_live) $ \(arr, v) -> do-  arr_t <- lookupType arr-  letBindNames [v] $ BasicOp $ Index arr $ fullSlice arr_t slice--tileReturns :: [(VName, SubExp)] -> [(SubExp, SubExp)] -> VName -> Binder Kernels KernelResult-tileReturns dims_on_top dims arr = do-  let unit_dims = replicate (length dims_on_top) (intConst Int32 1)-  arr' <- if null dims_on_top then return arr-          else do arr_t <- lookupType arr-                  let new_shape = unit_dims ++ arrayDims arr_t-                  letExp (baseString arr) $ BasicOp $ Reshape (map DimNew new_shape) arr-  let tile_dims = zip (map snd dims_on_top) unit_dims ++ dims-  return $ TileReturns tile_dims arr'--segMap1D :: String-         -> SegLevel -> ResultManifest-         -> (VName -> Binder Kernels [SubExp])-         -> Binder Kernels [VName]-segMap1D desc lvl manifest f = do-  ltid <- newVName "ltid"-  ltid_flat <- newVName "ltid_flat"-  let space = SegSpace ltid_flat [(ltid, unCount $ segGroupSize lvl)]--  ((ts, res), stms) <- runBinder $ do-    res <- f ltid-    ts <- mapM subExpType res-    return (ts, res)-  Body _ stms' res' <- renameBody $ mkBody stms res--  letTupExp desc $ Op $ SegOp $-    SegMap lvl space ts $ KernelBody () stms' $ map (Returns manifest) res'--reconstructGtids1D :: Count GroupSize SubExp -> VName -> VName -> VName-                   -> Binder Kernels ()-reconstructGtids1D group_size gtid gid ltid  =-  letBindNames [gtid] =<<-    toExp (LeafExp gid int32 *-           primExpFromSubExp int32 (unCount group_size) +-           LeafExp ltid int32)--readTile1D :: SubExp -> VName -> VName-           -> Count NumGroups SubExp -> Count GroupSize SubExp-           -> TileKind -> PrivStms-           -> SubExp-           -> [(VName, [Int])]-           -> Binder Kernels [VName]-readTile1D-  tile_size gid gtid num_groups group_size-  kind privstms tile_id arrs_and_perms =--  segMap1D "full_tile" (SegThread num_groups group_size SegNoVirt) ResultNoSimplify $ \ltid -> do-    j <- letSubExp "j" =<<-         toExp (primExpFromSubExp int32 tile_id *-                primExpFromSubExp int32 tile_size +-                LeafExp ltid int32)--    reconstructGtids1D group_size gtid gid ltid-    addPrivStms [DimFix $ Var ltid] privstms--    let arrs = map fst arrs_and_perms-    arr_ts <- mapM lookupType arrs-    let tile_ts = map rowType arr_ts-        w = arraysSize 0 arr_ts--    let readTileElem arr =-          -- No need for fullSlice because we are tiling only prims.-          letExp "tile_elem" $ BasicOp $ Index arr [DimFix j]-    fmap (map Var) $-      case kind of-        TilePartial ->-          letTupExp "pre" =<< eIf (toExp $ primExpFromSubExp int32 j .<.-                                   primExpFromSubExp int32 w)-          (resultBody <$> mapM (fmap Var . readTileElem) arrs)-          (eBody $ map eBlank tile_ts)-        TileFull ->-          mapM readTileElem arrs--processTile1D :: VName -> VName -> SubExp -> SubExp-              -> Count NumGroups SubExp -> Count GroupSize SubExp-              -> PrivStms-              -> Commutativity -> Lambda Kernels -> Lambda Kernels-              -> [(VName, [Int])] -> [VName]-              -> Binder Kernels [VName]-processTile1D-  gid gtid kdim tile_size num_groups group_size-  privstms-  red_comm red_lam map_lam tiles_and_perm accs = do--  let tile = map fst tiles_and_perm--  segMap1D "acc" (SegThread num_groups group_size SegNoVirt) ResultPrivate $ \ltid -> do--    reconstructGtids1D group_size gtid gid ltid-    addPrivStms [DimFix $ Var ltid] privstms--    -- We replace the neutral elements with the accumulators (this is-    -- OK because the parallel semantics are not used after this-    -- point).-    thread_accs <- forM accs $ \acc ->-      letSubExp "acc" $ BasicOp $ Index acc [DimFix $ Var ltid]-    let form' = redomapSOAC [Reduce red_comm red_lam thread_accs] map_lam--    fmap (map Var) $-      letTupExp "acc" =<< eIf (toExp $ LeafExp gtid int32 .<. primExpFromSubExp int32 kdim)-      (eBody [pure $ Op $ OtherOp $ Screma tile_size form' tile])-      (resultBodyM thread_accs)--processResidualTile1D :: VName -> VName -> SubExp -> SubExp-                      -> Count NumGroups SubExp -> Count GroupSize SubExp -> PrivStms-                      -> Commutativity -> Lambda Kernels -> Lambda Kernels-                      -> SubExp -> [VName] -> SubExp -> [(VName, [Int])]-                      -> Binder Kernels [VName]-processResidualTile1D-  gid gtid kdim tile_size num_groups group_size privstms red_comm red_lam map_lam-  num_whole_tiles accs w arrs_and_perms = do-  -- The number of residual elements that are not covered by-  -- the whole tiles.-  residual_input <- letSubExp "residual_input" $-    BasicOp $ BinOp (SRem Int32 Unsafe) w tile_size--  letTupExp "acc_after_residual" =<<-    eIf (toExp $ primExpFromSubExp int32 residual_input .==. 0)-    (resultBodyM $ map Var accs)-    (nonemptyTile residual_input)--  where-    nonemptyTile residual_input = runBodyBinder $ do-      -- Collectively construct a tile.  Threads that are out-of-bounds-      -- provide a blank dummy value.-      full_tile <- readTile1D tile_size gid gtid  num_groups group_size-                   TilePartial privstms num_whole_tiles arrs_and_perms-      tile <- forM full_tile $ \tile ->-        letExp "partial_tile" $ BasicOp $ Index tile-        [DimSlice (intConst Int32 0) residual_input (intConst Int32 1)]--      -- Now each thread performs a traversal of the tile and-      -- updates its accumulator.-      resultBody . map Var <$> processTile1D-        gid gtid kdim residual_input num_groups group_size privstms-        red_comm red_lam map_lam (zip tile $ repeat [0]) accs--tiling1d :: [(VName,SubExp)] -> DoTiling VName SubExp-tiling1d dims_on_top initial_lvl gtid kdim w = do-  gid <- newVName "gid"-  gid_flat <- newVName "gid_flat"--  (lvl, space) <--    if null dims_on_top-    then return (SegGroup (segNumGroups initial_lvl) (segGroupSize initial_lvl) $ segVirt initial_lvl,-                 SegSpace gid_flat [(gid, unCount $ segNumGroups initial_lvl)])-    else do-      group_size <- letSubExp "computed_group_size" $-                    BasicOp $ BinOp (SMin Int32) (unCount (segGroupSize initial_lvl)) kdim--      -- How many groups we need to exhaust the innermost dimension.-      ldim <- letSubExp "ldim" $-              BasicOp $ BinOp (SDivUp Int32 Unsafe) kdim group_size--      num_groups <- letSubExp "computed_num_groups" =<<-                    foldBinOp (Mul Int32 OverflowUndef) ldim (map snd dims_on_top)--      return (SegGroup (Count num_groups) (Count group_size) SegNoVirt,-              SegSpace gid_flat $ dims_on_top ++ [(gid, ldim)])-  let tile_size = unCount $ segGroupSize lvl---  return Tiling-    { tilingSegMap = \desc lvl' manifest f -> segMap1D desc lvl' manifest $ \ltid -> do-        letBindNames [gtid] =<<-          toExp (LeafExp gid int32 * primExpFromSubExp int32 tile_size +-                 LeafExp ltid int32)-        f (LeafExp gtid int32 .<. primExpFromSubExp int32 kdim)-          [DimFix $ Var ltid]--    , tilingReadTile =-        readTile1D tile_size gid gtid (segNumGroups lvl) (segGroupSize lvl)--    , tilingProcessTile =-        processTile1D gid gtid kdim tile_size (segNumGroups lvl) (segGroupSize lvl)--    , tilingProcessResidualTile =-        processResidualTile1D gid gtid kdim tile_size (segNumGroups lvl) (segGroupSize lvl)--    , tilingTileReturns = tileReturns dims_on_top [(kdim, tile_size)]--    , tilingTileShape = Shape [tile_size]-    , tilingNumWholeTiles = letSubExp "num_whole_tiles" $-                            BasicOp $ BinOp (SQuot Int32 Unsafe) w tile_size-    , tilingLevel = lvl-    , tilingSpace = space-    }--invariantToOneOfTwoInnerDims :: Names -> M.Map VName Names -> [VName] -> VName-                             -> Maybe [Int]-invariantToOneOfTwoInnerDims branch_variant variance dims arr = do-  j : i : _ <- Just $ reverse dims-  let variant_to = M.findWithDefault mempty arr variance-      branch_invariant = not $ nameIn j branch_variant || nameIn i branch_variant-  if branch_invariant && i `nameIn` variant_to && not (j `nameIn` variant_to) then-    Just [0,1]-  else if branch_invariant && j `nameIn` variant_to && not (i `nameIn` variant_to) then-    Just [1,0]-  else-    Nothing--segMap2D :: String-         -> SegLevel -> ResultManifest -> (SubExp, SubExp)-         -> ((VName, VName) -> Binder Kernels [SubExp])-         -> Binder Kernels [VName]-segMap2D desc lvl manifest (dim_x, dim_y) f = do-  ltid_x <- newVName "ltid_x"-  ltid_y <- newVName "ltid_y"-  ltid_flat <- newVName "ltid_flat"-  let space = SegSpace ltid_flat [(ltid_x, dim_x), (ltid_y, dim_y)]--  ((ts, res), stms) <- runBinder $ do-    res <- f (ltid_x, ltid_y)-    ts <- mapM subExpType res-    return (ts, res)-  Body _ stms' res' <- renameBody $ mkBody stms res--  letTupExp desc $ Op $ SegOp $-    SegMap lvl space ts $ KernelBody () stms' $ map (Returns manifest) res'---- Reconstruct the original gtids from group and local IDs.-reconstructGtids2D :: SubExp -> (VName, VName) -> (VName, VName) -> (VName, VName)-                   -> Binder Kernels ()-reconstructGtids2D tile_size (gtid_x, gtid_y) (gid_x, gid_y) (ltid_x, ltid_y) = do-  -- Reconstruct the original gtids from gid_x/gid_y and ltid_x/ltid_y.-  letBindNames [gtid_x] =<<-    toExp (LeafExp gid_x int32 * primExpFromSubExp int32 tile_size +-           LeafExp ltid_x int32)-  letBindNames [gtid_y] =<<-    toExp (LeafExp gid_y int32 * primExpFromSubExp int32 tile_size +-            LeafExp ltid_y int32)--readTile2D :: (SubExp, SubExp) -> (VName, VName) -> (VName, VName) -> SubExp-           -> Count NumGroups SubExp -> Count GroupSize SubExp-           -> TileKind -> PrivStms -> SubExp-           -> [(VName, [Int])]-           -> Binder Kernels [VName]-readTile2D (kdim_x, kdim_y) (gtid_x, gtid_y) (gid_x, gid_y) tile_size num_groups group_size kind privstms tile_id arrs_and_perms =-  segMap2D "full_tile" (SegThread num_groups group_size SegNoVirtFull)-  ResultNoSimplify (tile_size, tile_size) $ \(ltid_x, ltid_y) -> do-    i <- letSubExp "i" =<<-         toExp (primExpFromSubExp int32 tile_id *-                primExpFromSubExp int32 tile_size +-                LeafExp ltid_x int32)-    j <- letSubExp "j" =<<-         toExp (primExpFromSubExp int32 tile_id *-                primExpFromSubExp int32 tile_size +-                LeafExp ltid_y int32)--    reconstructGtids2D tile_size (gtid_x, gtid_y) (gid_x, gid_y) (ltid_x, ltid_y)-    addPrivStms [DimFix $ Var ltid_x, DimFix $ Var ltid_y] privstms--    let (arrs, perms) = unzip arrs_and_perms-    arr_ts <- mapM lookupType arrs-    let tile_ts = map rowType arr_ts-        w = arraysSize 0 arr_ts--    let readTileElem arr perm =-          -- No need for fullSlice because we are tiling only prims.-          letExp "tile_elem" $ BasicOp $ Index arr-          [DimFix $ last $ rearrangeShape perm [i,j]]-        readTileElemIfInBounds (tile_t, arr, perm) = do-          let idx = last $ rearrangeShape perm [i,j]-              othercheck = last $ rearrangeShape perm-                           [ LeafExp gtid_y int32 .<. primExpFromSubExp int32 kdim_y-                           , LeafExp gtid_x int32 .<. primExpFromSubExp int32 kdim_x-                           ]-          eIf (toExp $-               primExpFromSubExp int32 idx .<. primExpFromSubExp int32 w .&&. othercheck)-            (eBody [return $ BasicOp $ Index arr [DimFix idx]])-            (eBody [eBlank tile_t])--    fmap (map Var) $-      case kind of-        TilePartial ->-          mapM (letExp "pre" <=< readTileElemIfInBounds) (zip3 tile_ts arrs perms)-        TileFull ->-          zipWithM readTileElem arrs perms--processTile2D :: (VName, VName) -> (VName, VName) -> (SubExp, SubExp) -> SubExp-              -> Count NumGroups SubExp -> Count GroupSize SubExp-              -> PrivStms-              -> Commutativity -> Lambda Kernels -> Lambda Kernels-              -> [(VName,[Int])] -> [VName]-              -> Binder Kernels [VName]-processTile2D-  (gid_x, gid_y) (gtid_x, gtid_y) (kdim_x, kdim_y) tile_size num_groups group_size-  privstms red_comm red_lam map_lam tiles_and_perms accs = do--  -- Might be truncated in case of a partial tile.-  actual_tile_size <- arraysSize 0 <$> mapM (lookupType . fst) tiles_and_perms--  segMap2D "acc" (SegThread num_groups group_size SegNoVirtFull)-    ResultPrivate (tile_size, tile_size) $ \(ltid_x, ltid_y) -> do-    reconstructGtids2D tile_size (gtid_x, gtid_y) (gid_x, gid_y) (ltid_x, ltid_y)--    addPrivStms [DimFix $ Var ltid_x, DimFix $ Var ltid_y] privstms--    -- We replace the neutral elements with the accumulators (this is-    -- OK because the parallel semantics are not used after this-    -- point).-    thread_accs <- forM accs $ \acc ->-      letSubExp "acc" $ BasicOp $ Index acc [DimFix $ Var ltid_x, DimFix $ Var ltid_y]-    let form' = redomapSOAC [Reduce red_comm red_lam thread_accs] map_lam--    tiles' <- forM tiles_and_perms $ \(tile, perm) -> do-      tile_t <- lookupType tile-      letExp "tile" $ BasicOp $ Index tile $ sliceAt tile_t (head perm)-        [DimFix $ Var $ head $ rearrangeShape perm [ltid_x, ltid_y]]--    fmap (map Var) $-      letTupExp "acc" =<< eIf (toExp $-                               LeafExp gtid_x int32 .<. primExpFromSubExp int32 kdim_x .&&.-                               LeafExp gtid_y int32 .<. primExpFromSubExp int32 kdim_y)-      (eBody [pure $ Op $ OtherOp $ Screma actual_tile_size form' tiles'])-      (resultBodyM thread_accs)--processResidualTile2D :: (VName, VName) -> (VName, VName) -> (SubExp, SubExp) -> SubExp-                      -> Count NumGroups SubExp -> Count GroupSize SubExp -> PrivStms-                      -> Commutativity -> Lambda Kernels -> Lambda Kernels-                      -> SubExp -> [VName] -> SubExp -> [(VName, [Int])]-                      -> Binder Kernels [VName]-processResidualTile2D-  gids gtids kdims tile_size num_groups group_size privstms red_comm red_lam map_lam-  num_whole_tiles accs w arrs_and_perms = do-  -- The number of residual elements that are not covered by-  -- the whole tiles.-  residual_input <- letSubExp "residual_input" $-    BasicOp $ BinOp (SRem Int32 Unsafe) w tile_size--  letTupExp "acc_after_residual" =<<-    eIf (toExp $ primExpFromSubExp int32 residual_input .==. 0)-    (resultBodyM $ map Var accs)-    (nonemptyTile residual_input)--  where-    nonemptyTile residual_input = renameBody <=< runBodyBinder $ do-      -- Collectively construct a tile.  Threads that are out-of-bounds-      -- provide a blank dummy value.-      full_tile <- readTile2D kdims gtids gids tile_size num_groups group_size-                   TilePartial privstms num_whole_tiles arrs_and_perms--      tile <- forM full_tile $ \tile ->-        letExp "partial_tile" $ BasicOp $ Index tile-        [DimSlice (intConst Int32 0) residual_input (intConst Int32 1),-         DimSlice (intConst Int32 0) residual_input (intConst Int32 1)]--      -- Now each thread performs a traversal of the tile and-      -- updates its accumulator.-      resultBody . map Var <$>-        processTile2D gids gtids kdims tile_size num_groups group_size-        privstms red_comm red_lam map_lam-        (zip tile (map snd arrs_and_perms)) accs--tiling2d :: [(VName,SubExp)] -> DoTiling (VName, VName) (SubExp, SubExp)-tiling2d dims_on_top _initial_lvl (gtid_x, gtid_y) (kdim_x, kdim_y) w = do-  gid_x <- newVName "gid_x"-  gid_y <- newVName "gid_y"--  tile_size_key <- nameFromString . pretty <$> newVName "tile_size"-  tile_size <- letSubExp "tile_size" $ Op $ SizeOp $ GetSize tile_size_key SizeTile-  group_size <- letSubExp "group_size" $ BasicOp $ BinOp (Mul Int32 OverflowUndef) tile_size tile_size--  num_groups_x <- letSubExp "num_groups_x" $-                  BasicOp $ BinOp (SDivUp Int32 Unsafe) kdim_x tile_size-  num_groups_y <- letSubExp "num_groups_y" $-                  BasicOp $ BinOp (SDivUp Int32 Unsafe) kdim_y tile_size--  num_groups <- letSubExp "num_groups_top" =<<-                foldBinOp (Mul Int32 OverflowUndef) num_groups_x-                (num_groups_y : map snd dims_on_top)--  gid_flat <- newVName "gid_flat"-  let lvl = SegGroup (Count num_groups) (Count group_size) SegNoVirtFull-      space = SegSpace gid_flat $-              dims_on_top ++ [(gid_x, num_groups_x), (gid_y, num_groups_y)]--  return Tiling-    { tilingSegMap = \desc lvl' manifest f ->-        segMap2D desc lvl' manifest (tile_size, tile_size) $ \(ltid_x, ltid_y) -> do-        reconstructGtids2D tile_size (gtid_x, gtid_y) (gid_x, gid_y) (ltid_x, ltid_y)-        f (LeafExp gtid_x int32 .<. primExpFromSubExp int32 kdim_x .&&.-           LeafExp gtid_y int32 .<. primExpFromSubExp int32 kdim_y)-          [DimFix $ Var ltid_x, DimFix $ Var ltid_y]--    , tilingReadTile = readTile2D (kdim_x, kdim_y) (gtid_x, gtid_y) (gid_x, gid_y) tile_size (segNumGroups lvl) (segGroupSize lvl)-    , tilingProcessTile = processTile2D (gid_x, gid_y) (gtid_x, gtid_y) (kdim_x, kdim_y) tile_size (segNumGroups lvl) (segGroupSize lvl)-    , tilingProcessResidualTile = processResidualTile2D (gid_x, gid_y) (gtid_x, gtid_y) (kdim_x, kdim_y) tile_size (segNumGroups lvl) (segGroupSize lvl)--    , tilingTileReturns = tileReturns dims_on_top [(kdim_x, tile_size), (kdim_y, tile_size)]--    , tilingTileShape = Shape [tile_size, tile_size]-    , tilingNumWholeTiles = letSubExp "num_whole_tiles" $-                            BasicOp $ BinOp (SQuot Int32 Unsafe) w tile_size-    , tilingLevel = lvl-    , tilingSpace = space-    }---- | The variance table keeps a mapping from a variable name--- (something produced by a 'Stm') to the kernel thread indices--- that name depends on.  If a variable is not present in this table,--- that means it is bound outside the kernel (and so can be considered--- invariant to all dimensions).-type VarianceTable = M.Map VName Names--varianceInStms :: VarianceTable -> Stms Kernels -> VarianceTable-varianceInStms = foldl varianceInStm--varianceInStm :: VarianceTable -> Stm Kernels -> VarianceTable-varianceInStm variance bnd =-  foldl' add variance $ patternNames $ stmPattern bnd-  where add variance' v = M.insert v binding_variance variance'-        look variance' v = oneName v <> M.findWithDefault mempty v variance'-        binding_variance = mconcat $ map (look variance) $ namesToList (freeIn bnd)++-- | Perform a restricted form of loop tiling within SegMaps.  We only+-- tile primitive types, to avoid excessive local memory use.+module Futhark.Optimise.TileLoops (tileLoops) where++import Control.Monad.Reader+import Control.Monad.State+import Data.List (foldl')+import qualified Data.Map.Strict as M+import qualified Data.Sequence as Seq+import Futhark.IR.Kernels+import Futhark.MonadFreshNames+import Futhark.Pass+import Futhark.Tools+import Futhark.Transform.Rename+import Prelude hiding (quot)++-- | The pass definition.+tileLoops :: Pass Kernels Kernels+tileLoops =+  Pass "tile loops" "Tile stream loops inside kernels" $+    intraproceduralTransformation onStms+  where+    onStms scope stms =+      modifyNameSource $+        runState $+          runReaderT (optimiseStms stms) scope++type TileM = ReaderT (Scope Kernels) (State VNameSource)++optimiseBody :: Body Kernels -> TileM (Body Kernels)+optimiseBody (Body () stms res) =+  Body () <$> optimiseStms stms <*> pure res++optimiseStms :: Stms Kernels -> TileM (Stms Kernels)+optimiseStms stms =+  localScope (scopeOf stms) $+    mconcat <$> mapM optimiseStm (stmsToList stms)++optimiseStm :: Stm Kernels -> TileM (Stms Kernels)+optimiseStm (Let pat aux (Op (SegOp (SegMap lvl@SegThread {} space ts kbody)))) = do+  (host_stms, (lvl', space', kbody')) <- tileInKernelBody mempty initial_variance lvl space ts kbody+  return $+    host_stms+      <> oneStm (Let pat aux $ Op $ SegOp $ SegMap lvl' space' ts kbody')+  where+    initial_variance = M.map mempty $ scopeOfSegSpace space+optimiseStm (Let pat aux e) =+  pure <$> (Let pat aux <$> mapExpM optimise e)+  where+    optimise = identityMapper {mapOnBody = \scope -> localScope scope . optimiseBody}++tileInKernelBody ::+  Names ->+  VarianceTable ->+  SegLevel ->+  SegSpace ->+  [Type] ->+  KernelBody Kernels ->+  TileM (Stms Kernels, (SegLevel, SegSpace, KernelBody Kernels))+tileInKernelBody branch_variant initial_variance lvl initial_kspace ts kbody+  | Just kbody_res <- mapM isSimpleResult $ kernelBodyResult kbody = do+    maybe_tiled <-+      tileInBody branch_variant mempty initial_variance lvl initial_kspace ts $+        Body () (kernelBodyStms kbody) kbody_res+    case maybe_tiled of+      Just (host_stms, tiling, tiledBody) -> do+        (res', stms') <-+          runBinder $ mapM (tilingTileReturns tiling) =<< tiledBody mempty+        return+          ( host_stms,+            ( tilingLevel tiling,+              tilingSpace tiling,+              KernelBody () stms' res'+            )+          )+      Nothing ->+        return (mempty, (lvl, initial_kspace, kbody))+  | otherwise =+    return (mempty, (lvl, initial_kspace, kbody))+  where+    isSimpleResult (Returns _ se) = Just se+    isSimpleResult _ = Nothing++tileInBody ::+  Names ->+  Names ->+  VarianceTable ->+  SegLevel ->+  SegSpace ->+  [Type] ->+  Body Kernels ->+  TileM (Maybe (Stms Kernels, Tiling, TiledBody))+tileInBody branch_variant private initial_variance initial_lvl initial_space res_ts (Body () initial_kstms stms_res) =+  descend mempty $ stmsToList initial_kstms+  where+    variance = varianceInStms initial_variance initial_kstms++    descend _ [] =+      return Nothing+    descend prestms (stm_to_tile : poststms)+      -- 1D tiling of redomap.+      | (gtid, kdim) : top_space_rev <- reverse $ unSegSpace initial_space,+        Just (w, arrs, form) <- tileable stm_to_tile,+        not $+          any+            ( nameIn gtid+                . flip (M.findWithDefault mempty) variance+            )+            arrs,+        not $ gtid `nameIn` branch_variant,+        (prestms', poststms') <-+          preludeToPostlude variance prestms stm_to_tile (stmsFromList poststms),+        used <- freeIn stm_to_tile <> freeIn poststms' <> freeIn stms_res =+        Just . injectPrelude initial_space private variance prestms' used+          <$> tileGeneric+            (tiling1d $ reverse top_space_rev)+            initial_lvl+            res_ts+            (stmPattern stm_to_tile)+            gtid+            kdim+            w+            form+            (zip arrs $ repeat [0])+            poststms'+            stms_res+      -- 2D tiling of redomap.+      | (gtids, kdims) <- unzip $ unSegSpace initial_space,+        Just (w, arrs, form) <- tileable stm_to_tile,+        Just inner_perm <- mapM (invariantToOneOfTwoInnerDims branch_variant variance gtids) arrs,+        gtid_y : gtid_x : top_gtids_rev <- reverse gtids,+        kdim_y : kdim_x : top_kdims_rev <- reverse kdims,+        (prestms', poststms') <-+          preludeToPostlude variance prestms stm_to_tile (stmsFromList poststms),+        used <- freeIn stm_to_tile <> freeIn poststms' <> freeIn stms_res =+        Just . injectPrelude initial_space private variance prestms' used+          <$> tileGeneric+            (tiling2d $ reverse $ zip top_gtids_rev top_kdims_rev)+            initial_lvl+            res_ts+            (stmPattern stm_to_tile)+            (gtid_x, gtid_y)+            (kdim_x, kdim_y)+            w+            form+            (zip arrs inner_perm)+            poststms'+            stms_res+      -- Tiling inside for-loop.+      | DoLoop [] merge (ForLoop i it bound []) loopbody <- stmExp stm_to_tile,+        (prestms', poststms') <-+          preludeToPostlude variance prestms stm_to_tile (stmsFromList poststms) = do+        let branch_variant' =+              branch_variant+                <> mconcat+                  ( map+                      (flip (M.findWithDefault mempty) variance)+                      (namesToList (freeIn bound))+                  )+            merge_params = map fst merge+            private' = namesFromList $ map paramName merge_params++        maybe_tiled <-+          localScope (M.insert i (IndexName it) $ scopeOfFParams merge_params) $+            tileInBody+              branch_variant'+              private'+              variance+              initial_lvl+              initial_space+              (map paramType merge_params)+              $ mkBody (bodyStms loopbody) (bodyResult loopbody)++        case maybe_tiled of+          Nothing -> next+          Just tiled ->+            Just+              <$> tileDoLoop+                initial_space+                variance+                prestms'+                (freeIn loopbody <> freeIn merge)+                tiled+                res_ts+                (stmPattern stm_to_tile)+                (stmAux stm_to_tile)+                merge+                i+                it+                bound+                poststms'+                stms_res+      | otherwise = next+      where+        next =+          localScope (scopeOf stm_to_tile) $+            descend (prestms <> oneStm stm_to_tile) poststms++-- | Move statements from prelude to postlude if they are not used in+-- the tiled statement anyway.+preludeToPostlude ::+  VarianceTable ->+  Stms Kernels ->+  Stm Kernels ->+  Stms Kernels ->+  (Stms Kernels, Stms Kernels)+preludeToPostlude variance prelude stm_to_tile postlude =+  (prelude_used, prelude_not_used <> postlude)+  where+    used_in_tiled = freeIn stm_to_tile++    used_in_stm_variant =+      (used_in_tiled <>) $+        mconcat $+          map (flip (M.findWithDefault mempty) variance) $+            namesToList used_in_tiled++    used stm =+      any (`nameIn` used_in_stm_variant) $+        patternNames $ stmPattern stm++    (prelude_used, prelude_not_used) =+      Seq.partition used prelude++-- | Partition prelude statements preceding a tiled loop (or something+-- containing a tiled loop) into three categories:+--+-- 1) Group-level statements that are invariant to the threads in the group.+--+-- 2) Thread-variant statements that should be computed once with a segmap_thread_scalar.+--+-- 3) Thread-variant statements that should be recomputed whenever+-- they are needed.+--+-- The third category duplicates computation, so we only want to do it+-- when absolutely necessary.  Currently, this is necessary for+-- results that are views of an array (slicing, rotate, etc), because+-- these cannot be efficiently represented by a scalar segmap (they'll+-- be manifested in memory).+partitionPrelude ::+  VarianceTable ->+  Stms Kernels ->+  Names ->+  (Stms Kernels, Stms Kernels, Stms Kernels)+partitionPrelude variance prestms private =+  (invariant_prestms, precomputed_variant_prestms, recomputed_variant_prestms)+  where+    invariantTo names stm =+      case patternNames (stmPattern stm) of+        [] -> True -- Does not matter.+        v : _ ->+          not $+            any (`nameIn` names) $+              namesToList $+                M.findWithDefault mempty v variance+    (invariant_prestms, variant_prestms) =+      Seq.partition (invariantTo private) prestms++    mustBeInlinedExp (BasicOp (Index _ slice)) = not $ null $ sliceDims slice+    mustBeInlinedExp (BasicOp Rotate {}) = True+    mustBeInlinedExp (BasicOp Rearrange {}) = True+    mustBeInlinedExp (BasicOp Reshape {}) = True+    mustBeInlinedExp _ = False+    mustBeInlined = mustBeInlinedExp . stmExp++    must_be_inlined =+      namesFromList $+        concatMap (patternNames . stmPattern) $+          stmsToList $ Seq.filter mustBeInlined variant_prestms+    recompute stm =+      any (`nameIn` must_be_inlined) (patternNames (stmPattern stm))+        || not (invariantTo must_be_inlined stm)+    (recomputed_variant_prestms, precomputed_variant_prestms) =+      Seq.partition recompute variant_prestms++-- Anything that is variant to the "private" names should be+-- considered thread-local.+injectPrelude ::+  SegSpace ->+  Names ->+  VarianceTable ->+  Stms Kernels ->+  Names ->+  (Stms Kernels, Tiling, TiledBody) ->+  (Stms Kernels, Tiling, TiledBody)+injectPrelude initial_space private variance prestms used (host_stms, tiling, tiledBody) =+  (host_stms, tiling, tiledBody')+  where+    private' =+      private+        <> namesFromList+          ( map fst $+              filter (`notElem` unSegSpace (tilingSpace tiling)) $+                unSegSpace initial_space+          )++    tiledBody' privstms = do+      let ( invariant_prestms,+            precomputed_variant_prestms,+            recomputed_variant_prestms+            ) =+              partitionPrelude variance prestms private'++      addStms invariant_prestms++      let live_set =+            namesToList $+              liveSet precomputed_variant_prestms $+                used <> freeIn recomputed_variant_prestms+      prelude_arrs <-+        inScopeOf precomputed_variant_prestms $+          doPrelude tiling precomputed_variant_prestms live_set++      let prelude_privstms =+            PrivStms recomputed_variant_prestms $+              mkReadPreludeValues prelude_arrs live_set++      tiledBody (prelude_privstms <> privstms)++tileDoLoop ::+  SegSpace ->+  VarianceTable ->+  Stms Kernels ->+  Names ->+  (Stms Kernels, Tiling, TiledBody) ->+  [Type] ->+  Pattern Kernels ->+  StmAux (ExpDec Kernels) ->+  [(FParam Kernels, SubExp)] ->+  VName ->+  IntType ->+  SubExp ->+  Stms Kernels ->+  Result ->+  TileM (Stms Kernels, Tiling, TiledBody)+tileDoLoop initial_space variance prestms used_in_body (host_stms, tiling, tiledBody) res_ts pat aux merge i it bound poststms poststms_res = do+  let ( invariant_prestms,+        precomputed_variant_prestms,+        recomputed_variant_prestms+        ) =+          partitionPrelude variance prestms tiled_kdims++  let (mergeparams, mergeinits) = unzip merge++      -- Expand the loop merge parameters to be arrays.+      tileDim t = arrayOf t (tilingTileShape tiling) $ uniqueness t++      tiledBody' privstms = inScopeOf host_stms $ do+        addStms invariant_prestms++        let live_set =+              namesToList $+                liveSet precomputed_variant_prestms $+                  freeIn recomputed_variant_prestms+                    <> used_in_body+                    <> freeIn poststms++        prelude_arrs <-+          inScopeOf precomputed_variant_prestms $+            doPrelude tiling precomputed_variant_prestms live_set++        mergeparams' <- forM mergeparams $ \(Param pname pt) ->+          Param <$> newVName (baseString pname ++ "_group") <*> pure (tileDim pt)++        let merge_ts = map paramType mergeparams++        let inloop_privstms =+              PrivStms recomputed_variant_prestms $+                mkReadPreludeValues prelude_arrs live_set++        mergeinit' <-+          fmap (map Var) $+            certifying (stmAuxCerts aux) $+              tilingSegMap tiling "tiled_loopinit" (scalarLevel tiling) ResultPrivate $+                \in_bounds slice ->+                  fmap (map Var) $+                    protectOutOfBounds "loopinit" in_bounds merge_ts $ do+                      addPrivStms slice inloop_privstms+                      addPrivStms slice privstms+                      return mergeinits++        let merge' = zip mergeparams' mergeinit'++        let indexMergeParams slice =+              localScope (scopeOfFParams mergeparams') $+                forM_ (zip mergeparams mergeparams') $ \(to, from) ->+                  letBindNames [paramName to] $+                    BasicOp $+                      Index (paramName from) $+                        fullSlice (paramType from) slice++        loopbody' <-+          runBodyBinder $+            resultBody . map Var+              <$> tiledBody (PrivStms mempty indexMergeParams <> privstms <> inloop_privstms)+        accs' <-+          letTupExp "tiled_inside_loop" $+            DoLoop [] merge' (ForLoop i it bound []) loopbody'++        postludeGeneric tiling inloop_privstms pat accs' poststms poststms_res res_ts++  return (host_stms, tiling, tiledBody')+  where+    tiled_kdims =+      namesFromList $+        map fst $+          filter (`notElem` unSegSpace (tilingSpace tiling)) $+            unSegSpace initial_space++doPrelude :: Tiling -> Stms Kernels -> [VName] -> Binder Kernels [VName]+doPrelude tiling prestms prestms_live =+  -- Create a SegMap that takes care of the prelude for every thread.+  tilingSegMap tiling "prelude" (scalarLevel tiling) ResultPrivate $+    \in_bounds _slice -> do+      ts <- mapM lookupType prestms_live+      fmap (map Var) $+        letTupExp "pre"+          =<< eIf+            (toExp in_bounds)+            ( do+                addStms prestms+                resultBodyM $ map Var prestms_live+            )+            (eBody $ map eBlank ts)++liveSet :: FreeIn a => Stms Kernels -> a -> Names+liveSet stms after =+  namesFromList (concatMap (patternNames . stmPattern) stms)+    `namesIntersection` freeIn after++tileable ::+  Stm Kernels ->+  Maybe+    ( SubExp,+      [VName],+      (Commutativity, Lambda Kernels, [SubExp], Lambda Kernels)+    )+tileable stm+  | Op (OtherOp (Screma w form arrs)) <- stmExp stm,+    Just (reds, map_lam) <- isRedomapSOAC form,+    Reduce red_comm red_lam red_nes <- singleReduce reds,+    lambdaReturnType map_lam == lambdaReturnType red_lam, -- No mapout arrays.+    not $ null arrs,+    all primType $ lambdaReturnType map_lam,+    all (primType . paramType) $ lambdaParams map_lam =+    Just (w, arrs, (red_comm, red_lam, red_nes, map_lam))+  | otherwise =+    Nothing++-- | Statements that we insert directly into every thread-private+-- SegMaps.  This is for things that cannot efficiently be computed+-- once in advance in the prelude SegMap, primarily (exclusively?)+-- array slicing operations.+data PrivStms = PrivStms (Stms Kernels) ReadPrelude++privStms :: Stms Kernels -> PrivStms+privStms stms = PrivStms stms $ const $ return ()++addPrivStms :: Slice SubExp -> PrivStms -> Binder Kernels ()+addPrivStms local_slice (PrivStms stms readPrelude) = do+  readPrelude local_slice+  addStms stms++instance Semigroup PrivStms where+  PrivStms stms_x readPrelude_x <> PrivStms stms_y readPrelude_y =+    PrivStms stms_z readPrelude_z+    where+      stms_z = stms_x <> stms_y+      readPrelude_z slice = readPrelude_x slice >> readPrelude_y slice++instance Monoid PrivStms where+  mempty = privStms mempty++type ReadPrelude = Slice SubExp -> Binder Kernels ()++-- | Information about a loop that has been tiled inside a kernel, as+-- well as the kinds of changes that we would then like to perform on+-- the kernel.+data Tiling = Tiling+  { tilingSegMap ::+      String ->+      SegLevel ->+      ResultManifest ->+      (PrimExp VName -> Slice SubExp -> Binder Kernels [SubExp]) ->+      Binder Kernels [VName],+    -- The boolean PrimExp indicates whether they are in-bounds.++    tilingReadTile ::+      TileKind ->+      PrivStms ->+      SubExp ->+      [(VName, [Int])] ->+      Binder Kernels [VName],+    tilingProcessTile ::+      PrivStms ->+      Commutativity ->+      Lambda Kernels ->+      Lambda Kernels ->+      [(VName, [Int])] ->+      [VName] ->+      Binder Kernels [VName],+    tilingProcessResidualTile ::+      PrivStms ->+      Commutativity ->+      Lambda Kernels ->+      Lambda Kernels ->+      SubExp ->+      [VName] ->+      SubExp ->+      [(VName, [Int])] ->+      Binder Kernels [VName],+    tilingTileReturns :: VName -> Binder Kernels KernelResult,+    tilingSpace :: SegSpace,+    tilingTileShape :: Shape,+    tilingLevel :: SegLevel,+    tilingNumWholeTiles :: Binder Kernels SubExp+  }++type DoTiling gtids kdims =+  SegLevel -> gtids -> kdims -> SubExp -> Binder Kernels Tiling++scalarLevel :: Tiling -> SegLevel+scalarLevel tiling =+  SegThread (segNumGroups lvl) (segGroupSize lvl) SegNoVirt+  where+    lvl = tilingLevel tiling++protectOutOfBounds ::+  String ->+  PrimExp VName ->+  [Type] ->+  Binder Kernels [SubExp] ->+  Binder Kernels [VName]+protectOutOfBounds desc in_bounds ts m =+  letTupExp desc =<< eIf (toExp in_bounds) (resultBody <$> m) (eBody $ map eBlank ts)++postludeGeneric ::+  Tiling ->+  PrivStms ->+  Pattern Kernels ->+  [VName] ->+  Stms Kernels ->+  Result ->+  [Type] ->+  Binder Kernels [VName]+postludeGeneric tiling privstms pat accs' poststms poststms_res res_ts =+  tilingSegMap tiling "thread_res" (scalarLevel tiling) ResultPrivate $ \in_bounds slice -> do+    -- Read our per-thread result from the tiled loop.+    forM_ (zip (patternNames pat) accs') $ \(us, everyone) -> do+      everyone_t <- lookupType everyone+      letBindNames [us] $ BasicOp $ Index everyone $ fullSlice everyone_t slice++    if poststms == mempty+      then do+        -- The privstms may still be necessary for the result.+        addPrivStms slice privstms+        return poststms_res+      else fmap (map Var) $+        protectOutOfBounds "postlude" in_bounds res_ts $ do+          addPrivStms slice privstms+          addStms poststms+          return poststms_res++type TiledBody = PrivStms -> Binder Kernels [VName]++tileGeneric ::+  DoTiling gtids kdims ->+  SegLevel ->+  [Type] ->+  Pattern Kernels ->+  gtids ->+  kdims ->+  SubExp ->+  (Commutativity, Lambda Kernels, [SubExp], Lambda Kernels) ->+  [(VName, [Int])] ->+  Stms Kernels ->+  Result ->+  TileM (Stms Kernels, Tiling, TiledBody)+tileGeneric doTiling initial_lvl res_ts pat gtids kdims w form arrs_and_perms poststms poststms_res = do+  (tiling, tiling_stms) <- runBinder $ doTiling initial_lvl gtids kdims w++  return (tiling_stms, tiling, tiledBody tiling)+  where+    (red_comm, red_lam, red_nes, map_lam) = form++    tiledBody :: Tiling -> PrivStms -> Binder Kernels [VName]+    tiledBody tiling privstms = do+      let tile_shape = tilingTileShape tiling++      num_whole_tiles <- tilingNumWholeTiles tiling++      -- We don't use a Replicate here, because we want to enforce a+      -- scalar memory space.+      mergeinits <- tilingSegMap tiling "mergeinit" (scalarLevel tiling) ResultPrivate $ \in_bounds slice ->+        -- Constant neutral elements (a common case) do not need protection from OOB.+        if freeIn red_nes == mempty+          then return red_nes+          else fmap (map Var) $+            protectOutOfBounds "neutral" in_bounds (lambdaReturnType red_lam) $ do+              addPrivStms slice privstms+              return red_nes++      merge <- forM (zip (lambdaParams red_lam) mergeinits) $ \(p, mergeinit) ->+        (,)+          <$> newParam+            (baseString (paramName p) ++ "_merge")+            (paramType p `arrayOfShape` tile_shape `toDecl` Unique)+          <*> pure (Var mergeinit)++      tile_id <- newVName "tile_id"+      let loopform = ForLoop tile_id Int64 num_whole_tiles []+      loopbody <- renameBody <=< runBodyBinder $+        inScopeOf loopform $+          localScope (scopeOfFParams $ map fst merge) $ do+            -- Collectively read a tile.+            tile <- tilingReadTile tiling TilePartial privstms (Var tile_id) arrs_and_perms++            -- Now each thread performs a traversal of the tile and+            -- updates its accumulator.+            resultBody . map Var+              <$> tilingProcessTile+                tiling+                privstms+                red_comm+                red_lam+                map_lam+                (zip tile (map snd arrs_and_perms))+                (map (paramName . fst) merge)++      accs <- letTupExp "accs" $ DoLoop [] merge loopform loopbody++      -- We possibly have to traverse a residual tile.+      red_lam' <- renameLambda red_lam+      map_lam' <- renameLambda map_lam+      accs' <-+        tilingProcessResidualTile+          tiling+          privstms+          red_comm+          red_lam'+          map_lam'+          num_whole_tiles+          accs+          w+          arrs_and_perms++      -- Create a SegMap that takes care of the postlude for every thread.+      postludeGeneric tiling privstms pat accs' poststms poststms_res res_ts++data TileKind = TilePartial | TileFull++mkReadPreludeValues :: [VName] -> [VName] -> ReadPrelude+mkReadPreludeValues prestms_live_arrs prestms_live slice =+  fmap mconcat $+    forM (zip prestms_live_arrs prestms_live) $ \(arr, v) -> do+      arr_t <- lookupType arr+      letBindNames [v] $ BasicOp $ Index arr $ fullSlice arr_t slice++tileReturns :: [(VName, SubExp)] -> [(SubExp, SubExp)] -> VName -> Binder Kernels KernelResult+tileReturns dims_on_top dims arr = do+  let unit_dims = replicate (length dims_on_top) (intConst Int64 1)+  arr' <-+    if null dims_on_top+      then return arr+      else do+        arr_t <- lookupType arr+        let new_shape = unit_dims ++ arrayDims arr_t+        letExp (baseString arr) $ BasicOp $ Reshape (map DimNew new_shape) arr+  let tile_dims = zip (map snd dims_on_top) unit_dims ++ dims+  return $ TileReturns tile_dims arr'++segMap1D ::+  String ->+  SegLevel ->+  ResultManifest ->+  (VName -> Binder Kernels [SubExp]) ->+  Binder Kernels [VName]+segMap1D desc lvl manifest f = do+  ltid <- newVName "ltid"+  ltid_flat <- newVName "ltid_flat"+  let space = SegSpace ltid_flat [(ltid, unCount $ segGroupSize lvl)]++  ((ts, res), stms) <- runBinder $ do+    res <- f ltid+    ts <- mapM subExpType res+    return (ts, res)+  Body _ stms' res' <- renameBody $ mkBody stms res++  letTupExp desc $+    Op $+      SegOp $+        SegMap lvl space ts $ KernelBody () stms' $ map (Returns manifest) res'++reconstructGtids1D ::+  Count GroupSize SubExp ->+  VName ->+  VName ->+  VName ->+  Binder Kernels ()+reconstructGtids1D group_size gtid gid ltid =+  letBindNames [gtid]+    =<< toExp (le64 gid * pe64 (unCount group_size) + le64 ltid)++readTile1D ::+  SubExp ->+  VName ->+  VName ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  TileKind ->+  PrivStms ->+  SubExp ->+  [(VName, [Int])] ->+  Binder Kernels [VName]+readTile1D+  tile_size+  gid+  gtid+  num_groups+  group_size+  kind+  privstms+  tile_id+  arrs_and_perms =+    segMap1D "full_tile" (SegThread num_groups group_size SegNoVirt) ResultNoSimplify $ \ltid -> do+      j <-+        letSubExp "j"+          =<< toExp (pe64 tile_id * pe64 tile_size + le64 ltid)++      reconstructGtids1D group_size gtid gid ltid+      addPrivStms [DimFix $ Var ltid] privstms++      let arrs = map fst arrs_and_perms+      arr_ts <- mapM lookupType arrs+      let tile_ts = map rowType arr_ts+          w = arraysSize 0 arr_ts++      let readTileElem arr =+            -- No need for fullSlice because we are tiling only prims.+            letExp "tile_elem" $ BasicOp $ Index arr [DimFix j]+      fmap (map Var) $+        case kind of+          TilePartial ->+            letTupExp "pre"+              =<< eIf+                (toExp $ pe64 j .<. pe64 w)+                (resultBody <$> mapM (fmap Var . readTileElem) arrs)+                (eBody $ map eBlank tile_ts)+          TileFull ->+            mapM readTileElem arrs++processTile1D ::+  VName ->+  VName ->+  SubExp ->+  SubExp ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  PrivStms ->+  Commutativity ->+  Lambda Kernels ->+  Lambda Kernels ->+  [(VName, [Int])] ->+  [VName] ->+  Binder Kernels [VName]+processTile1D+  gid+  gtid+  kdim+  tile_size+  num_groups+  group_size+  privstms+  red_comm+  red_lam+  map_lam+  tiles_and_perm+  accs = do+    let tile = map fst tiles_and_perm++    segMap1D "acc" (SegThread num_groups group_size SegNoVirt) ResultPrivate $ \ltid -> do+      reconstructGtids1D group_size gtid gid ltid+      addPrivStms [DimFix $ Var ltid] privstms++      -- We replace the neutral elements with the accumulators (this is+      -- OK because the parallel semantics are not used after this+      -- point).+      thread_accs <- forM accs $ \acc ->+        letSubExp "acc" $ BasicOp $ Index acc [DimFix $ Var ltid]+      let form' = redomapSOAC [Reduce red_comm red_lam thread_accs] map_lam++      fmap (map Var) $+        letTupExp "acc"+          =<< eIf+            (toExp $ le64 gtid .<. pe64 kdim)+            (eBody [pure $ Op $ OtherOp $ Screma tile_size form' tile])+            (resultBodyM thread_accs)++processResidualTile1D ::+  VName ->+  VName ->+  SubExp ->+  SubExp ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  PrivStms ->+  Commutativity ->+  Lambda Kernels ->+  Lambda Kernels ->+  SubExp ->+  [VName] ->+  SubExp ->+  [(VName, [Int])] ->+  Binder Kernels [VName]+processResidualTile1D+  gid+  gtid+  kdim+  tile_size+  num_groups+  group_size+  privstms+  red_comm+  red_lam+  map_lam+  num_whole_tiles+  accs+  w+  arrs_and_perms = do+    -- The number of residual elements that are not covered by+    -- the whole tiles.+    residual_input <-+      letSubExp "residual_input" $+        BasicOp $ BinOp (SRem Int64 Unsafe) w tile_size++    letTupExp "acc_after_residual"+      =<< eIf+        (toExp $ pe64 residual_input .==. 0)+        (resultBodyM $ map Var accs)+        (nonemptyTile residual_input)+    where+      nonemptyTile residual_input = runBodyBinder $ do+        -- Collectively construct a tile.  Threads that are out-of-bounds+        -- provide a blank dummy value.+        full_tile <-+          readTile1D+            tile_size+            gid+            gtid+            num_groups+            group_size+            TilePartial+            privstms+            num_whole_tiles+            arrs_and_perms+        tile <- forM full_tile $ \tile ->+          letExp "partial_tile" $+            BasicOp $+              Index+                tile+                [DimSlice (intConst Int64 0) residual_input (intConst Int64 1)]++        -- Now each thread performs a traversal of the tile and+        -- updates its accumulator.+        resultBody . map Var+          <$> processTile1D+            gid+            gtid+            kdim+            residual_input+            num_groups+            group_size+            privstms+            red_comm+            red_lam+            map_lam+            (zip tile $ repeat [0])+            accs++tiling1d :: [(VName, SubExp)] -> DoTiling VName SubExp+tiling1d dims_on_top initial_lvl gtid kdim w = do+  gid <- newVName "gid"+  gid_flat <- newVName "gid_flat"++  (lvl, space) <-+    if null dims_on_top+      then+        return+          ( SegGroup (segNumGroups initial_lvl) (segGroupSize initial_lvl) $ segVirt initial_lvl,+            SegSpace gid_flat [(gid, unCount $ segNumGroups initial_lvl)]+          )+      else do+        group_size <-+          letSubExp "computed_group_size" $+            BasicOp $ BinOp (SMin Int64) (unCount (segGroupSize initial_lvl)) kdim++        -- How many groups we need to exhaust the innermost dimension.+        ldim <-+          letSubExp "ldim" $+            BasicOp $ BinOp (SDivUp Int64 Unsafe) kdim group_size++        num_groups <-+          letSubExp "computed_num_groups"+            =<< foldBinOp (Mul Int64 OverflowUndef) ldim (map snd dims_on_top)++        return+          ( SegGroup (Count num_groups) (Count group_size) SegNoVirt,+            SegSpace gid_flat $ dims_on_top ++ [(gid, ldim)]+          )+  let tile_size = unCount $ segGroupSize lvl++  return+    Tiling+      { tilingSegMap = \desc lvl' manifest f -> segMap1D desc lvl' manifest $ \ltid -> do+          letBindNames [gtid]+            =<< toExp (le64 gid * pe64 tile_size + le64 ltid)+          f (untyped $ le64 gtid .<. pe64 kdim) [DimFix $ Var ltid],+        tilingReadTile =+          readTile1D tile_size gid gtid (segNumGroups lvl) (segGroupSize lvl),+        tilingProcessTile =+          processTile1D gid gtid kdim tile_size (segNumGroups lvl) (segGroupSize lvl),+        tilingProcessResidualTile =+          processResidualTile1D gid gtid kdim tile_size (segNumGroups lvl) (segGroupSize lvl),+        tilingTileReturns = tileReturns dims_on_top [(kdim, tile_size)],+        tilingTileShape = Shape [tile_size],+        tilingNumWholeTiles =+          letSubExp "num_whole_tiles" $+            BasicOp $ BinOp (SQuot Int64 Unsafe) w tile_size,+        tilingLevel = lvl,+        tilingSpace = space+      }++invariantToOneOfTwoInnerDims ::+  Names ->+  M.Map VName Names ->+  [VName] ->+  VName ->+  Maybe [Int]+invariantToOneOfTwoInnerDims branch_variant variance dims arr = do+  j : i : _ <- Just $ reverse dims+  let variant_to = M.findWithDefault mempty arr variance+      branch_invariant = not $ nameIn j branch_variant || nameIn i branch_variant+  if branch_invariant && i `nameIn` variant_to && not (j `nameIn` variant_to)+    then Just [0, 1]+    else+      if branch_invariant && j `nameIn` variant_to && not (i `nameIn` variant_to)+        then Just [1, 0]+        else Nothing++segMap2D ::+  String ->+  SegLevel ->+  ResultManifest ->+  (SubExp, SubExp) ->+  ((VName, VName) -> Binder Kernels [SubExp]) ->+  Binder Kernels [VName]+segMap2D desc lvl manifest (dim_x, dim_y) f = do+  ltid_x <- newVName "ltid_x"+  ltid_y <- newVName "ltid_y"+  ltid_flat <- newVName "ltid_flat"+  let space = SegSpace ltid_flat [(ltid_x, dim_x), (ltid_y, dim_y)]++  ((ts, res), stms) <- runBinder $ do+    res <- f (ltid_x, ltid_y)+    ts <- mapM subExpType res+    return (ts, res)+  Body _ stms' res' <- renameBody $ mkBody stms res++  letTupExp desc $+    Op $+      SegOp $+        SegMap lvl space ts $ KernelBody () stms' $ map (Returns manifest) res'++-- Reconstruct the original gtids from group and local IDs.+reconstructGtids2D ::+  SubExp ->+  (VName, VName) ->+  (VName, VName) ->+  (VName, VName) ->+  Binder Kernels ()+reconstructGtids2D tile_size (gtid_x, gtid_y) (gid_x, gid_y) (ltid_x, ltid_y) = do+  -- Reconstruct the original gtids from gid_x/gid_y and ltid_x/ltid_y.+  letBindNames [gtid_x]+    =<< toExp (le64 gid_x * pe64 tile_size + le64 ltid_x)+  letBindNames [gtid_y]+    =<< toExp (le64 gid_y * pe64 tile_size + le64 ltid_y)++readTile2D ::+  (SubExp, SubExp) ->+  (VName, VName) ->+  (VName, VName) ->+  SubExp ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  TileKind ->+  PrivStms ->+  SubExp ->+  [(VName, [Int])] ->+  Binder Kernels [VName]+readTile2D (kdim_x, kdim_y) (gtid_x, gtid_y) (gid_x, gid_y) tile_size num_groups group_size kind privstms tile_id arrs_and_perms =+  segMap2D+    "full_tile"+    (SegThread num_groups group_size SegNoVirtFull)+    ResultNoSimplify+    (tile_size, tile_size)+    $ \(ltid_x, ltid_y) -> do+      i <-+        letSubExp "i"+          =<< toExp (pe64 tile_id * pe64 tile_size + le64 ltid_x)+      j <-+        letSubExp "j"+          =<< toExp (pe64 tile_id * pe64 tile_size + le64 ltid_y)++      reconstructGtids2D tile_size (gtid_x, gtid_y) (gid_x, gid_y) (ltid_x, ltid_y)+      addPrivStms [DimFix $ Var ltid_x, DimFix $ Var ltid_y] privstms++      let (arrs, perms) = unzip arrs_and_perms+      arr_ts <- mapM lookupType arrs+      let tile_ts = map rowType arr_ts+          w = arraysSize 0 arr_ts++      let readTileElem arr perm =+            -- No need for fullSlice because we are tiling only prims.+            letExp "tile_elem" $+              BasicOp $+                Index+                  arr+                  [DimFix $ last $ rearrangeShape perm [i, j]]+          readTileElemIfInBounds (tile_t, arr, perm) = do+            let idx = last $ rearrangeShape perm [i, j]+                othercheck =+                  last $+                    rearrangeShape+                      perm+                      [ le64 gtid_y .<. pe64 kdim_y,+                        le64 gtid_x .<. pe64 kdim_x+                      ]+            eIf+              (toExp $ pe64 idx .<. pe64 w .&&. othercheck)+              (eBody [return $ BasicOp $ Index arr [DimFix idx]])+              (eBody [eBlank tile_t])++      fmap (map Var) $+        case kind of+          TilePartial ->+            mapM (letExp "pre" <=< readTileElemIfInBounds) (zip3 tile_ts arrs perms)+          TileFull ->+            zipWithM readTileElem arrs perms++processTile2D ::+  (VName, VName) ->+  (VName, VName) ->+  (SubExp, SubExp) ->+  SubExp ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  PrivStms ->+  Commutativity ->+  Lambda Kernels ->+  Lambda Kernels ->+  [(VName, [Int])] ->+  [VName] ->+  Binder Kernels [VName]+processTile2D+  (gid_x, gid_y)+  (gtid_x, gtid_y)+  (kdim_x, kdim_y)+  tile_size+  num_groups+  group_size+  privstms+  red_comm+  red_lam+  map_lam+  tiles_and_perms+  accs = do+    -- Might be truncated in case of a partial tile.+    actual_tile_size <- arraysSize 0 <$> mapM (lookupType . fst) tiles_and_perms++    segMap2D+      "acc"+      (SegThread num_groups group_size SegNoVirtFull)+      ResultPrivate+      (tile_size, tile_size)+      $ \(ltid_x, ltid_y) -> do+        reconstructGtids2D tile_size (gtid_x, gtid_y) (gid_x, gid_y) (ltid_x, ltid_y)++        addPrivStms [DimFix $ Var ltid_x, DimFix $ Var ltid_y] privstms++        -- We replace the neutral elements with the accumulators (this is+        -- OK because the parallel semantics are not used after this+        -- point).+        thread_accs <- forM accs $ \acc ->+          letSubExp "acc" $ BasicOp $ Index acc [DimFix $ Var ltid_x, DimFix $ Var ltid_y]+        let form' = redomapSOAC [Reduce red_comm red_lam thread_accs] map_lam++        tiles' <- forM tiles_and_perms $ \(tile, perm) -> do+          tile_t <- lookupType tile+          letExp "tile" $+            BasicOp $+              Index tile $+                sliceAt+                  tile_t+                  (head perm)+                  [DimFix $ Var $ head $ rearrangeShape perm [ltid_x, ltid_y]]++        fmap (map Var) $+          letTupExp "acc"+            =<< eIf+              ( toExp $ le64 gtid_x .<. pe64 kdim_x .&&. le64 gtid_y .<. pe64 kdim_y+              )+              (eBody [pure $ Op $ OtherOp $ Screma actual_tile_size form' tiles'])+              (resultBodyM thread_accs)++processResidualTile2D ::+  (VName, VName) ->+  (VName, VName) ->+  (SubExp, SubExp) ->+  SubExp ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  PrivStms ->+  Commutativity ->+  Lambda Kernels ->+  Lambda Kernels ->+  SubExp ->+  [VName] ->+  SubExp ->+  [(VName, [Int])] ->+  Binder Kernels [VName]+processResidualTile2D+  gids+  gtids+  kdims+  tile_size+  num_groups+  group_size+  privstms+  red_comm+  red_lam+  map_lam+  num_whole_tiles+  accs+  w+  arrs_and_perms = do+    -- The number of residual elements that are not covered by+    -- the whole tiles.+    residual_input <-+      letSubExp "residual_input" $+        BasicOp $ BinOp (SRem Int64 Unsafe) w tile_size++    letTupExp "acc_after_residual"+      =<< eIf+        (toExp $ pe64 residual_input .==. 0)+        (resultBodyM $ map Var accs)+        (nonemptyTile residual_input)+    where+      nonemptyTile residual_input = renameBody <=< runBodyBinder $ do+        -- Collectively construct a tile.  Threads that are out-of-bounds+        -- provide a blank dummy value.+        full_tile <-+          readTile2D+            kdims+            gtids+            gids+            tile_size+            num_groups+            group_size+            TilePartial+            privstms+            num_whole_tiles+            arrs_and_perms++        tile <- forM full_tile $ \tile ->+          letExp "partial_tile" $+            BasicOp $+              Index+                tile+                [ DimSlice (intConst Int64 0) residual_input (intConst Int64 1),+                  DimSlice (intConst Int64 0) residual_input (intConst Int64 1)+                ]++        -- Now each thread performs a traversal of the tile and+        -- updates its accumulator.+        resultBody . map Var+          <$> processTile2D+            gids+            gtids+            kdims+            tile_size+            num_groups+            group_size+            privstms+            red_comm+            red_lam+            map_lam+            (zip tile (map snd arrs_and_perms))+            accs++tiling2d :: [(VName, SubExp)] -> DoTiling (VName, VName) (SubExp, SubExp)+tiling2d dims_on_top _initial_lvl (gtid_x, gtid_y) (kdim_x, kdim_y) w = do+  gid_x <- newVName "gid_x"+  gid_y <- newVName "gid_y"++  tile_size_key <- nameFromString . pretty <$> newVName "tile_size"+  tile_size <- letSubExp "tile_size" $ Op $ SizeOp $ GetSize tile_size_key SizeTile+  group_size <- letSubExp "group_size" $ BasicOp $ BinOp (Mul Int64 OverflowUndef) tile_size tile_size++  num_groups_x <-+    letSubExp "num_groups_x" $+      BasicOp $ BinOp (SDivUp Int64 Unsafe) kdim_x tile_size+  num_groups_y <-+    letSubExp "num_groups_y" $+      BasicOp $ BinOp (SDivUp Int64 Unsafe) kdim_y tile_size++  num_groups <-+    letSubExp "num_groups_top"+      =<< foldBinOp+        (Mul Int64 OverflowUndef)+        num_groups_x+        (num_groups_y : map snd dims_on_top)++  gid_flat <- newVName "gid_flat"+  let lvl = SegGroup (Count num_groups) (Count group_size) SegNoVirtFull+      space =+        SegSpace gid_flat $+          dims_on_top ++ [(gid_x, num_groups_x), (gid_y, num_groups_y)]++  return+    Tiling+      { tilingSegMap = \desc lvl' manifest f ->+          segMap2D desc lvl' manifest (tile_size, tile_size) $ \(ltid_x, ltid_y) -> do+            reconstructGtids2D tile_size (gtid_x, gtid_y) (gid_x, gid_y) (ltid_x, ltid_y)+            f+              ( untyped $+                  le64 gtid_x .<. pe64 kdim_x+                    .&&. le64 gtid_y .<. pe64 kdim_y+              )+              [DimFix $ Var ltid_x, DimFix $ Var ltid_y],+        tilingReadTile = readTile2D (kdim_x, kdim_y) (gtid_x, gtid_y) (gid_x, gid_y) tile_size (segNumGroups lvl) (segGroupSize lvl),+        tilingProcessTile = processTile2D (gid_x, gid_y) (gtid_x, gtid_y) (kdim_x, kdim_y) tile_size (segNumGroups lvl) (segGroupSize lvl),+        tilingProcessResidualTile = processResidualTile2D (gid_x, gid_y) (gtid_x, gtid_y) (kdim_x, kdim_y) tile_size (segNumGroups lvl) (segGroupSize lvl),+        tilingTileReturns = tileReturns dims_on_top [(kdim_x, tile_size), (kdim_y, tile_size)],+        tilingTileShape = Shape [tile_size, tile_size],+        tilingNumWholeTiles =+          letSubExp "num_whole_tiles" $+            BasicOp $ BinOp (SQuot Int64 Unsafe) w tile_size,+        tilingLevel = lvl,+        tilingSpace = space+      }++-- | The variance table keeps a mapping from a variable name+-- (something produced by a 'Stm') to the kernel thread indices+-- that name depends on.  If a variable is not present in this table,+-- that means it is bound outside the kernel (and so can be considered+-- invariant to all dimensions).+type VarianceTable = M.Map VName Names++varianceInStms :: VarianceTable -> Stms Kernels -> VarianceTable+varianceInStms = foldl varianceInStm++varianceInStm :: VarianceTable -> Stm Kernels -> VarianceTable+varianceInStm variance bnd =+  foldl' add variance $ patternNames $ stmPattern bnd+  where+    add variance' v = M.insert v binding_variance variance'+    look variance' v = oneName v <> M.findWithDefault mempty v variance'+    binding_variance = mconcat $ map (look variance) $ namesToList (freeIn bnd)
src/Futhark/Optimise/Unstream.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}+ -- | Sequentialise any remaining SOACs.  It is very important that -- this is run *after* any access-pattern-related optimisation, -- because this pass will destroy information.@@ -19,12 +20,11 @@ -- kept together. module Futhark.Optimise.Unstream (unstream) where -import Control.Monad.State import Control.Monad.Reader--import Futhark.MonadFreshNames+import Control.Monad.State import Futhark.IR.Kernels import Futhark.IR.Kernels.Simplify (simplifyKernels)+import Futhark.MonadFreshNames import Futhark.Pass import Futhark.Tools import qualified Futhark.Transform.FirstOrderTransform as FOT@@ -33,19 +33,21 @@  -- | The pass definition. unstream :: Pass Kernels Kernels-unstream = Pass "unstream" "sequentialise remaining SOACs" $-           intraproceduralTransformation (optimise SeqStreams)-           >=> simplifyKernels-           >=> intraproceduralTransformation (optimise SeqAll)-  where optimise stage scope stms =-          modifyNameSource $ runState $ runReaderT (optimiseStms stage stms) scope+unstream =+  Pass "unstream" "sequentialise remaining SOACs" $+    intraproceduralTransformation (optimise SeqStreams)+      >=> simplifyKernels+      >=> intraproceduralTransformation (optimise SeqAll)+  where+    optimise stage scope stms =+      modifyNameSource $ runState $ runReaderT (optimiseStms stage stms) scope  type UnstreamM = ReaderT (Scope Kernels) (State VNameSource)  optimiseStms :: Stage -> Stms Kernels -> UnstreamM (Stms Kernels) optimiseStms stage stms =   localScope (scopeOf stms) $-  stmsFromList . concat <$> mapM (optimiseStm stage) (stmsToList stms)+    stmsFromList . concat <$> mapM (optimiseStm stage) (stmsToList stms)  optimiseBody :: Stage -> Body Kernels -> UnstreamM (Body Kernels) optimiseBody stage (Body () stms res) =@@ -54,38 +56,40 @@ optimiseKernelBody :: Stage -> KernelBody Kernels -> UnstreamM (KernelBody Kernels) optimiseKernelBody stage (KernelBody () stms res) =   localScope (scopeOf stms) $-  KernelBody ()-  <$> (stmsFromList . concat <$> mapM (optimiseStm stage) (stmsToList stms))-  <*> pure res+    KernelBody ()+      <$> (stmsFromList . concat <$> mapM (optimiseStm stage) (stmsToList stms))+      <*> pure res  optimiseLambda :: Stage -> Lambda Kernels -> UnstreamM (Lambda Kernels) optimiseLambda stage lam = localScope (scopeOfLParams $ lambdaParams lam) $ do   body <- optimiseBody stage $ lambdaBody lam-  return lam { lambdaBody = body }+  return lam {lambdaBody = body}  sequentialise :: Stage -> SOAC Kernels -> Bool-sequentialise SeqStreams Stream{} = True+sequentialise SeqStreams Stream {} = True sequentialise SeqStreams _ = False sequentialise SeqAll _ = True  optimiseStm :: Stage -> Stm Kernels -> UnstreamM [Stm Kernels]- optimiseStm stage (Let pat aux (Op (OtherOp soac)))   | sequentialise stage soac = do-      stms <- runBinder_ $ FOT.transformSOAC pat soac-      fmap concat $ localScope (scopeOf stms) $ mapM (optimiseStm stage) $ stmsToList stms-  | otherwise = do-      -- Still sequentialise whatever's inside.-      pure <$> (Let pat aux . Op . OtherOp <$> mapSOACM optimise soac)-        where optimise = identitySOACMapper { mapOnSOACLambda = optimiseLambda stage }-+    stms <- runBinder_ $ FOT.transformSOAC pat soac+    fmap concat $ localScope (scopeOf stms) $ mapM (optimiseStm stage) $ stmsToList stms+  | otherwise =+    -- Still sequentialise whatever's inside.+    pure <$> (Let pat aux . Op . OtherOp <$> mapSOACM optimise soac)+  where+    optimise = identitySOACMapper {mapOnSOACLambda = optimiseLambda stage} optimiseStm stage (Let pat aux (Op (SegOp op))) =   localScope (scopeOfSegSpace $ segSpace op) $-  pure <$> (Let pat aux . Op . SegOp <$> mapSegOpM optimise op)-  where optimise = identitySegOpMapper { mapOnSegOpBody = optimiseKernelBody stage-                                       , mapOnSegOpLambda = optimiseLambda stage-                                       }-+    pure <$> (Let pat aux . Op . SegOp <$> mapSegOpM optimise op)+  where+    optimise =+      identitySegOpMapper+        { mapOnSegOpBody = optimiseKernelBody stage,+          mapOnSegOpLambda = optimiseLambda stage+        } optimiseStm stage (Let pat aux e) =   pure <$> (Let pat aux <$> mapExpM optimise e)-  where optimise = identityMapper { mapOnBody = \scope -> localScope scope . optimiseBody stage }+  where+    optimise = identityMapper {mapOnBody = \scope -> localScope scope . optimiseBody stage}
src/Futhark/Pass.hs view
@@ -1,36 +1,36 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE Strict #-}+ -- | Definition of a polymorphic (generic) pass that can work with -- programs of any lore. module Futhark.Pass-       ( PassM-       , runPassM-       , liftEither-       , liftEitherM-       , Pass (..)-       , passLongOption-       , parPass-       , intraproceduralTransformation-       , intraproceduralTransformationWithConsts-       ) where+  ( PassM,+    runPassM,+    liftEither,+    liftEitherM,+    Pass (..),+    passLongOption,+    parPass,+    intraproceduralTransformation,+    intraproceduralTransformationWithConsts,+  )+where -import Control.Monad.Writer.Strict import Control.Monad.State.Strict+import Control.Monad.Writer.Strict import Control.Parallel.Strategies import Data.Char import Data.Either--import Prelude hiding (log)- import Futhark.Error import Futhark.IR-import Futhark.Util.Log import Futhark.MonadFreshNames+import Futhark.Util.Log+import Prelude hiding (log)  -- | The monad in which passes execute. newtype PassM a = PassM (WriterT Log (State VNameSource) a)-              deriving (Functor, Applicative, Monad)+  deriving (Functor, Applicative, Monad)  instance MonadLogger PassM where   addLog = PassM . tell@@ -41,14 +41,16 @@  -- | Execute a 'PassM' action, yielding logging information and either -- an error text or a result.-runPassM :: MonadFreshNames m =>-            PassM a -> m (a, Log)+runPassM ::+  MonadFreshNames m =>+  PassM a ->+  m (a, Log) runPassM (PassM m) = modifyNameSource $ runState (runWriterT m)  -- | Turn an 'Either' computation into a 'PassM'.  If the 'Either' is -- 'Left', the result is a 'CompilerBug'. liftEither :: Show err => Either err a -> PassM a-liftEither (Left e)  = compilerBugS $ show e+liftEither (Left e) = compilerBugS $ show e liftEither (Right v) = return v  -- | Turn an 'Either' monadic computation into a 'PassM'.  If the 'Either' is@@ -58,23 +60,24 @@  -- | A compiler pass transforming a 'Prog' of a given lore to a 'Prog' -- of another lore.-data Pass fromlore tolore =-  Pass { passName :: String-         -- ^ Name of the pass.  Keep this short and simple.  It will-         -- be used to automatically generate a command-line option-         -- name via 'passLongOption'.-       , passDescription :: String-         -- ^ A slightly longer description, which will show up in the-         -- command-line help text.-       , passFunction :: Prog fromlore -> PassM (Prog tolore)-       }+data Pass fromlore tolore = Pass+  { -- | Name of the pass.  Keep this short and simple.  It will+    -- be used to automatically generate a command-line option+    -- name via 'passLongOption'.+    passName :: String,+    -- | A slightly longer description, which will show up in the+    -- command-line help text.+    passDescription :: String,+    passFunction :: Prog fromlore -> PassM (Prog tolore)+  }  -- | Take the name of the pass, turn spaces into dashes, and make all -- characters lowercase. passLongOption :: Pass fromlore tolore -> String passLongOption = map (spaceToDash . toLower) . passName-  where spaceToDash ' ' = '-'-        spaceToDash c   = c+  where+    spaceToDash ' ' = '-'+    spaceToDash c = c  -- | Apply a 'PassM' operation in parallel to multiple elements, -- joining together the name sources and logs, and propagating any@@ -83,23 +86,25 @@ parPass f as = do   (x, log) <- modifyNameSource $ \src ->     let (bs, logs, srcs) = unzip3 $ parMap rpar (f' src) as-    in ((bs, mconcat logs), mconcat srcs)+     in ((bs, mconcat logs), mconcat srcs)    addLog log   return x--  where f' src a =-          let ((x', log), src') = runState (runPassM (f a)) src-          in (x', log, src')+  where+    f' src a =+      let ((x', log), src') = runState (runPassM (f a)) src+       in (x', log, src')  -- | Apply some operation to the top-level constants.  Then applies an -- operation to all the function function definitions, which are also -- given the transformed constants so they can be brought into scope. -- The function definition transformations are run in parallel (with -- 'parPass'), since they cannot affect each other.-intraproceduralTransformationWithConsts :: (Stms fromlore -> PassM (Stms tolore))-                                        -> (Stms tolore -> FunDef fromlore -> PassM (FunDef tolore))-                                        -> Prog fromlore -> PassM (Prog tolore)+intraproceduralTransformationWithConsts ::+  (Stms fromlore -> PassM (Stms tolore)) ->+  (Stms tolore -> FunDef fromlore -> PassM (FunDef tolore)) ->+  Prog fromlore ->+  PassM (Prog tolore) intraproceduralTransformationWithConsts ct ft (Prog consts funs) = do   consts' <- ct consts   funs' <- parPass (ft consts') funs@@ -107,13 +112,16 @@  -- | Like 'intraproceduralTransformationWithConsts', but do not change -- the top-level constants, and simply pass along their 'Scope'.-intraproceduralTransformation :: (Scope lore -> Stms lore -> PassM (Stms lore))-                              -> Prog lore-                              -> PassM (Prog lore)+intraproceduralTransformation ::+  (Scope lore -> Stms lore -> PassM (Stms lore)) ->+  Prog lore ->+  PassM (Prog lore) intraproceduralTransformation f =   intraproceduralTransformationWithConsts (f mempty) f'-  where f' consts fd = do-          stms <- f-                  (scopeOf consts<>scopeOfFParams (funDefParams fd))-                  (bodyStms $ funDefBody fd)-          return fd { funDefBody = (funDefBody fd) { bodyStms = stms } }+  where+    f' consts fd = do+      stms <-+        f+          (scopeOf consts <> scopeOfFParams (funDefParams fd))+          (bodyStms $ funDefBody fd)+      return fd {funDefBody = (funDefBody fd) {bodyStms = stms}}
src/Futhark/Pass/ExpandAllocations.hs view
@@ -1,64 +1,70 @@-{-# LANGUAGE TypeFamilies, FlexibleContexts, GeneralizedNewtypeDeriving #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeFamilies #-}+ -- | Expand allocations inside of maps when possible.-module Futhark.Pass.ExpandAllocations-       ( expandAllocations )-where+module Futhark.Pass.ExpandAllocations (expandAllocations) where -import Control.Monad.Identity import Control.Monad.Except-import Control.Monad.State import Control.Monad.Reader+import Control.Monad.State import Control.Monad.Writer+import Data.List (foldl') import qualified Data.Map.Strict as M import Data.Maybe-import Data.List (foldl')--import Prelude hiding (quot)- import Futhark.Analysis.Rephrase+import qualified Futhark.Analysis.SymbolTable as ST import Futhark.Error-import Futhark.MonadFreshNames-import Futhark.Tools-import Futhark.Pass import Futhark.IR-import Futhark.IR.KernelsMem import qualified Futhark.IR.Kernels.Simplify as Kernels+import Futhark.IR.KernelsMem import qualified Futhark.IR.Mem.IxFun as IxFun+import Futhark.MonadFreshNames+import Futhark.Optimise.Simplify.Lore (addScopeWisdom)+import Futhark.Pass+import Futhark.Pass.ExplicitAllocations.Kernels (explicitAllocationsInStms) import Futhark.Pass.ExtractKernels.BlockedKernel (nonSegRed) import Futhark.Pass.ExtractKernels.ToKernels (segThread)-import Futhark.Pass.ExplicitAllocations.Kernels (explicitAllocationsInStms)-import Futhark.Transform.Rename (renameStm)+import Futhark.Tools import Futhark.Transform.CopyPropagate (copyPropagateInFun)-import Futhark.Optimise.Simplify.Lore (addScopeWisdom)-import qualified Futhark.Analysis.SymbolTable as ST-import Futhark.Util.IntegralExp+import Futhark.Transform.Rename (renameStm) import Futhark.Util (mapAccumLM)+import Futhark.Util.IntegralExp+import Prelude hiding (quot)  -- | The memory expansion pass definition. expandAllocations :: Pass KernelsMem KernelsMem expandAllocations =   Pass "expand allocations" "Expand allocations" $-  \(Prog consts funs) -> do-    consts' <--      modifyNameSource $ limitationOnLeft . runStateT (runReaderT (transformStms consts) mempty)-    Prog consts' <$> mapM (transformFunDef $ scopeOf consts') funs-  -- Cannot use intraproceduralTransformation because it might create-  -- duplicate size keys (which are not fixed by renamer, and size-  -- keys must currently be globally unique).+    \(Prog consts funs) -> do+      consts' <-+        modifyNameSource $ limitationOnLeft . runStateT (runReaderT (transformStms consts) mempty)+      Prog consts' <$> mapM (transformFunDef $ scopeOf consts') funs +-- Cannot use intraproceduralTransformation because it might create+-- duplicate size keys (which are not fixed by renamer, and size+-- keys must currently be globally unique).+ type ExpandM = ReaderT (Scope KernelsMem) (StateT VNameSource (Either String))  limitationOnLeft :: Either String a -> a limitationOnLeft = either compilerLimitationS id -transformFunDef :: Scope KernelsMem -> FunDef KernelsMem-                -> PassM (FunDef KernelsMem)+transformFunDef ::+  Scope KernelsMem ->+  FunDef KernelsMem ->+  PassM (FunDef KernelsMem) transformFunDef scope fundec = do   body' <- modifyNameSource $ limitationOnLeft . runStateT (runReaderT m mempty)-  copyPropagateInFun simpleKernelsMem-    (ST.fromScope (addScopeWisdom scope)) fundec { funDefBody = body' }-  where m = localScope scope $ inScopeOf fundec $-            transformBody $ funDefBody fundec+  copyPropagateInFun+    simpleKernelsMem+    (ST.fromScope (addScopeWisdom scope))+    fundec {funDefBody = body'}+  where+    m =+      localScope scope $+        inScopeOf fundec $+          transformBody $ funDefBody fundec  transformBody :: Body KernelsMem -> ExpandM (Body KernelsMem) transformBody (Body () stms res) = Body () <$> transformStms stms <*> pure res@@ -68,7 +74,6 @@   inScopeOf stms $ mconcat <$> mapM transformStm (stmsToList stms)  transformStm :: Stm KernelsMem -> ExpandM (Stms KernelsMem)- -- It is possible that we are unable to expand allocations in some -- code versions.  If so, we can remove the offending branch.  Only if -- both versions fail do we propagate the error.@@ -84,18 +89,20 @@       return $ oneStm $ Let pat aux $ If cond tbranch'' fbranch'' (IfDec ts IfEquiv)     (Left e, _) ->       throwError e--  where bindRes pe se = Let (Pattern [] [pe]) (defAux ()) $ BasicOp $ SubExp se--        useBranch b =-          bodyStms b <>-          stmsFromList (zipWith bindRes (patternElements pat) (bodyResult b))+  where+    bindRes pe se = Let (Pattern [] [pe]) (defAux ()) $ BasicOp $ SubExp se +    useBranch b =+      bodyStms b+        <> stmsFromList (zipWith bindRes (patternElements pat) (bodyResult b)) transformStm (Let pat aux e) = do   (bnds, e') <- transformExp =<< mapExpM transform e   return $ bnds <> oneStm (Let pat aux e')-  where transform = identityMapper { mapOnBody = \scope -> localScope scope . transformBody-                                   }+  where+    transform =+      identityMapper+        { mapOnBody = \scope -> localScope scope . transformBody+        }  nameInfoConv :: NameInfo KernelsMem -> NameInfo KernelsMem nameInfoConv (LetName mem_info) = LetName mem_info@@ -104,41 +111,47 @@ nameInfoConv (IndexName it) = IndexName it  transformExp :: Exp KernelsMem -> ExpandM (Stms KernelsMem, Exp KernelsMem)- transformExp (Op (Inner (SegOp (SegMap lvl space ts kbody)))) = do   (alloc_stms, (_, kbody')) <- transformScanRed lvl space [] kbody-  return (alloc_stms,-          Op $ Inner $ SegOp $ SegMap lvl space ts kbody')-+  return+    ( alloc_stms,+      Op $ Inner $ SegOp $ SegMap lvl space ts kbody'+    ) transformExp (Op (Inner (SegOp (SegRed lvl space reds ts kbody)))) = do   (alloc_stms, (lams, kbody')) <-     transformScanRed lvl space (map segBinOpLambda reds) kbody-  let reds' = zipWith (\red lam -> red { segBinOpLambda = lam }) reds lams-  return (alloc_stms,-          Op $ Inner $ SegOp $ SegRed lvl space reds' ts kbody')-+  let reds' = zipWith (\red lam -> red {segBinOpLambda = lam}) reds lams+  return+    ( alloc_stms,+      Op $ Inner $ SegOp $ SegRed lvl space reds' ts kbody'+    ) transformExp (Op (Inner (SegOp (SegScan lvl space scans ts kbody)))) = do   (alloc_stms, (lams, kbody')) <-     transformScanRed lvl space (map segBinOpLambda scans) kbody-  let scans' = zipWith (\red lam -> red { segBinOpLambda = lam }) scans lams-  return (alloc_stms,-          Op $ Inner $ SegOp $ SegScan lvl space scans' ts kbody')-+  let scans' = zipWith (\red lam -> red {segBinOpLambda = lam}) scans lams+  return+    ( alloc_stms,+      Op $ Inner $ SegOp $ SegScan lvl space scans' ts kbody'+    ) transformExp (Op (Inner (SegOp (SegHist lvl space ops ts kbody)))) = do   (alloc_stms, (lams', kbody')) <- transformScanRed lvl space lams kbody   let ops' = zipWith onOp ops lams'-  return (alloc_stms,-          Op $ Inner $ SegOp $ SegHist lvl space ops' ts kbody')-  where lams = map histOp ops-        onOp op lam = op { histOp = lam }-+  return+    ( alloc_stms,+      Op $ Inner $ SegOp $ SegHist lvl space ops' ts kbody'+    )+  where+    lams = map histOp ops+    onOp op lam = op {histOp = lam} transformExp e =   return (mempty, e) -transformScanRed :: SegLevel -> SegSpace-                 -> [Lambda KernelsMem]-                 -> KernelBody KernelsMem-                 -> ExpandM (Stms KernelsMem, ([Lambda KernelsMem], KernelBody KernelsMem))+transformScanRed ::+  SegLevel ->+  SegSpace ->+  [Lambda KernelsMem] ->+  KernelBody KernelsMem ->+  ExpandM (Stms KernelsMem, ([Lambda KernelsMem], KernelBody KernelsMem)) transformScanRed lvl space ops kbody = do   bound_outside <- asks $ namesFromList . M.keys   let (kbody', kbody_allocs) =@@ -150,9 +163,9 @@       (variant_allocs, invariant_allocs) = M.partition variantAlloc allocs    case lvl of-    SegGroup{}+    SegGroup {}       | not $ null variant_allocs ->-          throwError "Cannot handle invariant allocations in SegGroup."+        throwError "Cannot handle invariant allocations in SegGroup."     _ ->       return () @@ -160,185 +173,251 @@     ops'' <- forM ops' $ \op' ->       localScope (scopeOf op') $ offsetMemoryInLambda op'     return (alloc_stms, (ops'', kbody''))--  where bound_in_kernel = namesFromList $ M.keys $ scopeOfSegSpace space <>-                          scopeOf (kernelBodyStms kbody)+  where+    bound_in_kernel =+      namesFromList $+        M.keys $+          scopeOfSegSpace space+            <> scopeOf (kernelBodyStms kbody) -allocsForBody :: Extraction-              -> Extraction-              -> SegLevel -> SegSpace-              -> KernelBody KernelsMem-              -> (Stms KernelsMem -> KernelBody KernelsMem -> OffsetM b)-              -> ExpandM b+allocsForBody ::+  Extraction ->+  Extraction ->+  SegLevel ->+  SegSpace ->+  KernelBody KernelsMem ->+  (Stms KernelsMem -> KernelBody KernelsMem -> OffsetM b) ->+  ExpandM b allocsForBody variant_allocs invariant_allocs lvl space kbody' m = do   (alloc_offsets, alloc_stms) <--    memoryRequirements lvl space-    (kernelBodyStms kbody') variant_allocs invariant_allocs+    memoryRequirements+      lvl+      space+      (kernelBodyStms kbody')+      variant_allocs+      invariant_allocs    scope <- askScope   let scope' = scopeOfSegSpace space <> M.map nameInfoConv scope-  either throwError pure $ runOffsetM scope' alloc_offsets $ do-    kbody'' <- offsetMemoryInKernelBody kbody'-    m alloc_stms kbody''+  either throwError pure $+    runOffsetM scope' alloc_offsets $ do+      kbody'' <- offsetMemoryInKernelBody kbody'+      m alloc_stms kbody'' -memoryRequirements :: SegLevel -> SegSpace-                   -> Stms KernelsMem-                   -> Extraction -> Extraction-                   -> ExpandM (RebaseMap, Stms KernelsMem)+memoryRequirements ::+  SegLevel ->+  SegSpace ->+  Stms KernelsMem ->+  Extraction ->+  Extraction ->+  ExpandM (RebaseMap, Stms KernelsMem) memoryRequirements lvl space kstms variant_allocs invariant_allocs = do-  ((num_threads, num_groups64, num_threads64), num_threads_stms) <- runBinder $ do-    num_threads <- letSubExp "num_threads" $ BasicOp $ BinOp (Mul Int32 OverflowUndef)-                   (unCount $ segNumGroups lvl) (unCount $ segGroupSize lvl)-    num_groups64 <- letSubExp "num_groups64" $-                    BasicOp $ ConvOp (SExt Int32 Int64) (unCount $ segNumGroups lvl)-    num_threads64 <- letSubExp "num_threads64" $ BasicOp $ ConvOp (SExt Int32 Int64) num_threads-    return (num_threads, num_groups64, num_threads64)+  (num_threads, num_threads_stms) <-+    runBinder $+      letSubExp "num_threads" $+        BasicOp $+          BinOp+            (Mul Int64 OverflowUndef)+            (unCount $ segNumGroups lvl)+            (unCount $ segGroupSize lvl)    (invariant_alloc_stms, invariant_alloc_offsets) <--    inScopeOf num_threads_stms $ expandedInvariantAllocations-    (num_threads64, num_groups64, segNumGroups lvl, segGroupSize lvl)-    space invariant_allocs+    inScopeOf num_threads_stms $+      expandedInvariantAllocations+        (num_threads, segNumGroups lvl, segGroupSize lvl)+        space+        invariant_allocs    (variant_alloc_stms, variant_alloc_offsets) <--    inScopeOf num_threads_stms $ expandedVariantAllocations-    num_threads space kstms variant_allocs+    inScopeOf num_threads_stms $+      expandedVariantAllocations+        num_threads+        space+        kstms+        variant_allocs -  return (invariant_alloc_offsets <> variant_alloc_offsets,-          num_threads_stms <> invariant_alloc_stms <> variant_alloc_stms)+  return+    ( invariant_alloc_offsets <> variant_alloc_offsets,+      num_threads_stms <> invariant_alloc_stms <> variant_alloc_stms+    )  -- | A description of allocations that have been extracted, and how -- much memory (and which space) is needed. type Extraction = M.Map VName (SegLevel, SubExp, Space) -extractKernelBodyAllocations :: SegLevel -> Names -> Names -> KernelBody KernelsMem-                             -> (KernelBody KernelsMem,-                                 Extraction)+extractKernelBodyAllocations ::+  SegLevel ->+  Names ->+  Names ->+  KernelBody KernelsMem ->+  ( KernelBody KernelsMem,+    Extraction+  ) extractKernelBodyAllocations lvl bound_outside bound_kernel =   extractGenericBodyAllocations lvl bound_outside bound_kernel kernelBodyStms $-  \stms kbody -> kbody { kernelBodyStms = stms }+    \stms kbody -> kbody {kernelBodyStms = stms} -extractBodyAllocations :: SegLevel -> Names -> Names -> Body KernelsMem-                       -> (Body KernelsMem, Extraction)+extractBodyAllocations ::+  SegLevel ->+  Names ->+  Names ->+  Body KernelsMem ->+  (Body KernelsMem, Extraction) extractBodyAllocations lvl bound_outside bound_kernel =   extractGenericBodyAllocations lvl bound_outside bound_kernel bodyStms $-  \stms body -> body { bodyStms = stms }+    \stms body -> body {bodyStms = stms} -extractLambdaAllocations :: SegLevel -> Names -> Names -> Lambda KernelsMem-                         -> (Lambda KernelsMem, Extraction)-extractLambdaAllocations lvl bound_outside bound_kernel lam = (lam { lambdaBody = body' }, allocs)-  where (body', allocs) = extractBodyAllocations lvl bound_outside bound_kernel $ lambdaBody lam+extractLambdaAllocations ::+  SegLevel ->+  Names ->+  Names ->+  Lambda KernelsMem ->+  (Lambda KernelsMem, Extraction)+extractLambdaAllocations lvl bound_outside bound_kernel lam = (lam {lambdaBody = body'}, allocs)+  where+    (body', allocs) = extractBodyAllocations lvl bound_outside bound_kernel $ lambdaBody lam -extractGenericBodyAllocations :: SegLevel -> Names -> Names-                              -> (body -> Stms KernelsMem)-                              -> (Stms KernelsMem -> body -> body)-                              -> body-                              -> (body,-                                  Extraction)+extractGenericBodyAllocations ::+  SegLevel ->+  Names ->+  Names ->+  (body -> Stms KernelsMem) ->+  (Stms KernelsMem -> body -> body) ->+  body ->+  ( body,+    Extraction+  ) extractGenericBodyAllocations lvl bound_outside bound_kernel get_stms set_stms body =-  let (stms, allocs) = runWriter $ fmap catMaybes $-                       mapM (extractStmAllocations lvl bound_outside bound_kernel) $-                       stmsToList $ get_stms body-  in (set_stms (stmsFromList stms) body, allocs)+  let (stms, allocs) =+        runWriter $+          fmap catMaybes $+            mapM (extractStmAllocations lvl bound_outside bound_kernel) $+              stmsToList $ get_stms body+   in (set_stms (stmsFromList stms) body, allocs)  expandable :: Space -> Bool expandable (Space "local") = False-expandable ScalarSpace{} = False+expandable ScalarSpace {} = False expandable _ = True -extractStmAllocations :: SegLevel -> Names -> Names -> Stm KernelsMem-                      -> Writer Extraction (Maybe (Stm KernelsMem))+extractStmAllocations ::+  SegLevel ->+  Names ->+  Names ->+  Stm KernelsMem ->+  Writer Extraction (Maybe (Stm KernelsMem)) extractStmAllocations lvl bound_outside bound_kernel (Let (Pattern [] [patElem]) _ (Op (Alloc size space)))   | expandable space && expandableSize size || boundInKernel size = do-      tell $ M.singleton (patElemName patElem) (lvl, size, space)-      return Nothing--        where expandableSize (Var v) = v `nameIn` bound_outside || v `nameIn` bound_kernel-              expandableSize Constant{} = True-              boundInKernel (Var v) = v `nameIn` bound_kernel-              boundInKernel Constant{} = False-+    tell $ M.singleton (patElemName patElem) (lvl, size, space)+    return Nothing+  where+    expandableSize (Var v) = v `nameIn` bound_outside || v `nameIn` bound_kernel+    expandableSize Constant {} = True+    boundInKernel (Var v) = v `nameIn` bound_kernel+    boundInKernel Constant {} = False extractStmAllocations lvl bound_outside bound_kernel stm = do   e <- mapExpM (expMapper lvl) $ stmExp stm-  return $ Just $ stm { stmExp = e }-  where expMapper lvl' = identityMapper { mapOnBody = const $ onBody lvl'-                                        , mapOnOp = onOp-                                        }--        onBody lvl' body = do-          let (body', allocs) = extractBodyAllocations lvl' bound_outside bound_kernel body-          tell allocs-          return body'--        onOp (Inner (SegOp op)) =-          Inner . SegOp <$> mapSegOpM (opMapper (segLevel op)) op-        onOp op = return op+  return $ Just $ stm {stmExp = e}+  where+    expMapper lvl' =+      identityMapper+        { mapOnBody = const $ onBody lvl',+          mapOnOp = onOp+        } -        opMapper lvl' = identitySegOpMapper { mapOnSegOpLambda = onLambda lvl'-                                            , mapOnSegOpBody = onKernelBody lvl'-                                            }+    onBody lvl' body = do+      let (body', allocs) = extractBodyAllocations lvl' bound_outside bound_kernel body+      tell allocs+      return body' -        onKernelBody lvl' body = do-          let (body', allocs) = extractKernelBodyAllocations lvl' bound_outside bound_kernel body-          tell allocs-          return body'+    onOp (Inner (SegOp op)) =+      Inner . SegOp <$> mapSegOpM (opMapper (segLevel op)) op+    onOp op = return op -        onLambda lvl' lam = do-          body <- onBody lvl' $ lambdaBody lam-          return lam { lambdaBody = body }+    opMapper lvl' =+      identitySegOpMapper+        { mapOnSegOpLambda = onLambda lvl',+          mapOnSegOpBody = onKernelBody lvl'+        } -expandedInvariantAllocations :: (SubExp, SubExp,-                                 Count NumGroups SubExp, Count GroupSize SubExp)-                             -> SegSpace-                             -> Extraction-                             -> ExpandM (Stms KernelsMem, RebaseMap)-expandedInvariantAllocations (num_threads64, num_groups64,-                              Count num_groups, Count group_size)-                             segspace-                             invariant_allocs = do-  -- We expand the invariant allocations by adding an inner dimension-  -- equal to the number of kernel threads.-  (alloc_bnds, rebases) <- unzip <$> mapM expand (M.toList invariant_allocs)+    onKernelBody lvl' body = do+      let (body', allocs) = extractKernelBodyAllocations lvl' bound_outside bound_kernel body+      tell allocs+      return body' -  return (mconcat alloc_bnds, mconcat rebases)-  where expand (mem, (lvl, per_thread_size, space)) = do-          total_size <- newVName "total_size"-          let sizepat = Pattern [] [PatElem total_size $ MemPrim int64]-              allocpat = Pattern [] [PatElem mem $ MemMem space]-              num_users = case lvl of SegThread{} -> num_threads64-                                      SegGroup{} -> num_groups64-          return (stmsFromList-                  [Let sizepat (defAux ()) $-                    BasicOp $ BinOp (Mul Int64 OverflowUndef) num_users per_thread_size,-                   Let allocpat (defAux ()) $-                    Op $ Alloc (Var total_size) space],-                  M.singleton mem $ newBase lvl)+    onLambda lvl' lam = do+      body <- onBody lvl' $ lambdaBody lam+      return lam {lambdaBody = body} -        untouched d = DimSlice (fromInt32 0) d (fromInt32 1)+expandedInvariantAllocations ::+  ( SubExp,+    Count NumGroups SubExp,+    Count GroupSize SubExp+  ) ->+  SegSpace ->+  Extraction ->+  ExpandM (Stms KernelsMem, RebaseMap)+expandedInvariantAllocations+  ( num_threads,+    Count num_groups,+    Count group_size+    )+  segspace+  invariant_allocs = do+    -- We expand the invariant allocations by adding an inner dimension+    -- equal to the number of kernel threads.+    (alloc_bnds, rebases) <- unzip <$> mapM expand (M.toList invariant_allocs) -        newBase SegThread{} (old_shape, _) =-          let num_dims = length old_shape-              perm = num_dims : [0..num_dims-1]-              root_ixfun = IxFun.iota (old_shape-                                       ++ [primExpFromSubExp int32 num_groups *-                                           primExpFromSubExp int32 group_size])-              permuted_ixfun = IxFun.permute root_ixfun perm-              offset_ixfun = IxFun.slice permuted_ixfun $-                             DimFix (LeafExp (segFlat segspace) int32) :-                             map untouched old_shape-          in offset_ixfun+    return (mconcat alloc_bnds, mconcat rebases)+    where+      expand (mem, (lvl, per_thread_size, space)) = do+        total_size <- newVName "total_size"+        let sizepat = Pattern [] [PatElem total_size $ MemPrim int64]+            allocpat = Pattern [] [PatElem mem $ MemMem space]+            num_users = case lvl of+              SegThread {} -> num_threads+              SegGroup {} -> num_groups+        return+          ( stmsFromList+              [ Let sizepat (defAux ()) $+                  BasicOp $ BinOp (Mul Int64 OverflowUndef) num_users per_thread_size,+                Let allocpat (defAux ()) $+                  Op $ Alloc (Var total_size) space+              ],+            M.singleton mem $ newBase lvl+          ) -        newBase SegGroup{} (old_shape, _) =-          let root_ixfun = IxFun.iota (primExpFromSubExp int32 num_groups : old_shape)-              offset_ixfun = IxFun.slice root_ixfun $-                             DimFix (LeafExp (segFlat segspace) int32) :-                             map untouched old_shape-          in offset_ixfun+      untouched d = DimSlice 0 d 1 +      newBase SegThread {} (old_shape, _) =+        let num_dims = length old_shape+            perm = num_dims : [0 .. num_dims -1]+            root_ixfun =+              IxFun.iota+                ( old_shape+                    ++ [ pe64 num_groups * pe64 group_size+                       ]+                )+            permuted_ixfun = IxFun.permute root_ixfun perm+            offset_ixfun =+              IxFun.slice permuted_ixfun $+                DimFix (le64 (segFlat segspace)) :+                map untouched old_shape+         in offset_ixfun+      newBase SegGroup {} (old_shape, _) =+        let root_ixfun = IxFun.iota (pe64 num_groups : old_shape)+            offset_ixfun =+              IxFun.slice root_ixfun $+                DimFix (le64 (segFlat segspace)) :+                map untouched old_shape+         in offset_ixfun -expandedVariantAllocations :: SubExp-                           -> SegSpace -> Stms KernelsMem-                           -> Extraction-                           -> ExpandM (Stms KernelsMem, RebaseMap)+expandedVariantAllocations ::+  SubExp ->+  SegSpace ->+  Stms KernelsMem ->+  Extraction ->+  ExpandM (Stms KernelsMem, RebaseMap) expandedVariantAllocations _ _ _ variant_allocs   | null variant_allocs = return (mempty, mempty) expandedVariantAllocations num_threads kspace kstms variant_allocs = do@@ -354,49 +433,67 @@   slice_stms' <- transformStms slice_stms_tmp    let variant_allocs' :: [(VName, (SubExp, SubExp, Space))]-      variant_allocs' = concat $ zipWith memInfo (map snd sizes_to_blocks)-                        (zip offsets size_sums)+      variant_allocs' =+        concat $+          zipWith+            memInfo+            (map snd sizes_to_blocks)+            (zip offsets size_sums)       memInfo blocks (offset, total_size) =-        [ (mem, (Var offset, Var total_size, space)) | (mem, space) <- blocks ]+        [(mem, (Var offset, Var total_size, space)) | (mem, space) <- blocks]    -- We expand the invariant allocations by adding an inner dimension   -- equal to the sum of the sizes required by different threads.   (alloc_bnds, rebases) <- unzip <$> mapM expand variant_allocs'    return (slice_stms' <> stmsFromList alloc_bnds, mconcat rebases)-  where expand (mem, (offset, total_size, space)) = do-          let allocpat = Pattern [] [PatElem mem $ MemMem space]-          return (Let allocpat (defAux ()) $ Op $ Alloc total_size space,-                  M.singleton mem $ newBase offset)+  where+    expand (mem, (offset, total_size, space)) = do+      let allocpat = Pattern [] [PatElem mem $ MemMem space]+      return+        ( Let allocpat (defAux ()) $ Op $ Alloc total_size space,+          M.singleton mem $ newBase offset+        ) -        num_threads' = primExpFromSubExp int32 num_threads-        gtid = LeafExp (segFlat kspace) int32+    num_threads' = pe64 num_threads+    gtid = le64 $ segFlat kspace -        -- For the variant allocations, we add an inner dimension,-        -- which is then offset by a thread-specific amount.-        newBase size_per_thread (old_shape, pt) =-          let pt_size = fromInt32 $ primByteSize pt-              elems_per_thread = sExt Int32-                                 (primExpFromSubExp int64 size_per_thread)-                                 `quot` pt_size-              root_ixfun = IxFun.iota [elems_per_thread, num_threads']-              offset_ixfun = IxFun.slice root_ixfun-                             [DimSlice (fromInt32 0) num_threads' (fromInt32 1),-                              DimFix gtid]-              shapechange = if length old_shape == 1-                            then map DimCoercion old_shape-                            else map DimNew old_shape-          in IxFun.reshape offset_ixfun shapechange+    -- For the variant allocations, we add an inner dimension,+    -- which is then offset by a thread-specific amount.+    newBase size_per_thread (old_shape, pt) =+      let elems_per_thread =+            pe64 size_per_thread `quot` primByteSize pt+          root_ixfun = IxFun.iota [elems_per_thread, num_threads']+          offset_ixfun =+            IxFun.slice+              root_ixfun+              [ DimSlice 0 num_threads' 1,+                DimFix gtid+              ]+          shapechange =+            if length old_shape == 1+              then map DimCoercion old_shape+              else map DimNew old_shape+       in IxFun.reshape offset_ixfun shapechange  -- | A map from memory block names to new index function bases.--type RebaseMap = M.Map VName (([PrimExp VName], PrimType) -> IxFun)+type RebaseMap = M.Map VName (([TPrimExp Int64 VName], PrimType) -> IxFun) -newtype OffsetM a = OffsetM (ReaderT (Scope KernelsMem)-                             (ReaderT RebaseMap (Either String)) a)-  deriving (Applicative, Functor, Monad,-            HasScope KernelsMem, LocalScope KernelsMem,-            MonadError String)+newtype OffsetM a+  = OffsetM+      ( ReaderT+          (Scope KernelsMem)+          (ReaderT RebaseMap (Either String))+          a+      )+  deriving+    ( Applicative,+      Functor,+      Monad,+      HasScope KernelsMem,+      LocalScope KernelsMem,+      MonadError String+    )  runOffsetM :: Scope KernelsMem -> RebaseMap -> OffsetM a -> Either String a runOffsetM scope offsets (OffsetM m) =@@ -405,7 +502,7 @@ askRebaseMap :: OffsetM RebaseMap askRebaseMap = OffsetM $ lift ask -lookupNewBase :: VName -> ([PrimExp VName], PrimType) -> OffsetM (Maybe IxFun)+lookupNewBase :: VName -> ([TPrimExp Int64 VName], PrimType) -> OffsetM (Maybe IxFun) lookupNewBase name x = do   offsets <- askRebaseMap   return $ ($ x) <$> M.lookup name offsets@@ -413,17 +510,23 @@ offsetMemoryInKernelBody :: KernelBody KernelsMem -> OffsetM (KernelBody KernelsMem) offsetMemoryInKernelBody kbody = do   scope <- askScope-  stms' <- stmsFromList . snd <$>-           mapAccumLM (\scope' -> localScope scope' . offsetMemoryInStm) scope-           (stmsToList $ kernelBodyStms kbody)-  return kbody { kernelBodyStms = stms' }+  stms' <-+    stmsFromList . snd+      <$> mapAccumLM+        (\scope' -> localScope scope' . offsetMemoryInStm)+        scope+        (stmsToList $ kernelBodyStms kbody)+  return kbody {kernelBodyStms = stms'}  offsetMemoryInBody :: Body KernelsMem -> OffsetM (Body KernelsMem) offsetMemoryInBody (Body dec stms res) = do   scope <- askScope-  stms' <- stmsFromList . snd <$>-           mapAccumLM (\scope' -> localScope scope' . offsetMemoryInStm) scope-           (stmsToList stms)+  stms' <-+    stmsFromList . snd+      <$> mapAccumLM+        (\scope' -> localScope scope' . offsetMemoryInStm)+        scope+        (stmsToList stms)   return $ Body dec stms' res  offsetMemoryInStm :: Stm KernelsMem -> OffsetM (Scope KernelsMem, Stm KernelsMem)@@ -434,67 +537,81 @@   -- Try to recompute the index function.  Fall back to creating rebase   -- operations with the RebaseMap.   rts <- runReaderT (expReturns e') scope-  let pat'' = Pattern (patternContextElements pat')-              (zipWith pick (patternValueElements pat') rts)+  let pat'' =+        Pattern+          (patternContextElements pat')+          (zipWith pick (patternValueElements pat') rts)       stm = Let pat'' dec e'   let scope' = scopeOf stm <> scope   return (scope', stm)-  where pick :: PatElemT (MemInfo SubExp NoUniqueness MemBind) ->-                ExpReturns -> PatElemT (MemInfo SubExp NoUniqueness MemBind)-        pick (PatElem name (MemArray pt s u _ret))-             (MemArray _ _ _ (Just (ReturnsInBlock m extixfun)))-          | Just ixfun <- instantiateIxFun extixfun =-              PatElem name (MemArray pt s u (ArrayIn m ixfun))-        pick p _ = p+  where+    pick ::+      PatElemT (MemInfo SubExp NoUniqueness MemBind) ->+      ExpReturns ->+      PatElemT (MemInfo SubExp NoUniqueness MemBind)+    pick+      (PatElem name (MemArray pt s u _ret))+      (MemArray _ _ _ (Just (ReturnsInBlock m extixfun)))+        | Just ixfun <- instantiateIxFun extixfun =+          PatElem name (MemArray pt s u (ArrayIn m ixfun))+    pick p _ = p -        instantiateIxFun :: ExtIxFun -> Maybe IxFun-        instantiateIxFun = traverse (traverse inst)-          where inst Ext{} = Nothing-                inst (Free x) = return x+    instantiateIxFun :: ExtIxFun -> Maybe IxFun+    instantiateIxFun = traverse (traverse inst)+      where+        inst Ext {} = Nothing+        inst (Free x) = return x  offsetMemoryInPattern :: Pattern KernelsMem -> OffsetM (Pattern KernelsMem) offsetMemoryInPattern (Pattern ctx vals) = do   mapM_ inspectCtx ctx   Pattern ctx <$> mapM inspectVal vals-  where inspectVal patElem = do-          new_dec <- offsetMemoryInMemBound $ patElemDec patElem-          return patElem { patElemDec = new_dec }-        inspectCtx patElem-          | Mem space <- patElemType patElem,-            expandable space =-              throwError $ unwords ["Cannot deal with existential memory block",-                                    pretty (patElemName patElem),-                                    "when expanding inside kernels."]-          | otherwise = return ()+  where+    inspectVal patElem = do+      new_dec <- offsetMemoryInMemBound $ patElemDec patElem+      return patElem {patElemDec = new_dec}+    inspectCtx patElem+      | Mem space <- patElemType patElem,+        expandable space =+        throwError $+          unwords+            [ "Cannot deal with existential memory block",+              pretty (patElemName patElem),+              "when expanding inside kernels."+            ]+      | otherwise = return ()  offsetMemoryInParam :: Param (MemBound u) -> OffsetM (Param (MemBound u)) offsetMemoryInParam fparam = do   fparam' <- offsetMemoryInMemBound $ paramDec fparam-  return fparam { paramDec = fparam' }+  return fparam {paramDec = fparam'}  offsetMemoryInMemBound :: MemBound u -> OffsetM (MemBound u) offsetMemoryInMemBound summary@(MemArray pt shape u (ArrayIn mem ixfun)) = do   new_base <- lookupNewBase mem (IxFun.base ixfun, pt)-  return $ fromMaybe summary $ do-    new_base' <- new_base-    return $ MemArray pt shape u $ ArrayIn mem $ IxFun.rebase new_base' ixfun+  return $+    fromMaybe summary $ do+      new_base' <- new_base+      return $ MemArray pt shape u $ ArrayIn mem $ IxFun.rebase new_base' ixfun offsetMemoryInMemBound summary = return summary  offsetMemoryInBodyReturns :: BodyReturns -> OffsetM BodyReturns offsetMemoryInBodyReturns br@(MemArray pt shape u (ReturnsInBlock mem ixfun))   | Just ixfun' <- isStaticIxFun ixfun = do-      new_base <- lookupNewBase mem (IxFun.base ixfun', pt)-      return $ fromMaybe br $ do+    new_base <- lookupNewBase mem (IxFun.base ixfun', pt)+    return $+      fromMaybe br $ do         new_base' <- new_base         return $-          MemArray pt shape u $ ReturnsInBlock mem $-          IxFun.rebase (fmap (fmap Free) new_base') ixfun+          MemArray pt shape u $+            ReturnsInBlock mem $+              IxFun.rebase (fmap (fmap Free) new_base') ixfun offsetMemoryInBodyReturns br = return br  offsetMemoryInLambda :: Lambda KernelsMem -> OffsetM (Lambda KernelsMem) offsetMemoryInLambda lam = inScopeOf lam $ do   body <- offsetMemoryInBody $ lambdaBody lam-  return $ lam { lambdaBody = body }+  return $ lam {lambdaBody = body}  offsetMemoryInExp :: Exp KernelsMem -> OffsetM (Exp KernelsMem) offsetMemoryInExp (DoLoop ctx val form body) = do@@ -505,20 +622,23 @@   body' <- localScope (scopeOfFParams ctxparams' <> scopeOfFParams valparams' <> scopeOf form) (offsetMemoryInBody body)   return $ DoLoop (zip ctxparams' ctxinit) (zip valparams' valinit) form body' offsetMemoryInExp e = mapExpM recurse e-  where recurse = identityMapper-                  { mapOnBody = \bscope -> localScope bscope . offsetMemoryInBody-                  , mapOnBranchType = offsetMemoryInBodyReturns-                  , mapOnOp = onOp-                  }-        onOp (Inner (SegOp op)) =-          Inner . SegOp <$>-          localScope (scopeOfSegSpace (segSpace op)) (mapSegOpM segOpMapper op)-          where segOpMapper =-                  identitySegOpMapper { mapOnSegOpBody = offsetMemoryInKernelBody-                                      , mapOnSegOpLambda = offsetMemoryInLambda-                                      }-        onOp op = return op-+  where+    recurse =+      identityMapper+        { mapOnBody = \bscope -> localScope bscope . offsetMemoryInBody,+          mapOnBranchType = offsetMemoryInBodyReturns,+          mapOnOp = onOp+        }+    onOp (Inner (SegOp op)) =+      Inner . SegOp+        <$> localScope (scopeOfSegSpace (segSpace op)) (mapSegOpM segOpMapper op)+      where+        segOpMapper =+          identitySegOpMapper+            { mapOnSegOpBody = offsetMemoryInKernelBody,+              mapOnSegOpLambda = offsetMemoryInLambda+            }+    onOp op = return op  ---- Slicing allocation sizes out of a kernel. @@ -534,7 +654,7 @@     unAllocStms nested =       fmap (stmsFromList . catMaybes) . mapM (unAllocStm nested) . stmsToList -    unAllocStm nested stm@(Let _ _ (Op Alloc{}))+    unAllocStm nested stm@(Let _ _ (Op Alloc {}))       | nested = throwError $ "Cannot handle nested allocation: " ++ pretty stm       | otherwise = return Nothing     unAllocStm _ (Let pat dec e) =@@ -547,53 +667,68 @@      unAllocPattern pat@(Pattern ctx val) =       Pattern <$> maybe bad return (mapM (rephrasePatElem unMem) ctx)-              <*> maybe bad return (mapM (rephrasePatElem unMem) val)-      where bad = Left $ "Cannot handle memory in pattern " ++ pretty pat+        <*> maybe bad return (mapM (rephrasePatElem unMem) val)+      where+        bad = Left $ "Cannot handle memory in pattern " ++ pretty pat -    unAllocOp Alloc{} = Left "unAllocOp: unhandled Alloc"-    unAllocOp (Inner OtherOp{}) = Left "unAllocOp: unhandled OtherOp"+    unAllocOp Alloc {} = Left "unAllocOp: unhandled Alloc"+    unAllocOp (Inner OtherOp {}) = Left "unAllocOp: unhandled OtherOp"     unAllocOp (Inner (SizeOp op)) =       return $ SizeOp op     unAllocOp (Inner (SegOp op)) = SegOp <$> mapSegOpM mapper op-      where mapper = identitySegOpMapper { mapOnSegOpLambda = unAllocLambda-                                         , mapOnSegOpBody = unAllocKernelBody-                                         }+      where+        mapper =+          identitySegOpMapper+            { mapOnSegOpLambda = unAllocLambda,+              mapOnSegOpBody = unAllocKernelBody+            }      unParam p = maybe bad return $ traverse unMem p-      where bad = Left $ "Cannot handle memory-typed parameter '" ++ pretty p ++ "'"+      where+        bad = Left $ "Cannot handle memory-typed parameter '" ++ pretty p ++ "'"      unT t = maybe bad return $ unMem t-      where bad = Left $ "Cannot handle memory type '" ++ pretty t ++ "'"+      where+        bad = Left $ "Cannot handle memory type '" ++ pretty t ++ "'" -    unAlloc' = Mapper { mapOnBody = const unAllocBody-                      , mapOnRetType = unT-                      , mapOnBranchType = unT-                      , mapOnFParam = unParam-                      , mapOnLParam = unParam-                      , mapOnOp = unAllocOp-                      , mapOnSubExp = Right-                      , mapOnVName = Right-                      }+    unAlloc' =+      Mapper+        { mapOnBody = const unAllocBody,+          mapOnRetType = unT,+          mapOnBranchType = unT,+          mapOnFParam = unParam,+          mapOnLParam = unParam,+          mapOnOp = unAllocOp,+          mapOnSubExp = Right,+          mapOnVName = Right+        }  unMem :: MemInfo d u ret -> Maybe (TypeBase (ShapeBase d) u) unMem (MemPrim pt) = Just $ Prim pt unMem (MemArray pt shape u _) = Just $ Array pt shape u-unMem MemMem{} = Nothing+unMem MemMem {} = Nothing  unAllocScope :: Scope KernelsMem -> Scope Kernels.Kernels unAllocScope = M.mapMaybe unInfo-  where unInfo (LetName dec) = LetName <$> unMem dec-        unInfo (FParamName dec) = FParamName <$> unMem dec-        unInfo (LParamName dec) = LParamName <$> unMem dec-        unInfo (IndexName it) = Just $ IndexName it+  where+    unInfo (LetName dec) = LetName <$> unMem dec+    unInfo (FParamName dec) = FParamName <$> unMem dec+    unInfo (LParamName dec) = LParamName <$> unMem dec+    unInfo (IndexName it) = Just $ IndexName it -removeCommonSizes :: Extraction-                  -> [(SubExp, [(VName, Space)])]+removeCommonSizes ::+  Extraction ->+  [(SubExp, [(VName, Space)])] removeCommonSizes = M.toList . foldl' comb mempty . M.toList-  where comb m (mem, (_, size, space)) = M.insertWith (++) size [(mem, space)] m+  where+    comb m (mem, (_, size, space)) = M.insertWith (++) size [(mem, space)] m -sliceKernelSizes :: SubExp -> [SubExp] -> SegSpace -> Stms KernelsMem-                 -> ExpandM (Stms Kernels.Kernels, [VName], [VName])+sliceKernelSizes ::+  SubExp ->+  [SubExp] ->+  SegSpace ->+  Stms KernelsMem ->+  ExpandM (Stms Kernels.Kernels, [VName], [VName]) sliceKernelSizes num_threads sizes space kstms = do   kstms' <- either throwError return $ unAllocKernelsStms kstms   let num_sizes = length sizes@@ -604,55 +739,66 @@   (max_lam, _) <- flip runBinderT kernels_scope $ do     xs <- replicateM num_sizes $ newParam "x" (Prim int64)     ys <- replicateM num_sizes $ newParam "y" (Prim int64)-    (zs, stms) <- localScope (scopeOfLParams $ xs ++ ys) $ collectStms $-                  forM (zip xs ys) $ \(x,y) ->-      letSubExp "z" $ BasicOp $ BinOp (SMax Int64) (Var $ paramName x) (Var $ paramName y)+    (zs, stms) <- localScope (scopeOfLParams $ xs ++ ys) $+      collectStms $+        forM (zip xs ys) $ \(x, y) ->+          letSubExp "z" $ BasicOp $ BinOp (SMax Int64) (Var $ paramName x) (Var $ paramName y)     return $ Lambda (xs ++ ys) (mkBody stms zs) i64s -  flat_gtid_lparam <- Param <$> newVName "flat_gtid" <*> pure (Prim (IntType Int32))+  flat_gtid_lparam <- Param <$> newVName "flat_gtid" <*> pure (Prim (IntType Int64))    (size_lam', _) <- flip runBinderT kernels_scope $ do     params <- replicateM num_sizes $ newParam "x" (Prim int64)-    (zs, stms) <- localScope (scopeOfLParams params <>-                              scopeOfLParams [flat_gtid_lparam]) $ collectStms $ do--      -- Even though this SegRed is one-dimensional, we need to-      -- provide indexes corresponding to the original potentially-      -- multi-dimensional construct.-      let (kspace_gtids, kspace_dims) = unzip $ unSegSpace space-          new_inds = unflattenIndex-                     (map (primExpFromSubExp int32) kspace_dims)-                     (primExpFromSubExp int32 $ Var $ paramName flat_gtid_lparam)-      zipWithM_ letBindNames (map pure kspace_gtids) =<< mapM toExp new_inds+    (zs, stms) <- localScope+      ( scopeOfLParams params+          <> scopeOfLParams [flat_gtid_lparam]+      )+      $ collectStms $ do+        -- Even though this SegRed is one-dimensional, we need to+        -- provide indexes corresponding to the original potentially+        -- multi-dimensional construct.+        let (kspace_gtids, kspace_dims) = unzip $ unSegSpace space+            new_inds =+              unflattenIndex+                (map pe64 kspace_dims)+                (pe64 $ Var $ paramName flat_gtid_lparam)+        zipWithM_ letBindNames (map pure kspace_gtids) =<< mapM toExp new_inds -      mapM_ addStm kstms'-      return sizes+        mapM_ addStm kstms'+        return sizes      localScope (scopeOfSegSpace space) $       Kernels.simplifyLambda (Lambda [flat_gtid_lparam] (Body () stms zs) i64s)    ((maxes_per_thread, size_sums), slice_stms) <- flip runBinderT kernels_scope $ do-    num_threads_64 <- letSubExp "num_threads" $-                      BasicOp $ ConvOp (SExt Int32 Int64) num_threads--    pat <- basicPattern [] <$> replicateM num_sizes-           (newIdent "max_per_thread" $ Prim int64)+    pat <-+      basicPattern []+        <$> replicateM+          num_sizes+          (newIdent "max_per_thread" $ Prim int64) -    w <- letSubExp "size_slice_w" =<<-         foldBinOp (Mul Int32 OverflowUndef) (intConst Int32 1) (segSpaceDims space)+    w <-+      letSubExp "size_slice_w"+        =<< foldBinOp (Mul Int64 OverflowUndef) (intConst Int64 1) (segSpaceDims space) -    thread_space_iota <- letExp "thread_space_iota" $ BasicOp $-                         Iota w (intConst Int32 0) (intConst Int32 1) Int32-    let red_op = SegBinOp Commutative max_lam-                 (replicate num_sizes $ intConst Int64 0) mempty+    thread_space_iota <-+      letExp "thread_space_iota" $+        BasicOp $+          Iota w (intConst Int64 0) (intConst Int64 1) Int64+    let red_op =+          SegBinOp+            Commutative+            max_lam+            (replicate num_sizes $ intConst Int64 0)+            mempty     lvl <- segThread "segred" -    addStms =<< mapM renameStm =<<-      nonSegRed lvl pat w [red_op] size_lam' [thread_space_iota]+    addStms =<< mapM renameStm+      =<< nonSegRed lvl pat w [red_op] size_lam' [thread_space_iota]      size_sums <- forM (patternNames pat) $ \threads_max ->       letExp "size_sum" $-      BasicOp $ BinOp (Mul Int64 OverflowUndef) (Var threads_max) num_threads_64+        BasicOp $ BinOp (Mul Int64 OverflowUndef) (Var threads_max) num_threads      return (patternNames pat, size_sums) 
src/Futhark/Pass/ExplicitAllocations.hs view
@@ -1,976 +1,1181 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE DefaultSignatures #-}--- | A generic transformation for adding memory allocations to a--- Futhark program.  Specialised by specific representations in--- submodules.-module Futhark.Pass.ExplicitAllocations-       ( explicitAllocationsGeneric-       , explicitAllocationsInStmsGeneric-       , ExpHint(..)-       , defaultExpHints--       , Allocable-       , Allocator(..)-       , AllocM-       , AllocEnv(..)-       , SizeSubst(..)-       , allocInStms-       , allocForArray--       , simplifiable-       , arraySizeInBytesExp--       , mkLetNamesB'-       , mkLetNamesB''--       -- * Module re-exports-       ---       -- These are highly likely to be needed by any downstream-       -- users.-       , module Control.Monad.Reader-       , module Futhark.MonadFreshNames-       , module Futhark.Pass-       , module Futhark.Tools-       )-where--import Control.Monad.State-import Control.Monad.Writer-import Control.Monad.Reader-import Control.Monad.RWS.Strict-import qualified Data.Map.Strict as M-import qualified Data.Set as S-import Data.Maybe-import Data.List (foldl', zip4, partition, sort)--import qualified Futhark.Analysis.UsageTable as UT-import Futhark.Optimise.Simplify.Lore (mkWiseBody)-import Futhark.MonadFreshNames-import Futhark.IR.Mem-import qualified Futhark.IR.Mem.IxFun as IxFun-import Futhark.Tools-import Futhark.Optimise.Simplify.Engine (SimpleOps (..))-import qualified Futhark.Optimise.Simplify.Engine as Engine-import Futhark.Pass-import Futhark.Util (splitFromEnd, takeLast)--data AllocStm = SizeComputation VName (PrimExp VName)-              | Allocation VName SubExp Space-              | ArrayCopy VName VName-                    deriving (Eq, Ord, Show)--bindAllocStm :: (MonadBinder m, Op (Lore m) ~ MemOp inner) =>-                AllocStm -> m ()-bindAllocStm (SizeComputation name pe) =-  letBindNames [name] =<< toExp (coerceIntPrimExp Int64 pe)-bindAllocStm (Allocation name size space) =-  letBindNames [name] $ Op $ Alloc size space-bindAllocStm (ArrayCopy name src) =-  letBindNames [name] $ BasicOp $ Copy src--class (MonadFreshNames m, HasScope lore m, Mem lore) =>-      Allocator lore m where-  addAllocStm :: AllocStm -> m ()-  askDefaultSpace :: m Space--  default addAllocStm :: (Allocable fromlore lore,-                          m ~ AllocM fromlore lore)-                      => AllocStm -> m ()-  addAllocStm (SizeComputation name se) =-    letBindNames [name] =<< toExp (coerceIntPrimExp Int64 se)-  addAllocStm (Allocation name size space) =-    letBindNames [name] $ Op $ allocOp size space-  addAllocStm (ArrayCopy name src) =-    letBindNames [name] $ BasicOp $ Copy src--  -- | The subexpression giving the number of elements we should-  -- allocate space for.  See 'ChunkMap' comment.-  dimAllocationSize :: SubExp -> m SubExp--  default dimAllocationSize :: m ~ AllocM fromlore lore-                               => SubExp -> m SubExp-  dimAllocationSize (Var v) =-    -- It is important to recurse here, as the substitution may itself-    -- be a chunk size.-    maybe (return $ Var v) dimAllocationSize =<< asks (M.lookup v . chunkMap)-  dimAllocationSize size =-    return size--  -- | Get those names that are known to be constants at run-time.-  askConsts :: m (S.Set VName)--  expHints :: Exp lore -> m [ExpHint]-  expHints = defaultExpHints--allocateMemory :: Allocator lore m =>-                  String -> SubExp -> Space -> m VName-allocateMemory desc size space = do-  v <- newVName desc-  addAllocStm $ Allocation v size space-  return v--computeSize :: Allocator lore m =>-               String -> PrimExp VName -> m SubExp-computeSize desc se = do-  v <- newVName desc-  addAllocStm $ SizeComputation v se-  return $ Var v--type Allocable fromlore tolore =-  (PrettyLore fromlore, PrettyLore tolore,-   Mem tolore,-   FParamInfo fromlore ~ DeclType,-   LParamInfo fromlore ~ Type,-   BranchType fromlore ~ ExtType,-   RetType fromlore ~ DeclExtType,-   BodyDec fromlore ~ (),-   BodyDec tolore ~ (),-   ExpDec tolore ~ (),-   SizeSubst (Op tolore),-   BinderOps tolore)---- | A mapping from chunk names to their maximum size.  XXX FIXME--- HACK: This is part of a hack to add loop-invariant allocations to--- reduce kernels, because memory expansion does not use range--- analysis yet (it should).-type ChunkMap = M.Map VName SubExp--data AllocEnv fromlore tolore  =-  AllocEnv { chunkMap :: ChunkMap-           , aggressiveReuse :: Bool-             -- ^ Aggressively try to reuse memory in do-loops --             -- should be True inside kernels, False outside.-           , allocSpace :: Space-             -- ^ When allocating memory, put it in this memory space.-             -- This is primarily used to ensure that group-wide-             -- statements store their results in local memory.-           , envConsts :: S.Set VName-             -- ^ The set of names that are known to be constants at-             -- kernel compile time.-           , allocInOp :: Op fromlore -> AllocM fromlore tolore (Op tolore)-           , envExpHints :: Exp tolore -> AllocM fromlore tolore [ExpHint]-           }---- | Monad for adding allocations to an entire program.-newtype AllocM fromlore tolore a =-  AllocM (BinderT tolore (ReaderT (AllocEnv fromlore tolore) (State VNameSource)) a)-  deriving (Applicative, Functor, Monad,-             MonadFreshNames,-             HasScope tolore,-             LocalScope tolore,-             MonadReader (AllocEnv fromlore tolore))--instance (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>-         MonadBinder (AllocM fromlore tolore) where-  type Lore (AllocM fromlore tolore) = tolore--  mkExpDecM _ _ = return ()--  mkLetNamesM names e = do-    pat <- patternWithAllocations names e-    return $ Let pat (defAux ()) e--  mkBodyM bnds res = return $ Body () bnds res--  addStms = AllocM . addStms-  collectStms (AllocM m) = AllocM $ collectStms m--instance (Allocable fromlore tolore) =>-         Allocator tolore (AllocM fromlore tolore) where-  expHints e = do-    f <- asks envExpHints-    f e-  askDefaultSpace = asks allocSpace--  askConsts = asks envConsts--runAllocM :: MonadFreshNames m =>-             (Op fromlore -> AllocM fromlore tolore (Op tolore))-          -> (Exp tolore -> AllocM fromlore tolore [ExpHint])-          -> AllocM fromlore tolore a -> m a-runAllocM handleOp hints (AllocM m) =-  fmap fst $ modifyNameSource $ runState $ runReaderT (runBinderT m mempty) env-  where env = AllocEnv { chunkMap = mempty-                       , aggressiveReuse = False-                       , allocSpace = DefaultSpace-                       , envConsts = mempty-                       , allocInOp = handleOp-                       , envExpHints = hints-                       }---- | Monad for adding allocations to a single pattern.-newtype PatAllocM lore a = PatAllocM (RWS-                                      (Scope lore)-                                      [AllocStm]-                                      VNameSource-                                      a)-                    deriving (Applicative, Functor, Monad,-                              HasScope lore,-                              MonadWriter [AllocStm],-                              MonadFreshNames)--instance Mem lore => Allocator lore (PatAllocM lore) where-  addAllocStm = tell . pure-  dimAllocationSize = return-  askDefaultSpace = return DefaultSpace-  askConsts = pure mempty--runPatAllocM :: MonadFreshNames m =>-                PatAllocM lore a -> Scope lore-             -> m (a, [AllocStm])-runPatAllocM (PatAllocM m) mems =-  modifyNameSource $ frob . runRWS m mems-  where frob (a,s,w) = ((a,w),s)--arraySizeInBytesExp :: Type -> PrimExp VName-arraySizeInBytesExp t =-  foldl' (*)-  (ValueExp $ IntValue $ Int64Value $ primByteSize $ elemType t) $-  map (sExt Int64 .primExpFromSubExp int32) (arrayDims t)--arraySizeInBytesExpM :: Allocator lore m => Type -> m (PrimExp VName)-arraySizeInBytesExpM t = do-  dims <- mapM dimAllocationSize (arrayDims t)-  let dim_prod_i32 = product $ map (sExt Int64 . primExpFromSubExp int32) dims-  let elm_size_i64 = ValueExp $ IntValue $ Int64Value $ primByteSize $ elemType t-  return $ product [ dim_prod_i32, elm_size_i64 ]--arraySizeInBytes :: Allocator lore m => Type -> m SubExp-arraySizeInBytes = computeSize "bytes" <=< arraySizeInBytesExpM---- | Allocate memory for a value of the given type.-allocForArray :: Allocator lore m =>-                 Type -> Space -> m VName-allocForArray t space = do-  size <- arraySizeInBytes t-  allocateMemory "mem" size space--allocsForStm :: (Allocator lore m, ExpDec lore ~ ()) =>-                [Ident] -> [Ident] -> Exp lore-             -> m (Stm lore)-allocsForStm sizeidents validents e = do-  rts <- expReturns e-  hints <- expHints e-  (ctxElems, valElems) <- allocsForPattern sizeidents validents rts hints-  return $ Let (Pattern ctxElems valElems) (defAux ()) e--patternWithAllocations :: (Allocator lore m, ExpDec lore ~ ()) =>-                          [VName]-                       -> Exp lore-                       -> m (Pattern lore)-patternWithAllocations names e = do-  (ts',sizes) <- instantiateShapes' =<< expExtType e-  let identForBindage name t =-        pure $ Ident name t-  vals <- sequence [ identForBindage name t | (name, t) <- zip names ts' ]-  stmPattern <$> allocsForStm sizes vals e--allocsForPattern :: Allocator lore m =>-                    [Ident] -> [Ident] -> [ExpReturns] -> [ExpHint]-                 -> m ([PatElem lore],-                       [PatElem lore])-allocsForPattern sizeidents validents rts hints = do-  let sizes' = [ PatElem size $ MemPrim int32 | size <- map identName sizeidents ]-  (vals, (exts, mems)) <--    runWriterT $ forM (zip3 validents rts hints) $ \(ident, rt, hint) -> do-      let shape = arrayShape $ identType ident-      case rt of-        MemPrim _ -> do-          summary <- lift $ summaryForBindage (identType ident) hint-          return $ PatElem (identName ident) summary--        MemMem space ->-          return $ PatElem (identName ident) $-          MemMem space--        MemArray bt _ u (Just (ReturnsInBlock mem extixfun)) -> do-          (patels, ixfn) <- instantiateExtIxFun ident extixfun-          tell (patels, [])--          return $ PatElem (identName ident) $-            MemArray bt shape u $-            ArrayIn mem ixfn--        MemArray _ extshape _ Nothing-          | Just _ <- knownShape extshape -> do-            summary <- lift $ summaryForBindage (identType ident) hint-            return $ PatElem (identName ident) summary--        MemArray bt _ u (Just (ReturnsNewBlock space _ extixfn)) -> do-          -- treat existential index function first-          (patels, ixfn) <- instantiateExtIxFun ident extixfn-          tell (patels, [])--          memid <- lift $ mkMemIdent ident space-          tell ([], [PatElem (identName memid) $ MemMem space])-          return $ PatElem (identName ident) $ MemArray bt shape u $-            ArrayIn (identName memid) ixfn--        _ -> error "Impossible case reached in allocsForPattern!"--  return (sizes' <> exts <> mems,-          vals)-  where knownShape = mapM known . shapeDims-        known (Free v) = Just v-        known Ext{} = Nothing--        mkMemIdent :: (MonadFreshNames m) => Ident -> Space -> m Ident-        mkMemIdent ident space = do-          let memname = baseString (identName ident) <> "_mem"-          newIdent memname $ Mem space--        instantiateExtIxFun :: MonadFreshNames m =>-                               Ident -> ExtIxFun ->-                               m ([PatElemT (MemInfo d u ret)], IxFun)-        instantiateExtIxFun idd ext_ixfn = do-          let isAndPtps = S.toList $-                          foldMap onlyExts $-                          foldMap leafExpTypes ext_ixfn--          -- Find the existentials that reuse the sizeidents, and-          -- those that need new pattern elements.  Assumes that the-          -- Exts form a contiguous interval of integers.-          let (size_exts, new_exts) =-                span ((<length sizeidents) . fst) $ sort isAndPtps-          (new_substs, patels) <--            fmap unzip $ forM new_exts $ \(i, t) -> do-            v <- newVName $ baseString (identName idd) <> "_ixfn"-            return ((Ext i, LeafExp (Free v) t),-                    PatElem v $ MemPrim t)-          let size_substs = zipWith (\(i, t) ident ->-                                    (Ext i, LeafExp (Free (identName ident)) t))-                            size_exts sizeidents-              substs = M.fromList $ new_substs <> size_substs-          ixfn <- instantiateIxFun $ IxFun.substituteInIxFun substs ext_ixfn--          return (patels, ixfn)--onlyExts :: (Ext a, PrimType) -> S.Set (Int, PrimType)-onlyExts (Free _, _) = S.empty-onlyExts (Ext i, t) = S.singleton (i, t)---instantiateIxFun :: Monad m => ExtIxFun -> m IxFun-instantiateIxFun = traverse $ traverse inst-  where inst Ext{} = error "instantiateIxFun: not yet"-        inst (Free x) = return x--summaryForBindage :: Allocator lore m =>-                     Type -> ExpHint-                  -> m (MemBound NoUniqueness)-summaryForBindage (Prim bt) _ =-  return $ MemPrim bt-summaryForBindage (Mem space) _ =-  return $ MemMem space-summaryForBindage t@(Array bt shape u) NoHint = do-  m <- allocForArray t =<< askDefaultSpace-  return $ directIxFun bt shape u m t-summaryForBindage t (Hint ixfun space) = do-  let bt = elemType t-  bytes <- computeSize "bytes" $-           product [product $ map (sExt Int64) $ IxFun.base ixfun,-                    fromIntegral (primByteSize (elemType t)::Int64)]-  m <- allocateMemory "mem" bytes space-  return $ MemArray bt (arrayShape t) NoUniqueness $ ArrayIn m ixfun--lookupMemSpace :: (HasScope lore m, Monad m) => VName -> m Space-lookupMemSpace v = do-  t <- lookupType v-  case t of-    Mem space -> return space-    _ -> error $ "lookupMemSpace: " ++ pretty v ++ " is not a memory block."--directIxFun :: PrimType -> Shape -> u -> VName -> Type -> MemBound u-directIxFun bt shape u mem t =-  let ixf = IxFun.iota $ map (primExpFromSubExp int32) $ arrayDims t-  in MemArray bt shape u $ ArrayIn mem ixf---allocInFParams :: (Allocable fromlore tolore) =>-                  [(FParam fromlore, Space)] ->-                  ([FParam tolore] -> AllocM fromlore tolore a)-               -> AllocM fromlore tolore a-allocInFParams params m = do-  (valparams, (ctxparams, memparams)) <--    runWriterT $ mapM (uncurry allocInFParam) params-  let params' = ctxparams <> memparams <> valparams-      summary = scopeOfFParams params'-  localScope summary $ m params'--allocInFParam :: (Allocable fromlore tolore) =>-                 FParam fromlore-              -> Space-              -> WriterT ([FParam tolore], [FParam tolore])-                 (AllocM fromlore tolore) (FParam tolore)-allocInFParam param pspace =-  case paramDeclType param of-    Array bt shape u -> do-      let memname = baseString (paramName param) <> "_mem"-          ixfun = IxFun.iota $ map (primExpFromSubExp int32) $ shapeDims shape-      mem <- lift $ newVName memname-      tell ([], [Param mem $ MemMem pspace])-      return param { paramDec =  MemArray bt shape u $ ArrayIn mem ixfun }-    Prim bt ->-      return param { paramDec = MemPrim bt }-    Mem space ->-      return param { paramDec = MemMem space }--allocInMergeParams :: (Allocable fromlore tolore,-                       Allocator tolore (AllocM fromlore tolore)) =>-                      [(FParam fromlore,SubExp)]-                   -> ([FParam tolore]-                       -> [FParam tolore]-                       -> ([SubExp] -> AllocM fromlore tolore ([SubExp], [SubExp]))-                       -> AllocM fromlore tolore a)-                   -> AllocM fromlore tolore a-allocInMergeParams merge m = do-  ((valparams, handle_loop_subexps), (ctx_params, mem_params)) <--    runWriterT $ unzip <$> mapM allocInMergeParam merge-  let mergeparams' = ctx_params <> mem_params <> valparams-      summary = scopeOfFParams mergeparams'--      mk_loop_res ses = do-        (valargs, (ctxargs, memargs)) <--          runWriterT $ zipWithM ($) handle_loop_subexps ses-        return (ctxargs <> memargs, valargs)--  localScope summary $ m (ctx_params <> mem_params) valparams mk_loop_res-  where-    allocInMergeParam :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>-                         (Param DeclType, SubExp) ->-                         WriterT-                         ([FParam tolore], [FParam tolore])-                         (AllocM fromlore tolore)-                         (FParam tolore, SubExp -> WriterT ([SubExp], [SubExp]) (AllocM fromlore tolore) SubExp)-    allocInMergeParam (mergeparam, Var v)-      | Array bt shape u <- paramDeclType mergeparam = do-          (mem', _) <- lift $ lookupArraySummary v-          mem_space <- lift $ lookupMemSpace mem'--          (_, ext_ixfun, substs, _) <- lift $ existentializeArray mem_space v--          (ctx_params, param_ixfun_substs) <--            unzip <$>-            mapM (\primExp -> do-                     let pt = primExpType primExp-                     vname <- lift $ newVName "ctx_param_ext"-                     return (Param vname $ MemPrim pt,-                             fmap Free $ primExpFromSubExp int32 $ Var vname))-            substs--          tell (ctx_params, [])--          param_ixfun <- instantiateIxFun $-                         IxFun.substituteInIxFun (M.fromList $ zip (fmap Ext [0..]) param_ixfun_substs)-                         ext_ixfun--          mem_name <- newVName "mem_param"-          tell ([], [Param mem_name $ MemMem mem_space])--          return (mergeparam { paramDec = MemArray bt shape u $ ArrayIn mem_name param_ixfun },-                  ensureArrayIn mem_space)--    allocInMergeParam (mergeparam, _) = doDefault mergeparam =<< lift askDefaultSpace--    doDefault mergeparam space = do-      mergeparam' <- allocInFParam mergeparam space-      return (mergeparam', linearFuncallArg (paramType mergeparam) space)----- Returns the existentialized index function, the list of substituted values and the memory location.-existentializeArray :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>-                       Space -> VName -> AllocM fromlore tolore (SubExp, ExtIxFun, [PrimExp VName], VName)-existentializeArray space v = do-  (mem', ixfun) <- lookupArraySummary v-  sp <- lookupMemSpace mem'--  let (ext_ixfun', substs') = runState (IxFun.existentialize ixfun) []--  case (ext_ixfun', sp == space) of-    (Just x, True) -> return (Var v, x, substs', mem')-    _ -> do-      (mem, subexp) <- allocLinearArray space (baseString v) v-      ixfun' <- fromJust <$> subExpIxFun subexp-      let (ext_ixfun, substs) = runState (IxFun.existentialize ixfun') []-      return (subexp, fromJust ext_ixfun, substs, mem)----ensureArrayIn :: (Allocable fromlore tolore,-                  Allocator tolore (AllocM fromlore tolore)) =>-                 Space -> SubExp-              -> WriterT ([SubExp], [SubExp]) (AllocM fromlore tolore) SubExp-ensureArrayIn _ (Constant v) =-  error $ "ensureArrayIn: " ++ pretty v ++ " cannot be an array."-ensureArrayIn space (Var v) = do-  (sub_exp, _, substs, mem) <- lift $ existentializeArray space v-  (ctx_vals, _) <--    unzip <$>-    mapM (\s -> do-             vname <- lift $ letExp "ctx_val" =<< toExp s-             return (Var vname, fmap Free $ primExpFromSubExp int32 $ Var vname))-    substs--  tell (ctx_vals, [Var mem])--  return sub_exp--ensureDirectArray :: (Allocable fromlore tolore,-                      Allocator tolore (AllocM fromlore tolore)) =>-                     Maybe Space -> VName -> AllocM fromlore tolore (VName, SubExp)-ensureDirectArray space_ok v = do-  (mem, ixfun) <- lookupArraySummary v-  mem_space <- lookupMemSpace mem-  default_space <- askDefaultSpace-  if IxFun.isDirect ixfun && maybe True (==mem_space) space_ok-    then return (mem, Var v)-    else needCopy (fromMaybe default_space space_ok)-  where needCopy space =-          -- We need to do a new allocation, copy 'v', and make a new-          -- binding for the size of the memory block.-          allocLinearArray space (baseString v) v--allocLinearArray :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>-                    Space -> String -> VName-                 -> AllocM fromlore tolore (VName, SubExp)-allocLinearArray space s v = do-  t <- lookupType v-  mem <- allocForArray t space-  v' <- newIdent (s ++ "_linear") t-  let ixfun = directIxFun (elemType t) (arrayShape t) NoUniqueness mem t-  let pat = Pattern [] [PatElem (identName v') ixfun]-  addStm $ Let pat (defAux ()) $ BasicOp $ Copy v-  return (mem, Var $ identName v')--funcallArgs :: (Allocable fromlore tolore,-                Allocator tolore (AllocM fromlore tolore)) =>-               [(SubExp,Diet)] -> AllocM fromlore tolore [(SubExp,Diet)]-funcallArgs args = do-  (valargs, (ctx_args, mem_and_size_args)) <- runWriterT $ forM args $ \(arg,d) -> do-    t <- lift $ subExpType arg-    space <- lift askDefaultSpace-    arg' <- linearFuncallArg t space arg-    return (arg', d)-  return $ map (,Observe) (ctx_args <> mem_and_size_args) <> valargs--linearFuncallArg :: (Allocable fromlore tolore,-                     Allocator tolore (AllocM fromlore tolore)) =>-                    Type -> Space -> SubExp-                 -> WriterT ([SubExp], [SubExp]) (AllocM fromlore tolore) SubExp-linearFuncallArg Array{} space (Var v) = do-  (mem, arg') <- lift $ ensureDirectArray (Just space) v-  tell ([], [Var mem])-  return arg'-linearFuncallArg _ _ arg =-  return arg--explicitAllocationsGeneric :: (Allocable fromlore tolore,-                               Allocator tolore (AllocM fromlore tolore)) =>-                              (Op fromlore -> AllocM fromlore tolore (Op tolore))-                           -> (Exp tolore -> AllocM fromlore tolore [ExpHint])-                           -> Pass fromlore tolore-explicitAllocationsGeneric handleOp hints =-  Pass "explicit allocations" "Transform program to explicit memory representation" $-  intraproceduralTransformationWithConsts onStms allocInFun-  where onStms stms = runAllocM handleOp hints $ allocInStms stms pure--        allocInFun consts (FunDef entry attrs fname rettype params fbody) =-          runAllocM handleOp hints $ inScopeOf consts $-          allocInFParams (zip params $ repeat DefaultSpace) $ \params' -> do-          fbody' <- insertStmsM $ allocInFunBody-                    (map (const $ Just DefaultSpace) rettype) fbody-          return $ FunDef entry attrs fname (memoryInDeclExtType rettype) params' fbody'--explicitAllocationsInStmsGeneric :: (MonadFreshNames m, HasScope tolore m,-                                     Allocable fromlore tolore) =>-                                    (Op fromlore -> AllocM fromlore tolore (Op tolore))-                                 -> (Exp tolore -> AllocM fromlore tolore [ExpHint])-                                 -> Stms fromlore -> m (Stms tolore)-explicitAllocationsInStmsGeneric handleOp hints stms = do-  scope <- askScope-  runAllocM handleOp hints $ localScope scope $ allocInStms stms return--memoryInDeclExtType :: [DeclExtType] -> [FunReturns]-memoryInDeclExtType ts = evalState (mapM addMem ts) $ startOfFreeIDRange ts-  where addMem (Prim t) = return $ MemPrim t-        addMem Mem{} = error "memoryInDeclExtType: too much memory"-        addMem (Array bt shape u) = do-          i <- get <* modify (+1)-          return $ MemArray bt shape u $ ReturnsNewBlock DefaultSpace i $-            IxFun.iota $ map convert $ shapeDims shape--        convert (Ext i) = LeafExp (Ext i) int32-        convert (Free v) = Free <$> primExpFromSubExp int32 v--startOfFreeIDRange :: [TypeBase ExtShape u] -> Int-startOfFreeIDRange = S.size . shapeContext--bodyReturnMemCtx :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>-                    SubExp -> AllocM fromlore tolore [SubExp]-bodyReturnMemCtx Constant{} =-  return []-bodyReturnMemCtx (Var v) = do-  info <- lookupMemInfo v-  case info of-    MemPrim{} -> return []-    MemMem{} -> return [] -- should not happen-    MemArray _ _ _ (ArrayIn mem _) -> return [Var mem]--allocInFunBody :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>-                  [Maybe Space] -> Body fromlore -> AllocM fromlore tolore (Body tolore)-allocInFunBody space_oks (Body _ bnds res) =-  allocInStms bnds $ \bnds' -> do-    (res'', allocs) <- collectStms $ do-      res' <- zipWithM ensureDirect space_oks' res-      let (ctx_res, val_res) = splitFromEnd num_vals res'-      mem_ctx_res <- concat <$> mapM bodyReturnMemCtx val_res-      return $ ctx_res <> mem_ctx_res <> val_res-    return $ Body () (bnds'<>allocs) res''-  where num_vals = length space_oks-        space_oks' = replicate (length res - num_vals) Nothing ++ space_oks--ensureDirect :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>-                Maybe Space -> SubExp -> AllocM fromlore tolore SubExp-ensureDirect _ se@Constant{} = return se-ensureDirect space_ok (Var v) = do-  bt <- primType <$> lookupType v-  if bt-    then return $ Var v-    else do (_, v') <- ensureDirectArray space_ok v-            return v'--allocInStms :: (Allocable fromlore tolore) =>-               Stms fromlore -> (Stms tolore -> AllocM fromlore tolore a)-            -> AllocM fromlore tolore a-allocInStms origstms m = allocInStms' (stmsToList origstms) mempty-  where allocInStms' [] stms' =-          m stms'-        allocInStms' (x:xs) stms' = do-          allocstms <- allocInStm' x-          localScope (scopeOf allocstms) $ do-            let stms_substs = foldMap sizeSubst allocstms-                stms_consts = foldMap stmConsts allocstms-                f env = env { chunkMap = stms_substs <> chunkMap env-                            , envConsts = stms_consts <> envConsts env-                            }-            local f $ allocInStms' xs (stms'<>allocstms)-        allocInStm' stm =-          collectStms_ $ auxing (stmAux stm) $ allocInStm stm--allocInStm :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>-              Stm fromlore -> AllocM fromlore tolore ()-allocInStm (Let (Pattern sizeElems valElems) _ e) = do-  e' <- allocInExp e-  let sizeidents = map patElemIdent sizeElems-      validents = map patElemIdent valElems-  bnd <- allocsForStm sizeidents validents e'-  addStm bnd--allocInExp :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>-              Exp fromlore -> AllocM fromlore tolore (Exp tolore)-allocInExp (DoLoop ctx val form (Body () bodybnds bodyres)) =-  allocInMergeParams ctx $ \_ ctxparams' _ ->-  allocInMergeParams val $-  \new_ctx_params valparams' mk_loop_val -> do-  form' <- allocInLoopForm form-  localScope (scopeOf form') $ do-    (valinit_ctx, valinit') <- mk_loop_val valinit-    body' <- insertStmsM $ allocInStms bodybnds $ \bodybnds' -> do-      ((val_ses,valres'),val_retbnds) <- collectStms $ mk_loop_val valres-      return $ Body () (bodybnds'<>val_retbnds) (ctxres++val_ses++valres')-    return $-      DoLoop-      (zip (ctxparams'++new_ctx_params) (ctxinit++valinit_ctx))-      (zip valparams' valinit')-      form' body'-  where (_ctxparams, ctxinit) = unzip ctx-        (_valparams, valinit) = unzip val-        (ctxres, valres) = splitAt (length ctx) bodyres-allocInExp (Apply fname args rettype loc) = do-  args' <- funcallArgs args-  return $ Apply fname args' (memoryInDeclExtType rettype) loc-allocInExp (If cond tbranch0 fbranch0 (IfDec rets ifsort)) = do-  let num_rets = length rets-  -- switch to the explicit-mem rep, but do nothing about results-  (tbranch, tm_ixfs) <- allocInIfBody num_rets tbranch0-  (fbranch, fm_ixfs) <- allocInIfBody num_rets fbranch0-  tspaces <- mkSpaceOks num_rets tbranch-  fspaces <- mkSpaceOks num_rets fbranch-  -- try to generalize (antiunify) the index functions of the then and else bodies-  let sp_substs = zipWith generalize (zip tspaces tm_ixfs) (zip fspaces fm_ixfs)-      (spaces, subs) = unzip sp_substs-      tsubs = map (selectSub fst) subs-      fsubs = map (selectSub snd) subs-  (tbranch', trets) <- addResCtxInIfBody rets tbranch spaces tsubs-  (fbranch', frets) <- addResCtxInIfBody rets fbranch spaces fsubs-  if frets /= trets then error "In allocInExp, IF case: antiunification of then/else produce different ExtInFn!"-    else do -- above is a sanity check; implementation continues on else branch-    let res_then = bodyResult tbranch'-        res_else = bodyResult fbranch'-        size_ext = length res_then - length trets-        (ind_ses0, r_then_else) =-            partition (\(r_then, r_else, _) -> r_then == r_else) $-            zip3 res_then res_else [0 .. size_ext - 1]-        (r_then_ext, r_else_ext, _) = unzip3 r_then_else-        ind_ses = zipWith (\(se, _, i) k -> (i-k, se)) ind_ses0-                  [0 .. length ind_ses0 - 1]-        rets'' = foldl (\acc (i, se) -> fixExt i se acc) trets ind_ses-        tbranch'' = tbranch' { bodyResult = r_then_ext ++ drop size_ext res_then }-        fbranch'' = fbranch' { bodyResult = r_else_ext ++ drop size_ext res_else }-        res_if_expr = If cond tbranch'' fbranch'' $ IfDec rets'' ifsort-    return res_if_expr-      where generalize :: (Maybe Space, Maybe IxFun) -> (Maybe Space, Maybe IxFun)-                       -> (Maybe Space, Maybe (ExtIxFun, [(PrimExp VName, PrimExp VName)]))-            generalize (Just sp1, Just ixf1) (Just sp2, Just ixf2) =-              if sp1 /= sp2 then (Just sp1, Nothing)-              else case IxFun.leastGeneralGeneralization ixf1 ixf2 of-                Just (ixf, m) -> (Just sp1, Just (ixf, m))-                Nothing -> (Just sp1, Nothing)-            generalize (mbsp1, _) _ = (mbsp1, Nothing)--            selectSub :: ((a, a) -> a) -> Maybe (ExtIxFun, [(a, a)]) ->-                         Maybe (ExtIxFun, [a])-            selectSub f (Just (ixfn, m)) = Just (ixfn, map f m)-            selectSub _ Nothing = Nothing--            -- | Just introduces the new representation (index functions); but-            -- does not unify (e.g., does not ensures direct); implementation-            -- extends `allocInBodyNoDirect`, but also return `MemBind`-            allocInIfBody :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>-                             Int -> Body fromlore -> AllocM fromlore tolore (Body tolore, [Maybe IxFun])-            allocInIfBody num_vals (Body _ bnds res) =-              allocInStms bnds $ \bnds' -> do-                let (_, val_res) = splitFromEnd num_vals res-                mem_ixfs <- mapM subExpIxFun val_res-                return (Body () bnds' res, mem_ixfs)-allocInExp e = mapExpM alloc e-  where alloc =-          identityMapper { mapOnBody = error "Unhandled Body in ExplicitAllocations"-                         , mapOnRetType = error "Unhandled RetType in ExplicitAllocations"-                         , mapOnBranchType = error "Unhandled BranchType in ExplicitAllocations"-                         , mapOnFParam = error "Unhandled FParam in ExplicitAllocations"-                         , mapOnLParam = error "Unhandled LParam in ExplicitAllocations"-                         , mapOnOp = \op -> do handle <- asks allocInOp-                                               handle op-                         }----subExpIxFun :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>-                  SubExp -> AllocM fromlore tolore (Maybe IxFun)-subExpIxFun Constant{} = return Nothing-subExpIxFun (Var v) = do-  info <- lookupMemInfo v-  case info of-    MemArray _ptp _shp _u (ArrayIn _ ixf) -> return $ Just ixf-    _ -> return Nothing---addResCtxInIfBody :: (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>-                     [ExtType] -> Body tolore -> [Maybe Space] ->-                     [Maybe (ExtIxFun, [PrimExp VName])] ->-                     AllocM fromlore tolore (Body tolore, [BodyReturns])-addResCtxInIfBody ifrets (Body _ bnds res) spaces substs = do-  let num_vals = length ifrets-      (ctx_res, val_res) = splitFromEnd num_vals res-  ((res', bodyrets'), all_body_stms) <- collectStms $ do-    mapM_ addStm bnds-    (val_res', ext_ses_res, mem_ctx_res, bodyrets, total_existentials) <--      foldM helper ([], [], [], [], length ctx_res) (zip4 ifrets val_res substs spaces)-    return (ctx_res <> ext_ses_res <> mem_ctx_res <> val_res',-             -- We need to adjust the ReturnsNewBlock existentials, because they-             -- should always be numbered _after_ all other existentials in the-             -- return values.-            reverse $ fst $ foldl adjustNewBlockExistential ([], total_existentials) bodyrets)-  body' <- mkBodyM all_body_stms res'-  return (body', bodyrets')-    where-      helper (res_acc, ext_acc, ctx_acc, br_acc, k) (ifr, r, mbixfsub, sp) =-        case mbixfsub of-          Nothing -> do-            -- does NOT generalize/antiunify; ensure direct-            r' <- ensureDirect sp r-            mem_ctx_r <- bodyReturnMemCtx r'-            let body_ret = inspect ifr sp-            return (res_acc ++ [r'],-                    ext_acc,-                    ctx_acc ++ mem_ctx_r,-                    br_acc ++ [body_ret],-                    k)-          Just (ixfn, m) -> do -- generalizes-            let i = length m-            ext_ses <- mapM (toSubExp "ixfn_exist") m-            mem_ctx_r <- bodyReturnMemCtx r-            let sp' = fromMaybe DefaultSpace sp-                ixfn' = fmap (adjustExtPE k) ixfn-                exttp = case ifr of-                          Array pt shp' u ->-                            MemArray pt shp' u $-                            ReturnsNewBlock sp' 0 ixfn'-                          _ -> error "Impossible case reached in addResCtxInIfBody"-            return (res_acc ++ [r],-                    ext_acc ++ ext_ses,-                    ctx_acc ++ mem_ctx_r,-                    br_acc ++ [exttp],-                    k + i)--      adjustNewBlockExistential :: ([BodyReturns], Int) -> BodyReturns -> ([BodyReturns], Int)-      adjustNewBlockExistential (acc, k) (MemArray pt shp u (ReturnsNewBlock space _ ixfun)) =-        (MemArray pt shp u (ReturnsNewBlock space k ixfun) : acc, k + 1)-      adjustNewBlockExistential (acc, k) x = (x : acc, k)--      inspect (Array pt shape u) space =-        let space' = fromMaybe DefaultSpace space-            bodyret = MemArray pt shape u $ ReturnsNewBlock space' 0 $-              IxFun.iota $ map convert $ shapeDims shape-        in bodyret-      inspect (Prim pt) _ = MemPrim pt-      inspect (Mem space) _ = MemMem space--      convert (Ext i) = LeafExp (Ext i) int32-      convert (Free v) = Free <$> primExpFromSubExp int32 v--      adjustExtV :: Int -> Ext VName -> Ext VName-      adjustExtV _ (Free v) = Free v-      adjustExtV k (Ext i) = Ext (k + i)--      adjustExtPE :: Int -> PrimExp (Ext VName) -> PrimExp (Ext VName)-      adjustExtPE k = fmap (adjustExtV k)--mkSpaceOks :: (Mem tolore, LocalScope tolore m) =>-              Int -> Body tolore -> m [Maybe Space]-mkSpaceOks num_vals (Body _ stms res) =-  inScopeOf stms $-  mapM mkSpaceOK $ takeLast num_vals res-  where mkSpaceOK (Var v) = do-          v_info <- lookupMemInfo v-          case v_info of MemArray _ _ _ (ArrayIn mem _) -> do-                           mem_info <- lookupMemInfo mem-                           case mem_info of MemMem space -> return $ Just space-                                            _ -> return Nothing-                         _ -> return Nothing-        mkSpaceOK _ = return Nothing--allocInLoopForm :: (Allocable fromlore tolore,-                    Allocator tolore (AllocM fromlore tolore)) =>-                   LoopForm fromlore -> AllocM fromlore tolore (LoopForm tolore)-allocInLoopForm (WhileLoop v) = return $ WhileLoop v-allocInLoopForm (ForLoop i it n loopvars) =-  ForLoop i it n <$> mapM allocInLoopVar loopvars-  where allocInLoopVar (p,a) = do-          (mem, ixfun) <- lookupArraySummary a-          case paramType p of-            Array bt shape u -> do-              dims <- map (primExpFromSubExp int32) . arrayDims <$> lookupType a-              let ixfun' = IxFun.slice ixfun $-                           fullSliceNum dims [DimFix $ LeafExp i int32]-              return (p { paramDec = MemArray bt shape u $ ArrayIn mem ixfun' }, a)-            Prim bt ->-              return (p { paramDec = MemPrim bt }, a)-            Mem space ->-              return (p { paramDec = MemMem space }, a)--class SizeSubst op where-  opSizeSubst :: PatternT dec -> op -> ChunkMap-  opIsConst :: op -> Bool-  opIsConst = const False--instance SizeSubst () where-  opSizeSubst _ _ = mempty--instance SizeSubst op => SizeSubst (MemOp op) where-  opSizeSubst pat (Inner op) = opSizeSubst pat op-  opSizeSubst _ _ = mempty--  opIsConst (Inner op) = opIsConst op-  opIsConst _ = False--sizeSubst :: SizeSubst (Op lore) => Stm lore -> ChunkMap-sizeSubst (Let pat _ (Op op)) = opSizeSubst pat op-sizeSubst _ = mempty--stmConsts :: SizeSubst (Op lore) => Stm lore -> S.Set VName-stmConsts (Let pat _ (Op op))-  | opIsConst op = S.fromList $ patternNames pat-stmConsts _ = mempty--mkLetNamesB' :: (Op (Lore m) ~ MemOp inner,-                 MonadBinder m, ExpDec (Lore m) ~ (),-                 Allocator (Lore m) (PatAllocM (Lore m))) =>-                ExpDec (Lore m) -> [VName] -> Exp (Lore m) -> m (Stm (Lore m))-mkLetNamesB' dec names e = do-  scope <- askScope-  pat <- bindPatternWithAllocations scope names e-  return $ Let pat (defAux dec) e--mkLetNamesB'' :: (Op (Lore m) ~ MemOp inner, ExpDec lore ~ (),-                   HasScope (Engine.Wise lore) m, Allocator lore (PatAllocM lore),-                   MonadBinder m, Engine.CanBeWise (Op lore)) =>-                 [VName] -> Exp (Engine.Wise lore)-              -> m (Stm (Engine.Wise lore))-mkLetNamesB'' names e = do-  scope <- Engine.removeScopeWisdom <$> askScope-  (pat, prestms) <- runPatAllocM (patternWithAllocations names $ Engine.removeExpWisdom e) scope-  mapM_ bindAllocStm prestms-  let pat' = Engine.addWisdomToPattern pat e-      dec = Engine.mkWiseExpDec pat' () e-  return $ Let pat' (defAux dec) e--simplifiable :: (Engine.SimplifiableLore lore,-                 ExpDec lore ~ (),-                 BodyDec lore ~ (),-                 Op lore ~ MemOp inner,-                 Allocator lore (PatAllocM lore)) =>-                (Engine.OpWithWisdom inner -> UT.UsageTable)-             -> (inner -> Engine.SimpleM lore (Engine.OpWithWisdom inner, Stms (Engine.Wise lore)))-             -> SimpleOps lore-simplifiable innerUsage simplifyInnerOp =-  SimpleOps mkExpDecS' mkBodyS' protectOp opUsage simplifyOp-  where mkExpDecS' _ pat e =-          return $ Engine.mkWiseExpDec pat () e--        mkBodyS' _ bnds res = return $ mkWiseBody () bnds res--        protectOp taken pat (Alloc size space) = Just $ do-          tbody <- resultBodyM [size]-          fbody <- resultBodyM [intConst Int64 0]-          size' <- letSubExp "hoisted_alloc_size" $-                   If taken tbody fbody $ IfDec [MemPrim int64] IfFallback-          letBind pat $ Op $ Alloc size' space-        protectOp _ _ _ = Nothing--        opUsage (Alloc (Var size) _) =-          UT.sizeUsage size-        opUsage (Alloc _ _) =-          mempty-        opUsage (Inner inner) =-          innerUsage inner--        simplifyOp (Alloc size space) =-          (,) <$> (Alloc <$> Engine.simplify size <*> pure space) <*> pure mempty-        simplifyOp (Inner k) = do (k', hoisted) <- simplifyInnerOp k-                                  return (Inner k', hoisted)--bindPatternWithAllocations :: (MonadBinder m,-                               ExpDec lore ~ (),-                               Op (Lore m) ~ MemOp inner,-                               Allocator lore (PatAllocM lore)) =>-                              Scope lore -> [VName] -> Exp lore-                           -> m (Pattern lore)-bindPatternWithAllocations types names e = do-  (pat,prebnds) <- runPatAllocM (patternWithAllocations names e) types-  mapM_ bindAllocStm prebnds-  return pat--data ExpHint = NoHint-             | Hint IxFun Space+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}++-- | A generic transformation for adding memory allocations to a+-- Futhark program.  Specialised by specific representations in+-- submodules.+module Futhark.Pass.ExplicitAllocations+  ( explicitAllocationsGeneric,+    explicitAllocationsInStmsGeneric,+    ExpHint (..),+    defaultExpHints,+    Allocable,+    Allocator (..),+    AllocM,+    AllocEnv (..),+    SizeSubst (..),+    allocInStms,+    allocForArray,+    simplifiable,+    arraySizeInBytesExp,+    mkLetNamesB',+    mkLetNamesB'',++    -- * Module re-exports++    --+    -- These are highly likely to be needed by any downstream+    -- users.+    module Control.Monad.Reader,+    module Futhark.MonadFreshNames,+    module Futhark.Pass,+    module Futhark.Tools,+  )+where++import Control.Monad.RWS.Strict+import Control.Monad.Reader+import Control.Monad.State+import Control.Monad.Writer+import Data.List (foldl', partition, sort, zip4)+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Data.Set as S+import qualified Futhark.Analysis.UsageTable as UT+import Futhark.IR.Mem+import qualified Futhark.IR.Mem.IxFun as IxFun+import Futhark.MonadFreshNames+import Futhark.Optimise.Simplify.Engine (SimpleOps (..))+import qualified Futhark.Optimise.Simplify.Engine as Engine+import Futhark.Optimise.Simplify.Lore (mkWiseBody)+import Futhark.Pass+import Futhark.Tools+import Futhark.Util (splitFromEnd, takeLast)++data AllocStm+  = SizeComputation VName (PrimExp VName)+  | Allocation VName SubExp Space+  | ArrayCopy VName VName+  deriving (Eq, Ord, Show)++bindAllocStm ::+  (MonadBinder m, Op (Lore m) ~ MemOp inner) =>+  AllocStm ->+  m ()+bindAllocStm (SizeComputation name pe) =+  letBindNames [name] =<< toExp (coerceIntPrimExp Int64 pe)+bindAllocStm (Allocation name size space) =+  letBindNames [name] $ Op $ Alloc size space+bindAllocStm (ArrayCopy name src) =+  letBindNames [name] $ BasicOp $ Copy src++class+  (MonadFreshNames m, HasScope lore m, Mem lore) =>+  Allocator lore m+  where+  addAllocStm :: AllocStm -> m ()+  askDefaultSpace :: m Space++  default addAllocStm ::+    ( Allocable fromlore lore,+      m ~ AllocM fromlore lore+    ) =>+    AllocStm ->+    m ()+  addAllocStm (SizeComputation name se) =+    letBindNames [name] =<< toExp (coerceIntPrimExp Int64 se)+  addAllocStm (Allocation name size space) =+    letBindNames [name] $ Op $ allocOp size space+  addAllocStm (ArrayCopy name src) =+    letBindNames [name] $ BasicOp $ Copy src++  -- | The subexpression giving the number of elements we should+  -- allocate space for.  See 'ChunkMap' comment.+  dimAllocationSize :: SubExp -> m SubExp+  default dimAllocationSize ::+    m ~ AllocM fromlore lore =>+    SubExp ->+    m SubExp+  dimAllocationSize (Var v) =+    -- It is important to recurse here, as the substitution may itself+    -- be a chunk size.+    maybe (return $ Var v) dimAllocationSize =<< asks (M.lookup v . chunkMap)+  dimAllocationSize size =+    return size++  -- | Get those names that are known to be constants at run-time.+  askConsts :: m (S.Set VName)++  expHints :: Exp lore -> m [ExpHint]+  expHints = defaultExpHints++allocateMemory ::+  Allocator lore m =>+  String ->+  SubExp ->+  Space ->+  m VName+allocateMemory desc size space = do+  v <- newVName desc+  addAllocStm $ Allocation v size space+  return v++computeSize ::+  Allocator lore m =>+  String ->+  PrimExp VName ->+  m SubExp+computeSize desc se = do+  v <- newVName desc+  addAllocStm $ SizeComputation v se+  return $ Var v++type Allocable fromlore tolore =+  ( PrettyLore fromlore,+    PrettyLore tolore,+    Mem tolore,+    FParamInfo fromlore ~ DeclType,+    LParamInfo fromlore ~ Type,+    BranchType fromlore ~ ExtType,+    RetType fromlore ~ DeclExtType,+    BodyDec fromlore ~ (),+    BodyDec tolore ~ (),+    ExpDec tolore ~ (),+    SizeSubst (Op tolore),+    BinderOps tolore+  )++-- | A mapping from chunk names to their maximum size.  XXX FIXME+-- HACK: This is part of a hack to add loop-invariant allocations to+-- reduce kernels, because memory expansion does not use range+-- analysis yet (it should).+type ChunkMap = M.Map VName SubExp++data AllocEnv fromlore tolore = AllocEnv+  { chunkMap :: ChunkMap,+    -- | Aggressively try to reuse memory in do-loops -+    -- should be True inside kernels, False outside.+    aggressiveReuse :: Bool,+    -- | When allocating memory, put it in this memory space.+    -- This is primarily used to ensure that group-wide+    -- statements store their results in local memory.+    allocSpace :: Space,+    -- | The set of names that are known to be constants at+    -- kernel compile time.+    envConsts :: S.Set VName,+    allocInOp :: Op fromlore -> AllocM fromlore tolore (Op tolore),+    envExpHints :: Exp tolore -> AllocM fromlore tolore [ExpHint]+  }++-- | Monad for adding allocations to an entire program.+newtype AllocM fromlore tolore a+  = AllocM (BinderT tolore (ReaderT (AllocEnv fromlore tolore) (State VNameSource)) a)+  deriving+    ( Applicative,+      Functor,+      Monad,+      MonadFreshNames,+      HasScope tolore,+      LocalScope tolore,+      MonadReader (AllocEnv fromlore tolore)+    )++instance+  (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+  MonadBinder (AllocM fromlore tolore)+  where+  type Lore (AllocM fromlore tolore) = tolore++  mkExpDecM _ _ = return ()++  mkLetNamesM names e = do+    pat <- patternWithAllocations names e+    return $ Let pat (defAux ()) e++  mkBodyM bnds res = return $ Body () bnds res++  addStms = AllocM . addStms+  collectStms (AllocM m) = AllocM $ collectStms m++instance+  (Allocable fromlore tolore) =>+  Allocator tolore (AllocM fromlore tolore)+  where+  expHints e = do+    f <- asks envExpHints+    f e+  askDefaultSpace = asks allocSpace++  askConsts = asks envConsts++runAllocM ::+  MonadFreshNames m =>+  (Op fromlore -> AllocM fromlore tolore (Op tolore)) ->+  (Exp tolore -> AllocM fromlore tolore [ExpHint]) ->+  AllocM fromlore tolore a ->+  m a+runAllocM handleOp hints (AllocM m) =+  fmap fst $ modifyNameSource $ runState $ runReaderT (runBinderT m mempty) env+  where+    env =+      AllocEnv+        { chunkMap = mempty,+          aggressiveReuse = False,+          allocSpace = DefaultSpace,+          envConsts = mempty,+          allocInOp = handleOp,+          envExpHints = hints+        }++-- | Monad for adding allocations to a single pattern.+newtype PatAllocM lore a+  = PatAllocM+      ( RWS+          (Scope lore)+          [AllocStm]+          VNameSource+          a+      )+  deriving+    ( Applicative,+      Functor,+      Monad,+      HasScope lore,+      MonadWriter [AllocStm],+      MonadFreshNames+    )++instance Mem lore => Allocator lore (PatAllocM lore) where+  addAllocStm = tell . pure+  dimAllocationSize = return+  askDefaultSpace = return DefaultSpace+  askConsts = pure mempty++runPatAllocM ::+  MonadFreshNames m =>+  PatAllocM lore a ->+  Scope lore ->+  m (a, [AllocStm])+runPatAllocM (PatAllocM m) mems =+  modifyNameSource $ frob . runRWS m mems+  where+    frob (a, s, w) = ((a, w), s)++elemSize :: Num a => Type -> a+elemSize = primByteSize . elemType++arraySizeInBytesExp :: Type -> PrimExp VName+arraySizeInBytesExp t =+  untyped $ foldl' (*) (elemSize t) $ map pe64 (arrayDims t)++arraySizeInBytesExpM :: Allocator lore m => Type -> m (PrimExp VName)+arraySizeInBytesExpM t = do+  dims <- mapM dimAllocationSize (arrayDims t)+  let dim_prod_i64 = product $ map pe64 dims+      elm_size_i64 = primByteSize $ elemType t+  return $ untyped $ dim_prod_i64 * elm_size_i64++arraySizeInBytes :: Allocator lore m => Type -> m SubExp+arraySizeInBytes = computeSize "bytes" <=< arraySizeInBytesExpM++-- | Allocate memory for a value of the given type.+allocForArray ::+  Allocator lore m =>+  Type ->+  Space ->+  m VName+allocForArray t space = do+  size <- arraySizeInBytes t+  allocateMemory "mem" size space++allocsForStm ::+  (Allocator lore m, ExpDec lore ~ ()) =>+  [Ident] ->+  [Ident] ->+  Exp lore ->+  m (Stm lore)+allocsForStm sizeidents validents e = do+  rts <- expReturns e+  hints <- expHints e+  (ctxElems, valElems) <- allocsForPattern sizeidents validents rts hints+  return $ Let (Pattern ctxElems valElems) (defAux ()) e++patternWithAllocations ::+  (Allocator lore m, ExpDec lore ~ ()) =>+  [VName] ->+  Exp lore ->+  m (Pattern lore)+patternWithAllocations names e = do+  (ts', sizes) <- instantiateShapes' =<< expExtType e+  let identForBindage name t =+        pure $ Ident name t+  vals <- sequence [identForBindage name t | (name, t) <- zip names ts']+  stmPattern <$> allocsForStm sizes vals e++allocsForPattern ::+  Allocator lore m =>+  [Ident] ->+  [Ident] ->+  [ExpReturns] ->+  [ExpHint] ->+  m+    ( [PatElem lore],+      [PatElem lore]+    )+allocsForPattern sizeidents validents rts hints = do+  let sizes' = [PatElem size $ MemPrim int64 | size <- map identName sizeidents]+  (vals, (exts, mems)) <-+    runWriterT $+      forM (zip3 validents rts hints) $ \(ident, rt, hint) -> do+        let shape = arrayShape $ identType ident+        case rt of+          MemPrim _ -> do+            summary <- lift $ summaryForBindage (identType ident) hint+            return $ PatElem (identName ident) summary+          MemMem space ->+            return $+              PatElem (identName ident) $+                MemMem space+          MemArray bt _ u (Just (ReturnsInBlock mem extixfun)) -> do+            (patels, ixfn) <- instantiateExtIxFun ident extixfun+            tell (patels, [])++            return $+              PatElem (identName ident) $+                MemArray bt shape u $+                  ArrayIn mem ixfn+          MemArray _ extshape _ Nothing+            | Just _ <- knownShape extshape -> do+              summary <- lift $ summaryForBindage (identType ident) hint+              return $ PatElem (identName ident) summary+          MemArray bt _ u (Just (ReturnsNewBlock space _ extixfn)) -> do+            -- treat existential index function first+            (patels, ixfn) <- instantiateExtIxFun ident extixfn+            tell (patels, [])++            memid <- lift $ mkMemIdent ident space+            tell ([], [PatElem (identName memid) $ MemMem space])+            return $+              PatElem (identName ident) $+                MemArray bt shape u $+                  ArrayIn (identName memid) ixfn+          _ -> error "Impossible case reached in allocsForPattern!"++  return+    ( sizes' <> exts <> mems,+      vals+    )+  where+    knownShape = mapM known . shapeDims+    known (Free v) = Just v+    known Ext {} = Nothing++    mkMemIdent :: (MonadFreshNames m) => Ident -> Space -> m Ident+    mkMemIdent ident space = do+      let memname = baseString (identName ident) <> "_mem"+      newIdent memname $ Mem space++    instantiateExtIxFun ::+      MonadFreshNames m =>+      Ident ->+      ExtIxFun ->+      m ([PatElemT (MemInfo d u ret)], IxFun)+    instantiateExtIxFun idd ext_ixfn = do+      let isAndPtps =+            S.toList $+              foldMap onlyExts $+                foldMap (leafExpTypes . untyped) ext_ixfn++      -- Find the existentials that reuse the sizeidents, and+      -- those that need new pattern elements.  Assumes that the+      -- Exts form a contiguous interval of integers.+      let (size_exts, new_exts) =+            span ((< length sizeidents) . fst) $ sort isAndPtps+      (new_substs, patels) <-+        fmap unzip $+          forM new_exts $ \(i, t) -> do+            v <- newVName $ baseString (identName idd) <> "_ixfn"+            return+              ( (Ext i, LeafExp (Free v) t),+                PatElem v $ MemPrim t+              )+      let size_substs =+            zipWith+              ( \(i, t) ident ->+                  (Ext i, LeafExp (Free (identName ident)) t)+              )+              size_exts+              sizeidents+          substs = M.fromList $ new_substs <> size_substs+      ixfn <- instantiateIxFun $ IxFun.substituteInIxFun (fmap isInt64 substs) ext_ixfn++      return (patels, ixfn)++onlyExts :: (Ext a, PrimType) -> S.Set (Int, PrimType)+onlyExts (Free _, _) = S.empty+onlyExts (Ext i, t) = S.singleton (i, t)++instantiateIxFun :: Monad m => ExtIxFun -> m IxFun+instantiateIxFun = traverse $ traverse inst+  where+    inst Ext {} = error "instantiateIxFun: not yet"+    inst (Free x) = return x++summaryForBindage ::+  Allocator lore m =>+  Type ->+  ExpHint ->+  m (MemBound NoUniqueness)+summaryForBindage (Prim bt) _ =+  return $ MemPrim bt+summaryForBindage (Mem space) _ =+  return $ MemMem space+summaryForBindage t@(Array bt shape u) NoHint = do+  m <- allocForArray t =<< askDefaultSpace+  return $ directIxFun bt shape u m t+summaryForBindage t (Hint ixfun space) = do+  let bt = elemType t+  bytes <-+    computeSize "bytes" $+      untyped $+        product+          [ product $ IxFun.base ixfun,+            primByteSize (elemType t)+          ]+  m <- allocateMemory "mem" bytes space+  return $ MemArray bt (arrayShape t) NoUniqueness $ ArrayIn m ixfun++lookupMemSpace :: (HasScope lore m, Monad m) => VName -> m Space+lookupMemSpace v = do+  t <- lookupType v+  case t of+    Mem space -> return space+    _ -> error $ "lookupMemSpace: " ++ pretty v ++ " is not a memory block."++directIxFun :: PrimType -> Shape -> u -> VName -> Type -> MemBound u+directIxFun bt shape u mem t =+  let ixf = IxFun.iota $ map pe64 $ arrayDims t+   in MemArray bt shape u $ ArrayIn mem ixf++allocInFParams ::+  (Allocable fromlore tolore) =>+  [(FParam fromlore, Space)] ->+  ([FParam tolore] -> AllocM fromlore tolore a) ->+  AllocM fromlore tolore a+allocInFParams params m = do+  (valparams, (ctxparams, memparams)) <-+    runWriterT $ mapM (uncurry allocInFParam) params+  let params' = ctxparams <> memparams <> valparams+      summary = scopeOfFParams params'+  localScope summary $ m params'++allocInFParam ::+  (Allocable fromlore tolore) =>+  FParam fromlore ->+  Space ->+  WriterT+    ([FParam tolore], [FParam tolore])+    (AllocM fromlore tolore)+    (FParam tolore)+allocInFParam param pspace =+  case paramDeclType param of+    Array bt shape u -> do+      let memname = baseString (paramName param) <> "_mem"+          ixfun = IxFun.iota $ map pe64 $ shapeDims shape+      mem <- lift $ newVName memname+      tell ([], [Param mem $ MemMem pspace])+      return param {paramDec = MemArray bt shape u $ ArrayIn mem ixfun}+    Prim bt ->+      return param {paramDec = MemPrim bt}+    Mem space ->+      return param {paramDec = MemMem space}++allocInMergeParams ::+  ( Allocable fromlore tolore,+    Allocator tolore (AllocM fromlore tolore)+  ) =>+  [(FParam fromlore, SubExp)] ->+  ( [FParam tolore] ->+    [FParam tolore] ->+    ([SubExp] -> AllocM fromlore tolore ([SubExp], [SubExp])) ->+    AllocM fromlore tolore a+  ) ->+  AllocM fromlore tolore a+allocInMergeParams merge m = do+  ((valparams, handle_loop_subexps), (ctx_params, mem_params)) <-+    runWriterT $ unzip <$> mapM allocInMergeParam merge+  let mergeparams' = ctx_params <> mem_params <> valparams+      summary = scopeOfFParams mergeparams'++      mk_loop_res ses = do+        (valargs, (ctxargs, memargs)) <-+          runWriterT $ zipWithM ($) handle_loop_subexps ses+        return (ctxargs <> memargs, valargs)++  localScope summary $ m (ctx_params <> mem_params) valparams mk_loop_res+  where+    allocInMergeParam ::+      (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+      (Param DeclType, SubExp) ->+      WriterT+        ([FParam tolore], [FParam tolore])+        (AllocM fromlore tolore)+        (FParam tolore, SubExp -> WriterT ([SubExp], [SubExp]) (AllocM fromlore tolore) SubExp)+    allocInMergeParam (mergeparam, Var v)+      | Array bt shape u <- paramDeclType mergeparam = do+        (mem', _) <- lift $ lookupArraySummary v+        mem_space <- lift $ lookupMemSpace mem'++        (_, ext_ixfun, substs, _) <- lift $ existentializeArray mem_space v++        (ctx_params, param_ixfun_substs) <-+          unzip+            <$> mapM+              ( \_ -> do+                  vname <- lift $ newVName "ctx_param_ext"+                  return+                    ( Param vname $ MemPrim int64,+                      fmap Free $ pe64 $ Var vname+                    )+              )+              substs++        tell (ctx_params, [])++        param_ixfun <-+          instantiateIxFun $+            IxFun.substituteInIxFun+              (M.fromList $ zip (fmap Ext [0 ..]) param_ixfun_substs)+              ext_ixfun++        mem_name <- newVName "mem_param"+        tell ([], [Param mem_name $ MemMem mem_space])++        return+          ( mergeparam {paramDec = MemArray bt shape u $ ArrayIn mem_name param_ixfun},+            ensureArrayIn mem_space+          )+    allocInMergeParam (mergeparam, _) = doDefault mergeparam =<< lift askDefaultSpace++    doDefault mergeparam space = do+      mergeparam' <- allocInFParam mergeparam space+      return (mergeparam', linearFuncallArg (paramType mergeparam) space)++-- Returns the existentialized index function, the list of substituted values and the memory location.+existentializeArray ::+  (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+  Space ->+  VName ->+  AllocM fromlore tolore (SubExp, ExtIxFun, [TPrimExp Int64 VName], VName)+existentializeArray space v = do+  (mem', ixfun) <- lookupArraySummary v+  sp <- lookupMemSpace mem'++  let (ext_ixfun', substs') = runState (IxFun.existentialize ixfun) []++  case (ext_ixfun', sp == space) of+    (Just x, True) -> return (Var v, x, substs', mem')+    _ -> do+      (mem, subexp) <- allocLinearArray space (baseString v) v+      ixfun' <- fromJust <$> subExpIxFun subexp+      let (ext_ixfun, substs) = runState (IxFun.existentialize ixfun') []+      return (subexp, fromJust ext_ixfun, substs, mem)++ensureArrayIn ::+  ( Allocable fromlore tolore,+    Allocator tolore (AllocM fromlore tolore)+  ) =>+  Space ->+  SubExp ->+  WriterT ([SubExp], [SubExp]) (AllocM fromlore tolore) SubExp+ensureArrayIn _ (Constant v) =+  error $ "ensureArrayIn: " ++ pretty v ++ " cannot be an array."+ensureArrayIn space (Var v) = do+  (sub_exp, _, substs, mem) <- lift $ existentializeArray space v+  (ctx_vals, _) <-+    unzip+      <$> mapM+        ( \s -> do+            vname <- lift $ letExp "ctx_val" =<< toExp s+            return (Var vname, fmap Free $ primExpFromSubExp int64 $ Var vname)+        )+        substs++  tell (ctx_vals, [Var mem])++  return sub_exp++ensureDirectArray ::+  ( Allocable fromlore tolore,+    Allocator tolore (AllocM fromlore tolore)+  ) =>+  Maybe Space ->+  VName ->+  AllocM fromlore tolore (VName, SubExp)+ensureDirectArray space_ok v = do+  (mem, ixfun) <- lookupArraySummary v+  mem_space <- lookupMemSpace mem+  default_space <- askDefaultSpace+  if IxFun.isDirect ixfun && maybe True (== mem_space) space_ok+    then return (mem, Var v)+    else needCopy (fromMaybe default_space space_ok)+  where+    needCopy space =+      -- We need to do a new allocation, copy 'v', and make a new+      -- binding for the size of the memory block.+      allocLinearArray space (baseString v) v++allocLinearArray ::+  (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+  Space ->+  String ->+  VName ->+  AllocM fromlore tolore (VName, SubExp)+allocLinearArray space s v = do+  t <- lookupType v+  mem <- allocForArray t space+  v' <- newIdent (s ++ "_linear") t+  let ixfun = directIxFun (elemType t) (arrayShape t) NoUniqueness mem t+  let pat = Pattern [] [PatElem (identName v') ixfun]+  addStm $ Let pat (defAux ()) $ BasicOp $ Copy v+  return (mem, Var $ identName v')++funcallArgs ::+  ( Allocable fromlore tolore,+    Allocator tolore (AllocM fromlore tolore)+  ) =>+  [(SubExp, Diet)] ->+  AllocM fromlore tolore [(SubExp, Diet)]+funcallArgs args = do+  (valargs, (ctx_args, mem_and_size_args)) <- runWriterT $+    forM args $ \(arg, d) -> do+      t <- lift $ subExpType arg+      space <- lift askDefaultSpace+      arg' <- linearFuncallArg t space arg+      return (arg', d)+  return $ map (,Observe) (ctx_args <> mem_and_size_args) <> valargs++linearFuncallArg ::+  ( Allocable fromlore tolore,+    Allocator tolore (AllocM fromlore tolore)+  ) =>+  Type ->+  Space ->+  SubExp ->+  WriterT ([SubExp], [SubExp]) (AllocM fromlore tolore) SubExp+linearFuncallArg Array {} space (Var v) = do+  (mem, arg') <- lift $ ensureDirectArray (Just space) v+  tell ([], [Var mem])+  return arg'+linearFuncallArg _ _ arg =+  return arg++explicitAllocationsGeneric ::+  ( Allocable fromlore tolore,+    Allocator tolore (AllocM fromlore tolore)+  ) =>+  (Op fromlore -> AllocM fromlore tolore (Op tolore)) ->+  (Exp tolore -> AllocM fromlore tolore [ExpHint]) ->+  Pass fromlore tolore+explicitAllocationsGeneric handleOp hints =+  Pass "explicit allocations" "Transform program to explicit memory representation" $+    intraproceduralTransformationWithConsts onStms allocInFun+  where+    onStms stms = runAllocM handleOp hints $ allocInStms stms pure++    allocInFun consts (FunDef entry attrs fname rettype params fbody) =+      runAllocM handleOp hints $+        inScopeOf consts $+          allocInFParams (zip params $ repeat DefaultSpace) $ \params' -> do+            fbody' <-+              insertStmsM $+                allocInFunBody+                  (map (const $ Just DefaultSpace) rettype)+                  fbody+            return $ FunDef entry attrs fname (memoryInDeclExtType rettype) params' fbody'++explicitAllocationsInStmsGeneric ::+  ( MonadFreshNames m,+    HasScope tolore m,+    Allocable fromlore tolore+  ) =>+  (Op fromlore -> AllocM fromlore tolore (Op tolore)) ->+  (Exp tolore -> AllocM fromlore tolore [ExpHint]) ->+  Stms fromlore ->+  m (Stms tolore)+explicitAllocationsInStmsGeneric handleOp hints stms = do+  scope <- askScope+  runAllocM handleOp hints $ localScope scope $ allocInStms stms return++memoryInDeclExtType :: [DeclExtType] -> [FunReturns]+memoryInDeclExtType ts = evalState (mapM addMem ts) $ startOfFreeIDRange ts+  where+    addMem (Prim t) = return $ MemPrim t+    addMem Mem {} = error "memoryInDeclExtType: too much memory"+    addMem (Array bt shape u) = do+      i <- get <* modify (+ 1)+      return $+        MemArray bt shape u $+          ReturnsNewBlock DefaultSpace i $+            IxFun.iota $ map convert $ shapeDims shape++    convert (Ext i) = le64 $ Ext i+    convert (Free v) = Free <$> pe64 v++startOfFreeIDRange :: [TypeBase ExtShape u] -> Int+startOfFreeIDRange = S.size . shapeContext++bodyReturnMemCtx ::+  (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+  SubExp ->+  AllocM fromlore tolore [SubExp]+bodyReturnMemCtx Constant {} =+  return []+bodyReturnMemCtx (Var v) = do+  info <- lookupMemInfo v+  case info of+    MemPrim {} -> return []+    MemMem {} -> return [] -- should not happen+    MemArray _ _ _ (ArrayIn mem _) -> return [Var mem]++allocInFunBody ::+  (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+  [Maybe Space] ->+  Body fromlore ->+  AllocM fromlore tolore (Body tolore)+allocInFunBody space_oks (Body _ bnds res) =+  allocInStms bnds $ \bnds' -> do+    (res'', allocs) <- collectStms $ do+      res' <- zipWithM ensureDirect space_oks' res+      let (ctx_res, val_res) = splitFromEnd num_vals res'+      mem_ctx_res <- concat <$> mapM bodyReturnMemCtx val_res+      return $ ctx_res <> mem_ctx_res <> val_res+    return $ Body () (bnds' <> allocs) res''+  where+    num_vals = length space_oks+    space_oks' = replicate (length res - num_vals) Nothing ++ space_oks++ensureDirect ::+  (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+  Maybe Space ->+  SubExp ->+  AllocM fromlore tolore SubExp+ensureDirect _ se@Constant {} = return se+ensureDirect space_ok (Var v) = do+  bt <- primType <$> lookupType v+  if bt+    then return $ Var v+    else do+      (_, v') <- ensureDirectArray space_ok v+      return v'++allocInStms ::+  (Allocable fromlore tolore) =>+  Stms fromlore ->+  (Stms tolore -> AllocM fromlore tolore a) ->+  AllocM fromlore tolore a+allocInStms origstms m = allocInStms' (stmsToList origstms) mempty+  where+    allocInStms' [] stms' =+      m stms'+    allocInStms' (x : xs) stms' = do+      allocstms <- allocInStm' x+      localScope (scopeOf allocstms) $ do+        let stms_substs = foldMap sizeSubst allocstms+            stms_consts = foldMap stmConsts allocstms+            f env =+              env+                { chunkMap = stms_substs <> chunkMap env,+                  envConsts = stms_consts <> envConsts env+                }+        local f $ allocInStms' xs (stms' <> allocstms)+    allocInStm' stm =+      collectStms_ $ auxing (stmAux stm) $ allocInStm stm++allocInStm ::+  (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+  Stm fromlore ->+  AllocM fromlore tolore ()+allocInStm (Let (Pattern sizeElems valElems) _ e) = do+  e' <- allocInExp e+  let sizeidents = map patElemIdent sizeElems+      validents = map patElemIdent valElems+  bnd <- allocsForStm sizeidents validents e'+  addStm bnd++allocInExp ::+  (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+  Exp fromlore ->+  AllocM fromlore tolore (Exp tolore)+allocInExp (DoLoop ctx val form (Body () bodybnds bodyres)) =+  allocInMergeParams ctx $ \_ ctxparams' _ ->+    allocInMergeParams val $+      \new_ctx_params valparams' mk_loop_val -> do+        form' <- allocInLoopForm form+        localScope (scopeOf form') $ do+          (valinit_ctx, valinit') <- mk_loop_val valinit+          body' <- insertStmsM $+            allocInStms bodybnds $ \bodybnds' -> do+              ((val_ses, valres'), val_retbnds) <- collectStms $ mk_loop_val valres+              return $ Body () (bodybnds' <> val_retbnds) (ctxres ++ val_ses ++ valres')+          return $+            DoLoop+              (zip (ctxparams' ++ new_ctx_params) (ctxinit ++ valinit_ctx))+              (zip valparams' valinit')+              form'+              body'+  where+    (_ctxparams, ctxinit) = unzip ctx+    (_valparams, valinit) = unzip val+    (ctxres, valres) = splitAt (length ctx) bodyres+allocInExp (Apply fname args rettype loc) = do+  args' <- funcallArgs args+  return $ Apply fname args' (memoryInDeclExtType rettype) loc+allocInExp (If cond tbranch0 fbranch0 (IfDec rets ifsort)) = do+  let num_rets = length rets+  -- switch to the explicit-mem rep, but do nothing about results+  (tbranch, tm_ixfs) <- allocInIfBody num_rets tbranch0+  (fbranch, fm_ixfs) <- allocInIfBody num_rets fbranch0+  tspaces <- mkSpaceOks num_rets tbranch+  fspaces <- mkSpaceOks num_rets fbranch+  -- try to generalize (antiunify) the index functions of the then and else bodies+  let sp_substs = zipWith generalize (zip tspaces tm_ixfs) (zip fspaces fm_ixfs)+      (spaces, subs) = unzip sp_substs+      tsubs = map (selectSub fst) subs+      fsubs = map (selectSub snd) subs+  (tbranch', trets) <- addResCtxInIfBody rets tbranch spaces tsubs+  (fbranch', frets) <- addResCtxInIfBody rets fbranch spaces fsubs+  if frets /= trets+    then error "In allocInExp, IF case: antiunification of then/else produce different ExtInFn!"+    else do+      -- above is a sanity check; implementation continues on else branch+      let res_then = bodyResult tbranch'+          res_else = bodyResult fbranch'+          size_ext = length res_then - length trets+          (ind_ses0, r_then_else) =+            partition (\(r_then, r_else, _) -> r_then == r_else) $+              zip3 res_then res_else [0 .. size_ext - 1]+          (r_then_ext, r_else_ext, _) = unzip3 r_then_else+          ind_ses =+            zipWith+              (\(se, _, i) k -> (i - k, se))+              ind_ses0+              [0 .. length ind_ses0 - 1]+          rets'' = foldl (\acc (i, se) -> fixExt i se acc) trets ind_ses+          tbranch'' = tbranch' {bodyResult = r_then_ext ++ drop size_ext res_then}+          fbranch'' = fbranch' {bodyResult = r_else_ext ++ drop size_ext res_else}+          res_if_expr = If cond tbranch'' fbranch'' $ IfDec rets'' ifsort+      return res_if_expr+  where+    generalize ::+      (Maybe Space, Maybe IxFun) ->+      (Maybe Space, Maybe IxFun) ->+      (Maybe Space, Maybe (ExtIxFun, [(TPrimExp Int64 VName, TPrimExp Int64 VName)]))+    generalize (Just sp1, Just ixf1) (Just sp2, Just ixf2) =+      if sp1 /= sp2+        then (Just sp1, Nothing)+        else case IxFun.leastGeneralGeneralization (fmap untyped ixf1) (fmap untyped ixf2) of+          Just (ixf, m) ->+            ( Just sp1,+              Just+                ( fmap TPrimExp ixf,+                  zip (map (TPrimExp . fst) m) (map (TPrimExp . snd) m)+                )+            )+          Nothing -> (Just sp1, Nothing)+    generalize (mbsp1, _) _ = (mbsp1, Nothing)++    selectSub ::+      ((a, a) -> a) ->+      Maybe (ExtIxFun, [(a, a)]) ->+      Maybe (ExtIxFun, [a])+    selectSub f (Just (ixfn, m)) = Just (ixfn, map f m)+    selectSub _ Nothing = Nothing+    allocInIfBody ::+      (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+      Int ->+      Body fromlore ->+      AllocM fromlore tolore (Body tolore, [Maybe IxFun])+    allocInIfBody num_vals (Body _ bnds res) =+      allocInStms bnds $ \bnds' -> do+        let (_, val_res) = splitFromEnd num_vals res+        mem_ixfs <- mapM subExpIxFun val_res+        return (Body () bnds' res, mem_ixfs)+allocInExp e = mapExpM alloc e+  where+    alloc =+      identityMapper+        { mapOnBody = error "Unhandled Body in ExplicitAllocations",+          mapOnRetType = error "Unhandled RetType in ExplicitAllocations",+          mapOnBranchType = error "Unhandled BranchType in ExplicitAllocations",+          mapOnFParam = error "Unhandled FParam in ExplicitAllocations",+          mapOnLParam = error "Unhandled LParam in ExplicitAllocations",+          mapOnOp = \op -> do+            handle <- asks allocInOp+            handle op+        }++subExpIxFun ::+  (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+  SubExp ->+  AllocM fromlore tolore (Maybe IxFun)+subExpIxFun Constant {} = return Nothing+subExpIxFun (Var v) = do+  info <- lookupMemInfo v+  case info of+    MemArray _ptp _shp _u (ArrayIn _ ixf) -> return $ Just ixf+    _ -> return Nothing++addResCtxInIfBody ::+  (Allocable fromlore tolore, Allocator tolore (AllocM fromlore tolore)) =>+  [ExtType] ->+  Body tolore ->+  [Maybe Space] ->+  [Maybe (ExtIxFun, [TPrimExp Int64 VName])] ->+  AllocM fromlore tolore (Body tolore, [BodyReturns])+addResCtxInIfBody ifrets (Body _ bnds res) spaces substs = do+  let num_vals = length ifrets+      (ctx_res, val_res) = splitFromEnd num_vals res+  ((res', bodyrets'), all_body_stms) <- collectStms $ do+    mapM_ addStm bnds+    (val_res', ext_ses_res, mem_ctx_res, bodyrets, total_existentials) <-+      foldM helper ([], [], [], [], length ctx_res) (zip4 ifrets val_res substs spaces)+    return+      ( ctx_res <> ext_ses_res <> mem_ctx_res <> val_res',+        -- We need to adjust the ReturnsNewBlock existentials, because they+        -- should always be numbered _after_ all other existentials in the+        -- return values.+        reverse $ fst $ foldl adjustNewBlockExistential ([], total_existentials) bodyrets+      )+  body' <- mkBodyM all_body_stms res'+  return (body', bodyrets')+  where+    helper (res_acc, ext_acc, ctx_acc, br_acc, k) (ifr, r, mbixfsub, sp) =+      case mbixfsub of+        Nothing -> do+          -- does NOT generalize/antiunify; ensure direct+          r' <- ensureDirect sp r+          mem_ctx_r <- bodyReturnMemCtx r'+          let body_ret = inspect ifr sp+          return+            ( res_acc ++ [r'],+              ext_acc,+              ctx_acc ++ mem_ctx_r,+              br_acc ++ [body_ret],+              k+            )+        Just (ixfn, m) -> do+          -- generalizes+          let i = length m+          ext_ses <- mapM (toSubExp "ixfn_exist") m+          mem_ctx_r <- bodyReturnMemCtx r+          let sp' = fromMaybe DefaultSpace sp+              ixfn' = fmap (adjustExtPE k) ixfn+              exttp = case ifr of+                Array pt shp' u ->+                  MemArray pt shp' u $+                    ReturnsNewBlock sp' 0 ixfn'+                _ -> error "Impossible case reached in addResCtxInIfBody"+          return+            ( res_acc ++ [r],+              ext_acc ++ ext_ses,+              ctx_acc ++ mem_ctx_r,+              br_acc ++ [exttp],+              k + i+            )++    adjustNewBlockExistential :: ([BodyReturns], Int) -> BodyReturns -> ([BodyReturns], Int)+    adjustNewBlockExistential (acc, k) (MemArray pt shp u (ReturnsNewBlock space _ ixfun)) =+      (MemArray pt shp u (ReturnsNewBlock space k ixfun) : acc, k + 1)+    adjustNewBlockExistential (acc, k) x = (x : acc, k)++    inspect (Array pt shape u) space =+      let space' = fromMaybe DefaultSpace space+          bodyret =+            MemArray pt shape u $+              ReturnsNewBlock space' 0 $+                IxFun.iota $ map convert $ shapeDims shape+       in bodyret+    inspect (Prim pt) _ = MemPrim pt+    inspect (Mem space) _ = MemMem space++    convert (Ext i) = le64 (Ext i)+    convert (Free v) = Free <$> pe64 v++    adjustExtV :: Int -> Ext VName -> Ext VName+    adjustExtV _ (Free v) = Free v+    adjustExtV k (Ext i) = Ext (k + i)++    adjustExtPE :: Int -> TPrimExp t (Ext VName) -> TPrimExp t (Ext VName)+    adjustExtPE k = fmap (adjustExtV k)++mkSpaceOks ::+  (Mem tolore, LocalScope tolore m) =>+  Int ->+  Body tolore ->+  m [Maybe Space]+mkSpaceOks num_vals (Body _ stms res) =+  inScopeOf stms $+    mapM mkSpaceOK $ takeLast num_vals res+  where+    mkSpaceOK (Var v) = do+      v_info <- lookupMemInfo v+      case v_info of+        MemArray _ _ _ (ArrayIn mem _) -> do+          mem_info <- lookupMemInfo mem+          case mem_info of+            MemMem space -> return $ Just space+            _ -> return Nothing+        _ -> return Nothing+    mkSpaceOK _ = return Nothing++allocInLoopForm ::+  ( Allocable fromlore tolore,+    Allocator tolore (AllocM fromlore tolore)+  ) =>+  LoopForm fromlore ->+  AllocM fromlore tolore (LoopForm tolore)+allocInLoopForm (WhileLoop v) = return $ WhileLoop v+allocInLoopForm (ForLoop i it n loopvars) =+  ForLoop i it n <$> mapM allocInLoopVar loopvars+  where+    allocInLoopVar (p, a) = do+      (mem, ixfun) <- lookupArraySummary a+      case paramType p of+        Array bt shape u -> do+          dims <- map pe64 . arrayDims <$> lookupType a+          let ixfun' =+                IxFun.slice ixfun $+                  fullSliceNum dims [DimFix $ le64 i]+          return (p {paramDec = MemArray bt shape u $ ArrayIn mem ixfun'}, a)+        Prim bt ->+          return (p {paramDec = MemPrim bt}, a)+        Mem space ->+          return (p {paramDec = MemMem space}, a)++class SizeSubst op where+  opSizeSubst :: PatternT dec -> op -> ChunkMap+  opIsConst :: op -> Bool+  opIsConst = const False++instance SizeSubst () where+  opSizeSubst _ _ = mempty++instance SizeSubst op => SizeSubst (MemOp op) where+  opSizeSubst pat (Inner op) = opSizeSubst pat op+  opSizeSubst _ _ = mempty++  opIsConst (Inner op) = opIsConst op+  opIsConst _ = False++sizeSubst :: SizeSubst (Op lore) => Stm lore -> ChunkMap+sizeSubst (Let pat _ (Op op)) = opSizeSubst pat op+sizeSubst _ = mempty++stmConsts :: SizeSubst (Op lore) => Stm lore -> S.Set VName+stmConsts (Let pat _ (Op op))+  | opIsConst op = S.fromList $ patternNames pat+stmConsts _ = mempty++mkLetNamesB' ::+  ( Op (Lore m) ~ MemOp inner,+    MonadBinder m,+    ExpDec (Lore m) ~ (),+    Allocator (Lore m) (PatAllocM (Lore m))+  ) =>+  ExpDec (Lore m) ->+  [VName] ->+  Exp (Lore m) ->+  m (Stm (Lore m))+mkLetNamesB' dec names e = do+  scope <- askScope+  pat <- bindPatternWithAllocations scope names e+  return $ Let pat (defAux dec) e++mkLetNamesB'' ::+  ( Op (Lore m) ~ MemOp inner,+    ExpDec lore ~ (),+    HasScope (Engine.Wise lore) m,+    Allocator lore (PatAllocM lore),+    MonadBinder m,+    Engine.CanBeWise (Op lore)+  ) =>+  [VName] ->+  Exp (Engine.Wise lore) ->+  m (Stm (Engine.Wise lore))+mkLetNamesB'' names e = do+  scope <- Engine.removeScopeWisdom <$> askScope+  (pat, prestms) <- runPatAllocM (patternWithAllocations names $ Engine.removeExpWisdom e) scope+  mapM_ bindAllocStm prestms+  let pat' = Engine.addWisdomToPattern pat e+      dec = Engine.mkWiseExpDec pat' () e+  return $ Let pat' (defAux dec) e++simplifiable ::+  ( Engine.SimplifiableLore lore,+    ExpDec lore ~ (),+    BodyDec lore ~ (),+    Op lore ~ MemOp inner,+    Allocator lore (PatAllocM lore)+  ) =>+  (Engine.OpWithWisdom inner -> UT.UsageTable) ->+  (inner -> Engine.SimpleM lore (Engine.OpWithWisdom inner, Stms (Engine.Wise lore))) ->+  SimpleOps lore+simplifiable innerUsage simplifyInnerOp =+  SimpleOps mkExpDecS' mkBodyS' protectOp opUsage simplifyOp+  where+    mkExpDecS' _ pat e =+      return $ Engine.mkWiseExpDec pat () e++    mkBodyS' _ bnds res = return $ mkWiseBody () bnds res++    protectOp taken pat (Alloc size space) = Just $ do+      tbody <- resultBodyM [size]+      fbody <- resultBodyM [intConst Int64 0]+      size' <-+        letSubExp "hoisted_alloc_size" $+          If taken tbody fbody $ IfDec [MemPrim int64] IfFallback+      letBind pat $ Op $ Alloc size' space+    protectOp _ _ _ = Nothing++    opUsage (Alloc (Var size) _) =+      UT.sizeUsage size+    opUsage (Alloc _ _) =+      mempty+    opUsage (Inner inner) =+      innerUsage inner++    simplifyOp (Alloc size space) =+      (,) <$> (Alloc <$> Engine.simplify size <*> pure space) <*> pure mempty+    simplifyOp (Inner k) = do+      (k', hoisted) <- simplifyInnerOp k+      return (Inner k', hoisted)++bindPatternWithAllocations ::+  ( MonadBinder m,+    ExpDec lore ~ (),+    Op (Lore m) ~ MemOp inner,+    Allocator lore (PatAllocM lore)+  ) =>+  Scope lore ->+  [VName] ->+  Exp lore ->+  m (Pattern lore)+bindPatternWithAllocations types names e = do+  (pat, prebnds) <- runPatAllocM (patternWithAllocations names e) types+  mapM_ bindAllocStm prebnds+  return pat++data ExpHint+  = NoHint+  | Hint IxFun Space  defaultExpHints :: (Monad m, ASTLore lore) => Exp lore -> m [ExpHint] defaultExpHints e = return $ replicate (expExtTypeSize e) NoHint
src/Futhark/Pass/ExplicitAllocations/Kernels.hs view
@@ -2,19 +2,19 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -fno-warn-orphans #-}+ -- | Facilities for converting a 'Kernels' program to 'KernelsMem'. module Futhark.Pass.ExplicitAllocations.Kernels-       ( explicitAllocations-       , explicitAllocationsInStms-       )+  ( explicitAllocations,+    explicitAllocationsInStms,+  ) where  import qualified Data.Map as M import qualified Data.Set as S--import qualified Futhark.IR.Mem.IxFun as IxFun-import Futhark.IR.KernelsMem import Futhark.IR.Kernels+import Futhark.IR.KernelsMem+import qualified Futhark.IR.Mem.IxFun as IxFun import Futhark.Pass.ExplicitAllocations import Futhark.Pass.ExplicitAllocations.SegOp @@ -23,43 +23,56 @@     M.singleton (patElemName size) elems_per_thread   opSizeSubst _ _ = mempty -  opIsConst (SizeOp GetSize{}) = True-  opIsConst (SizeOp GetSizeMax{}) = True+  opIsConst (SizeOp GetSize {}) = True+  opIsConst (SizeOp GetSizeMax {}) = True   opIsConst _ = False  instance SizeSubst (SegOp lvl lore) where   opSizeSubst _ _ = mempty  allocAtLevel :: SegLevel -> AllocM fromlore tlore a -> AllocM fromlore tlore a-allocAtLevel lvl = local $ \env -> env { allocSpace = space-                                       , aggressiveReuse = True-                                       }-  where space = case lvl of SegThread{} -> DefaultSpace-                            SegGroup{} -> Space "local"+allocAtLevel lvl = local $ \env ->+  env+    { allocSpace = space,+      aggressiveReuse = True+    }+  where+    space = case lvl of+      SegThread {} -> DefaultSpace+      SegGroup {} -> Space "local" -handleSegOp :: SegOp SegLevel Kernels-            -> AllocM Kernels KernelsMem (SegOp SegLevel KernelsMem)+handleSegOp ::+  SegOp SegLevel Kernels ->+  AllocM Kernels KernelsMem (SegOp SegLevel KernelsMem) handleSegOp op = do-  num_threads <- letSubExp "num_threads" $ BasicOp $ BinOp (Mul Int32 OverflowUndef)-                 (unCount (segNumGroups lvl)) (unCount (segGroupSize lvl))+  num_threads <-+    letSubExp "num_threads" $+      BasicOp $+        BinOp+          (Mul Int64 OverflowUndef)+          (unCount (segNumGroups lvl))+          (unCount (segGroupSize lvl))   allocAtLevel lvl $ mapSegOpM (mapper num_threads) op-  where scope = scopeOfSegSpace $ segSpace op-        lvl = segLevel op-        mapper num_threads =-          identitySegOpMapper-          { mapOnSegOpBody =-              localScope scope . local f . allocInKernelBody-          , mapOnSegOpLambda =-              local inThread .-              allocInBinOpLambda num_threads (segSpace op)-          }-        f = case segLevel op of SegThread{} -> inThread-                                SegGroup{} -> inGroup-        inThread env = env { envExpHints = inThreadExpHints }-        inGroup env = env { envExpHints = inGroupExpHints }+  where+    scope = scopeOfSegSpace $ segSpace op+    lvl = segLevel op+    mapper num_threads =+      identitySegOpMapper+        { mapOnSegOpBody =+            localScope scope . local f . allocInKernelBody,+          mapOnSegOpLambda =+            local inThread+              . allocInBinOpLambda num_threads (segSpace op)+        }+    f = case segLevel op of+      SegThread {} -> inThread+      SegGroup {} -> inGroup+    inThread env = env {envExpHints = inThreadExpHints}+    inGroup env = env {envExpHints = inGroupExpHints} -handleHostOp :: HostOp Kernels (SOAC Kernels)-             -> AllocM Kernels KernelsMem (MemOp (HostOp KernelsMem ()))+handleHostOp ::+  HostOp Kernels (SOAC Kernels) ->+  AllocM Kernels KernelsMem (MemOp (HostOp KernelsMem ())) handleHostOp (SizeOp op) =   return $ Inner $ SizeOp op handleHostOp (OtherOp op) =@@ -72,104 +85,111 @@   dims <- arrayDims <$> lookupType v   let perm_inv = rearrangeInverse perm       dims' = rearrangeShape perm dims-      ixfun = IxFun.permute (IxFun.iota $ map (primExpFromSubExp int32) dims')-              perm_inv+      ixfun = IxFun.permute (IxFun.iota $ map pe64 dims') perm_inv   return [Hint ixfun DefaultSpace]--kernelExpHints (Op (Inner (SegOp (SegMap lvl@SegThread{} space ts body)))) =+kernelExpHints (Op (Inner (SegOp (SegMap lvl@SegThread {} space ts body)))) =   zipWithM (mapResultHint lvl space) ts $ kernelBodyResult body--kernelExpHints (Op (Inner (SegOp (SegRed lvl@SegThread{} space reds ts body)))) =+kernelExpHints (Op (Inner (SegOp (SegRed lvl@SegThread {} space reds ts body)))) =   (map (const NoHint) red_res <>) <$> zipWithM (mapResultHint lvl space) (drop num_reds ts) map_res-  where num_reds = segBinOpResults reds-        (red_res, map_res) = splitAt num_reds $ kernelBodyResult body-+  where+    num_reds = segBinOpResults reds+    (red_res, map_res) = splitAt num_reds $ kernelBodyResult body kernelExpHints e =   return $ replicate (expExtTypeSize e) NoHint -mapResultHint :: Allocator lore m =>-                 SegLevel -> SegSpace -> Type -> KernelResult -> m ExpHint+mapResultHint ::+  Allocator lore m =>+  SegLevel ->+  SegSpace ->+  Type ->+  KernelResult ->+  m ExpHint mapResultHint lvl space = hint-  where num_threads = primExpFromSubExp int32 (unCount $ segNumGroups lvl) *-                      primExpFromSubExp int32 (unCount $ segGroupSize lvl)--        -- Heuristic: do not rearrange for returned arrays that are-        -- sufficiently small.-        coalesceReturnOfShape _ [] = False-        coalesceReturnOfShape bs [Constant (IntValue (Int32Value d))] = bs * d > 4-        coalesceReturnOfShape _ _ = True--        hint t Returns{}-          | coalesceReturnOfShape (primByteSize (elemType t)) $ arrayDims t = do-              let space_dims = segSpaceDims space-              t_dims <- mapM dimAllocationSize $ arrayDims t-              return $ Hint (innermost space_dims t_dims) DefaultSpace--        hint t (ConcatReturns SplitStrided{} w _ _) = do-          t_dims <- mapM dimAllocationSize $ arrayDims t-          return $ Hint (innermost [w] t_dims) DefaultSpace+  where+    num_threads =+      pe64 (unCount $ segNumGroups lvl) * pe64 (unCount $ segGroupSize lvl) -        hint Prim{} (ConcatReturns SplitContiguous w elems_per_thread _) = do-          let ixfun_base = IxFun.iota [num_threads, primExpFromSubExp int32 elems_per_thread]-              ixfun_tr = IxFun.permute ixfun_base [1,0]-              ixfun = IxFun.reshape ixfun_tr $ map (DimNew . primExpFromSubExp int32) [w]-          return $ Hint ixfun DefaultSpace+    -- Heuristic: do not rearrange for returned arrays that are+    -- sufficiently small.+    coalesceReturnOfShape _ [] = False+    coalesceReturnOfShape bs [Constant (IntValue (Int64Value d))] = bs * d > 4+    coalesceReturnOfShape _ _ = True -        hint _ _ = return NoHint+    hint t Returns {}+      | coalesceReturnOfShape (primByteSize (elemType t)) $ arrayDims t = do+        let space_dims = segSpaceDims space+        t_dims <- mapM dimAllocationSize $ arrayDims t+        return $ Hint (innermost space_dims t_dims) DefaultSpace+    hint t (ConcatReturns SplitStrided {} w _ _) = do+      t_dims <- mapM dimAllocationSize $ arrayDims t+      return $ Hint (innermost [w] t_dims) DefaultSpace+    hint Prim {} (ConcatReturns SplitContiguous w elems_per_thread _) = do+      let ixfun_base = IxFun.iota [sExt64 num_threads, pe64 elems_per_thread]+          ixfun_tr = IxFun.permute ixfun_base [1, 0]+          ixfun = IxFun.reshape ixfun_tr $ map (DimNew . pe64) [w]+      return $ Hint ixfun DefaultSpace+    hint _ _ = return NoHint  innermost :: [SubExp] -> [SubExp] -> IxFun innermost space_dims t_dims =   let r = length t_dims       dims = space_dims ++ t_dims-      perm = [length space_dims..length space_dims+r-1] ++-             [0..length space_dims-1]+      perm =+        [length space_dims .. length space_dims + r -1]+          ++ [0 .. length space_dims -1]       perm_inv = rearrangeInverse perm       dims_perm = rearrangeShape perm dims-      ixfun_base = IxFun.iota $ map (primExpFromSubExp int32) dims_perm+      ixfun_base = IxFun.iota $ map pe64 dims_perm       ixfun_rearranged = IxFun.permute ixfun_base perm_inv-  in ixfun_rearranged+   in ixfun_rearranged  semiStatic :: S.Set VName -> SubExp -> Bool-semiStatic _ Constant{} = True+semiStatic _ Constant {} = True semiStatic consts (Var v) = v `S.member` consts  inGroupExpHints :: Allocator KernelsMem m => Exp KernelsMem -> m [ExpHint] inGroupExpHints (Op (Inner (SegOp (SegMap _ space ts body))))   | any private $ kernelBodyResult body = do-      consts <- askConsts-      return $ do-        (t, r) <- zip ts $ kernelBodyResult body-        return $-          if private r && all (semiStatic consts) (arrayDims t)-          then let seg_dims = map (primExpFromSubExp int32) $ segSpaceDims space-                   dims = seg_dims ++ map (primExpFromSubExp int32) (arrayDims t)-                   nilSlice d = DimSlice 0 d 0-             in Hint (IxFun.slice (IxFun.iota dims) $-                      fullSliceNum dims $ map nilSlice seg_dims) $-                ScalarSpace (arrayDims t) $ elemType t+    consts <- askConsts+    return $ do+      (t, r) <- zip ts $ kernelBodyResult body+      return $+        if private r && all (semiStatic consts) (arrayDims t)+          then+            let seg_dims = map pe64 $ segSpaceDims space+                dims = seg_dims ++ map pe64 (arrayDims t)+                nilSlice d = DimSlice 0 d 0+             in Hint+                  ( IxFun.slice (IxFun.iota dims) $+                      fullSliceNum dims $ map nilSlice seg_dims+                  )+                  $ ScalarSpace (arrayDims t) $ elemType t           else NoHint-  where private (Returns ResultPrivate _) = True-        private _                         = False+  where+    private (Returns ResultPrivate _) = True+    private _ = False inGroupExpHints e = return $ replicate (expExtTypeSize e) NoHint  inThreadExpHints :: Allocator KernelsMem m => Exp KernelsMem -> m [ExpHint] inThreadExpHints e = do   consts <- askConsts   mapM (maybePrivate consts) =<< expExtType e-  where maybePrivate consts t-          | Just (Array pt shape _) <- hasStaticShape t,-            all (semiStatic consts) $ shapeDims shape = do-              let ixfun = IxFun.iota $ map (primExpFromSubExp int32) $-                          shapeDims shape-              return $ Hint ixfun $ ScalarSpace (shapeDims shape) pt-          | otherwise =-              return NoHint+  where+    maybePrivate consts t+      | Just (Array pt shape _) <- hasStaticShape t,+        all (semiStatic consts) $ shapeDims shape = do+        let ixfun = IxFun.iota $ map pe64 $ shapeDims shape+        return $ Hint ixfun $ ScalarSpace (shapeDims shape) pt+      | otherwise =+        return NoHint  -- | The pass from 'Kernels' to 'KernelsMem'. explicitAllocations :: Pass Kernels KernelsMem explicitAllocations = explicitAllocationsGeneric handleHostOp kernelExpHints  -- | Convert some 'Kernels' stms to 'KernelsMem'.-explicitAllocationsInStms :: (MonadFreshNames m, HasScope KernelsMem m) =>-                             Stms Kernels -> m (Stms KernelsMem)+explicitAllocationsInStms ::+  (MonadFreshNames m, HasScope KernelsMem m) =>+  Stms Kernels ->+  m (Stms KernelsMem) explicitAllocationsInStms = explicitAllocationsInStmsGeneric handleHostOp kernelExpHints
src/Futhark/Pass/ExplicitAllocations/SegOp.hs view
@@ -1,73 +1,94 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeFamilies #-}+ module Futhark.Pass.ExplicitAllocations.SegOp-       ( allocInKernelBody-       , allocInBinOpLambda-       )+  ( allocInKernelBody,+    allocInBinOpLambda,+  ) where -import qualified Futhark.IR.Mem.IxFun as IxFun import Futhark.IR.KernelsMem+import qualified Futhark.IR.Mem.IxFun as IxFun import Futhark.Pass.ExplicitAllocations -allocInKernelBody :: Allocable fromlore tolore =>-                     KernelBody fromlore-                  -> AllocM fromlore tolore (KernelBody tolore)+allocInKernelBody ::+  Allocable fromlore tolore =>+  KernelBody fromlore ->+  AllocM fromlore tolore (KernelBody tolore) allocInKernelBody (KernelBody () stms res) =   allocInStms stms $ \stms' -> return $ KernelBody () stms' res -allocInLambda :: Allocable fromlore tolore =>-                 [LParam tolore] -> Body fromlore -> [Type]-              -> AllocM fromlore tolore (Lambda tolore)+allocInLambda ::+  Allocable fromlore tolore =>+  [LParam tolore] ->+  Body fromlore ->+  [Type] ->+  AllocM fromlore tolore (Lambda tolore) allocInLambda params body rettype = do   body' <- localScope (scopeOfLParams params) $-           allocInStms (bodyStms body) $ \bnds' ->-           return $ Body () bnds' $ bodyResult body+    allocInStms (bodyStms body) $ \bnds' ->+      return $ Body () bnds' $ bodyResult body   return $ Lambda params body' rettype -allocInBinOpParams :: Allocable fromlore tolore =>-                      SubExp-                   -> PrimExp VName -> PrimExp VName-                   -> [LParam fromlore]-                   -> [LParam fromlore]-                   -> AllocM fromlore tolore ([LParam tolore], [LParam tolore])+allocInBinOpParams ::+  Allocable fromlore tolore =>+  SubExp ->+  TPrimExp Int64 VName ->+  TPrimExp Int64 VName ->+  [LParam fromlore] ->+  [LParam fromlore] ->+  AllocM fromlore tolore ([LParam tolore], [LParam tolore]) allocInBinOpParams num_threads my_id other_id xs ys = unzip <$> zipWithM alloc xs ys-  where alloc x y =-          case paramType x of-            Array bt shape u -> do-              twice_num_threads <--                letSubExp "twice_num_threads" $-                BasicOp $ BinOp (Mul Int32 OverflowUndef) num_threads $ intConst Int32 2-              let t = paramType x `arrayOfRow` twice_num_threads-              mem <- allocForArray t DefaultSpace-              -- XXX: this iota ixfun is a bit inefficient; leading to-              -- uncoalesced access.-              let base_dims = map (primExpFromSubExp int32) (arrayDims t)-                  ixfun_base = IxFun.iota base_dims-                  ixfun_x = IxFun.slice ixfun_base $-                            fullSliceNum base_dims [DimFix my_id]-                  ixfun_y = IxFun.slice ixfun_base $-                            fullSliceNum base_dims [DimFix other_id]-              return (x { paramDec = MemArray bt shape u $ ArrayIn mem ixfun_x },-                      y { paramDec = MemArray bt shape u $ ArrayIn mem ixfun_y })-            Prim bt ->-              return (x { paramDec = MemPrim bt },-                      y { paramDec = MemPrim bt })-            Mem space ->-              return (x { paramDec = MemMem space },-                      y { paramDec = MemMem space })+  where+    alloc x y =+      case paramType x of+        Array bt shape u -> do+          twice_num_threads <-+            letSubExp "twice_num_threads" $+              BasicOp $ BinOp (Mul Int64 OverflowUndef) num_threads $ intConst Int64 2+          let t = paramType x `arrayOfRow` twice_num_threads+          mem <- allocForArray t DefaultSpace+          -- XXX: this iota ixfun is a bit inefficient; leading to+          -- uncoalesced access.+          let base_dims = map pe64 $ arrayDims t+              ixfun_base = IxFun.iota base_dims+              ixfun_x =+                IxFun.slice ixfun_base $+                  fullSliceNum base_dims [DimFix my_id]+              ixfun_y =+                IxFun.slice ixfun_base $+                  fullSliceNum base_dims [DimFix other_id]+          return+            ( x {paramDec = MemArray bt shape u $ ArrayIn mem ixfun_x},+              y {paramDec = MemArray bt shape u $ ArrayIn mem ixfun_y}+            )+        Prim bt ->+          return+            ( x {paramDec = MemPrim bt},+              y {paramDec = MemPrim bt}+            )+        Mem space ->+          return+            ( x {paramDec = MemMem space},+              y {paramDec = MemMem space}+            ) -allocInBinOpLambda :: Allocable fromlore tolore =>-                      SubExp -> SegSpace -> Lambda fromlore-                   -> AllocM fromlore tolore (Lambda tolore)+allocInBinOpLambda ::+  Allocable fromlore tolore =>+  SubExp ->+  SegSpace ->+  Lambda fromlore ->+  AllocM fromlore tolore (Lambda tolore) allocInBinOpLambda num_threads (SegSpace flat _) lam = do   let (acc_params, arr_params) =         splitAt (length (lambdaParams lam) `div` 2) $ lambdaParams lam-      index_x = LeafExp flat int32-      index_y = index_x + primExpFromSubExp int32 num_threads+      index_x = TPrimExp $ LeafExp flat int64+      index_y = index_x + pe64 num_threads   (acc_params', arr_params') <-     allocInBinOpParams num_threads index_x index_y acc_params arr_params -  allocInLambda (acc_params' ++ arr_params')-    (lambdaBody lam) (lambdaReturnType lam)+  allocInLambda+    (acc_params' ++ arr_params')+    (lambdaBody lam)+    (lambdaReturnType lam)
src/Futhark/Pass/ExplicitAllocations/Seq.hs view
@@ -1,15 +1,16 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-}+ module Futhark.Pass.ExplicitAllocations.Seq-       ( explicitAllocations-       , simplifiable-       )+  ( explicitAllocations,+    simplifiable,+  ) where -import Futhark.Pass-import Futhark.IR.SeqMem import Futhark.IR.Seq+import Futhark.IR.SeqMem+import Futhark.Pass import Futhark.Pass.ExplicitAllocations  explicitAllocations :: Pass Seq SeqMem
src/Futhark/Pass/ExtractKernels.hs view
@@ -1,12 +1,13 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-}-{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE RankNTypes #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+ -- | Kernel extraction. -- -- In the following, I will use the term "width" to denote the amount@@ -157,43 +158,41 @@ -- intermediate array (@b@ above) must be written to main memory.  An -- often better approach is to just turn the entire @redomap@ into a -- single kernel.--- module Futhark.Pass.ExtractKernels (extractKernels) where  import Control.Monad.Identity import Control.Monad.RWS.Strict import Control.Monad.Reader import Data.Maybe--import Prelude hiding (log)--import Futhark.IR.SOACS-import Futhark.IR.SOACS.Simplify (simplifyStms) import qualified Futhark.IR.Kernels as Out import Futhark.IR.Kernels.Kernel+import Futhark.IR.SOACS+import Futhark.IR.SOACS.Simplify (simplifyStms) import Futhark.MonadFreshNames-import Futhark.Tools-import qualified Futhark.Transform.FirstOrderTransform as FOT-import Futhark.Transform.Rename import Futhark.Pass-import Futhark.Pass.ExtractKernels.Distribution+import Futhark.Pass.ExtractKernels.BlockedKernel import Futhark.Pass.ExtractKernels.DistributeNests+import Futhark.Pass.ExtractKernels.Distribution import Futhark.Pass.ExtractKernels.ISRWIM-import Futhark.Pass.ExtractKernels.BlockedKernel import Futhark.Pass.ExtractKernels.Intragroup import Futhark.Pass.ExtractKernels.StreamKernel import Futhark.Pass.ExtractKernels.ToKernels+import Futhark.Tools+import qualified Futhark.Transform.FirstOrderTransform as FOT+import Futhark.Transform.Rename import Futhark.Util import Futhark.Util.Log+import Prelude hiding (log)  -- | Transform a program using SOACs to a program using explicit -- kernels, using the kernel extraction transformation. extractKernels :: Pass SOACS Out.Kernels extractKernels =-  Pass { passName = "extract kernels"-       , passDescription = "Perform kernel extraction"-       , passFunction = transformProg-       }+  Pass+    { passName = "extract kernels",+      passDescription = "Perform kernel extraction",+      passFunction = transformProg+    }  transformProg :: Prog SOACS -> PassM (Prog Out.Kernels) transformProg (Prog consts funs) = do@@ -204,175 +203,190 @@ -- In order to generate more stable threshold names, we keep track of -- the numbers used for thresholds separately from the ordinary name -- source,-data State = State { stateNameSource :: VNameSource-                   , stateThresholdCounter :: Int-                   }+data State = State+  { stateNameSource :: VNameSource,+    stateThresholdCounter :: Int+  }  newtype DistribM a = DistribM (RWS (Scope Out.Kernels) Log State a)-                   deriving (Functor, Applicative, Monad,-                             HasScope Out.Kernels, LocalScope Out.Kernels,-                             MonadState State,-                             MonadLogger)+  deriving+    ( Functor,+      Applicative,+      Monad,+      HasScope Out.Kernels,+      LocalScope Out.Kernels,+      MonadState State,+      MonadLogger+    )  instance MonadFreshNames DistribM where   getNameSource = gets stateNameSource-  putNameSource src = modify $ \s -> s { stateNameSource = src }+  putNameSource src = modify $ \s -> s {stateNameSource = src} -runDistribM :: (MonadLogger m, MonadFreshNames m) =>-               DistribM a -> m a+runDistribM ::+  (MonadLogger m, MonadFreshNames m) =>+  DistribM a ->+  m a runDistribM (DistribM m) = do   (x, msgs) <- modifyNameSource $ \src ->     let (x, s, msgs) = runRWS m mempty (State src 0)-    in ((x, msgs), stateNameSource s)+     in ((x, msgs), stateNameSource s)   addLog msgs   return x -transformFunDef :: (MonadFreshNames m, MonadLogger m) =>-                   Scope Out.Kernels -> FunDef SOACS-                -> m (Out.FunDef Out.Kernels)+transformFunDef ::+  (MonadFreshNames m, MonadLogger m) =>+  Scope Out.Kernels ->+  FunDef SOACS ->+  m (Out.FunDef Out.Kernels) transformFunDef scope (FunDef entry attrs name rettype params body) = runDistribM $ do-  body' <- localScope (scope <> scopeOfFParams params) $-           transformBody mempty body+  body' <-+    localScope (scope <> scopeOfFParams params) $+      transformBody mempty body   return $ FunDef entry attrs name rettype params body'  type KernelsStms = Stms Out.Kernels  transformBody :: KernelPath -> Body -> DistribM (Out.Body Out.Kernels)-transformBody path body = do bnds <- transformStms path $ stmsToList $ bodyStms body-                             return $ mkBody bnds $ bodyResult body+transformBody path body = do+  bnds <- transformStms path $ stmsToList $ bodyStms body+  return $ mkBody bnds $ bodyResult body  transformStms :: KernelPath -> [Stm] -> DistribM KernelsStms transformStms _ [] =   return mempty-transformStms path (bnd:bnds) =+transformStms path (bnd : bnds) =   sequentialisedUnbalancedStm bnd >>= \case     Nothing -> do       bnd' <- transformStm path bnd       inScopeOf bnd' $-        (bnd'<>) <$> transformStms path bnds+        (bnd' <>) <$> transformStms path bnds     Just bnds' ->       transformStms path $ stmsToList bnds' <> bnds  unbalancedLambda :: Lambda -> Bool unbalancedLambda lam =   unbalancedBody-  (namesFromList $ map paramName $ lambdaParams lam) $-  lambdaBody lam--  where subExpBound (Var i) bound = i `nameIn` bound-        subExpBound (Constant _) _ = False--        unbalancedBody bound body =-          any (unbalancedStm (bound <> boundInBody body) . stmExp) $-          bodyStms body--        -- XXX - our notion of balancing is probably still too naive.-        unbalancedStm bound (Op (Stream w _ _ _)) =-          w `subExpBound` bound-        unbalancedStm bound (Op (Screma w _ _)) =-          w `subExpBound` bound-        unbalancedStm _ Op{} =-          False-        unbalancedStm _ DoLoop{} = False+    (namesFromList $ map paramName $ lambdaParams lam)+    $ lambdaBody lam+  where+    subExpBound (Var i) bound = i `nameIn` bound+    subExpBound (Constant _) _ = False -        unbalancedStm bound (If cond tbranch fbranch _) =-          cond `subExpBound` bound &&-          (unbalancedBody bound tbranch || unbalancedBody bound fbranch)+    unbalancedBody bound body =+      any (unbalancedStm (bound <> boundInBody body) . stmExp) $+        bodyStms body -        unbalancedStm _ (BasicOp _) =-          False-        unbalancedStm _ (Apply fname _ _ _) =-          not $ isBuiltInFunction fname+    -- XXX - our notion of balancing is probably still too naive.+    unbalancedStm bound (Op (Stream w _ _ _)) =+      w `subExpBound` bound+    unbalancedStm bound (Op (Screma w _ _)) =+      w `subExpBound` bound+    unbalancedStm _ Op {} =+      False+    unbalancedStm _ DoLoop {} = False+    unbalancedStm bound (If cond tbranch fbranch _) =+      cond `subExpBound` bound+        && (unbalancedBody bound tbranch || unbalancedBody bound fbranch)+    unbalancedStm _ (BasicOp _) =+      False+    unbalancedStm _ (Apply fname _ _ _) =+      not $ isBuiltInFunction fname  sequentialisedUnbalancedStm :: Stm -> DistribM (Maybe (Stms SOACS)) sequentialisedUnbalancedStm (Let pat _ (Op soac@(Screma _ form _)))   | Just (_, lam2) <- isRedomapSOAC form,-    unbalancedLambda lam2, lambdaContainsParallelism lam2 = do-      types <- asksScope scopeForSOACs-      Just . snd <$> runBinderT (FOT.transformSOAC pat soac) types+    unbalancedLambda lam2,+    lambdaContainsParallelism lam2 = do+    types <- asksScope scopeForSOACs+    Just . snd <$> runBinderT (FOT.transformSOAC pat soac) types sequentialisedUnbalancedStm _ =   return Nothing -cmpSizeLe :: String -> Out.SizeClass -> [SubExp]-          -> DistribM ((SubExp, Name), Out.Stms Out.Kernels)+cmpSizeLe ::+  String ->+  Out.SizeClass ->+  [SubExp] ->+  DistribM ((SubExp, Name), Out.Stms Out.Kernels) cmpSizeLe desc size_class to_what = do   x <- gets stateThresholdCounter-  modify $ \s -> s { stateThresholdCounter = x + 1}+  modify $ \s -> s {stateThresholdCounter = x + 1}   let size_key = nameFromString $ desc ++ "_" ++ show x   runBinder $ do-    to_what' <- letSubExp "comparatee" =<<-                foldBinOp (Mul Int32 OverflowUndef) (intConst Int32 1) to_what+    to_what' <-+      letSubExp "comparatee"+        =<< foldBinOp (Mul Int64 OverflowUndef) (intConst Int64 1) to_what     cmp_res <- letSubExp desc $ Op $ SizeOp $ CmpSizeLe size_key size_class to_what'     return (cmp_res, size_key) -kernelAlternatives :: (MonadFreshNames m, HasScope Out.Kernels m) =>-                      Out.Pattern Out.Kernels-                   -> Out.Body Out.Kernels-                   -> [(SubExp, Out.Body Out.Kernels)]-                   -> m (Out.Stms Out.Kernels)+kernelAlternatives ::+  (MonadFreshNames m, HasScope Out.Kernels m) =>+  Out.Pattern Out.Kernels ->+  Out.Body Out.Kernels ->+  [(SubExp, Out.Body Out.Kernels)] ->+  m (Out.Stms Out.Kernels) kernelAlternatives pat default_body [] = runBinder_ $ do   ses <- bodyBind default_body   forM_ (zip (patternNames pat) ses) $ \(name, se) ->     letBindNames [name] $ BasicOp $ SubExp se-kernelAlternatives pat default_body ((cond,alt):alts) = runBinder_ $ do-  alts_pat <- fmap (Pattern []) $ forM (patternElements pat) $ \pe -> do-    name <- newVName $ baseString $ patElemName pe-    return pe { patElemName = name }+kernelAlternatives pat default_body ((cond, alt) : alts) = runBinder_ $ do+  alts_pat <- fmap (Pattern []) $+    forM (patternElements pat) $ \pe -> do+      name <- newVName $ baseString $ patElemName pe+      return pe {patElemName = name}    alt_stms <- kernelAlternatives alts_pat default_body alts   let alt_body = mkBody alt_stms $ map Var $ patternValueNames alts_pat -  letBind pat $ If cond alt alt_body $-    IfDec (staticShapes (patternTypes pat)) IfEquiv+  letBind pat $+    If cond alt alt_body $+      IfDec (staticShapes (patternTypes pat)) IfEquiv  transformStm :: KernelPath -> Stm -> DistribM KernelsStms- transformStm _ stm   | "sequential" `inAttrs` stmAuxAttrs (stmAux stm) =     runBinder_ $ FOT.transformStmRecursively stm- transformStm path (Let pat aux (Op soac))   | "sequential_outer" `inAttrs` stmAuxAttrs aux =-      transformStms path . stmsToList . fmap (certify (stmAuxCerts aux)) =<<-      runBinder_ (FOT.transformSOAC pat soac)-+    transformStms path . stmsToList . fmap (certify (stmAuxCerts aux))+      =<< runBinder_ (FOT.transformSOAC pat soac) transformStm path (Let pat aux (If c tb fb rt)) = do   tb' <- transformBody path tb   fb' <- transformBody path fb   return $ oneStm $ Let pat aux $ If c tb' fb' rt- transformStm path (Let pat aux (DoLoop ctx val form body)) =-  localScope (castScope (scopeOf form) <>-              scopeOfFParams mergeparams) $-    oneStm . Let pat aux . DoLoop ctx val form' <$> transformBody path body-  where mergeparams = map fst $ ctx ++ val-        form' = case form of-                  WhileLoop cond ->-                    WhileLoop cond-                  ForLoop i it bound ps ->-                    ForLoop i it bound ps-+  localScope+    ( castScope (scopeOf form)+        <> scopeOfFParams mergeparams+    )+    $ oneStm . Let pat aux . DoLoop ctx val form' <$> transformBody path body+  where+    mergeparams = map fst $ ctx ++ val+    form' = case form of+      WhileLoop cond ->+        WhileLoop cond+      ForLoop i it bound ps ->+        ForLoop i it bound ps transformStm path (Let pat aux (Op (Screma w form arrs)))   | Just lam <- isMapSOAC form =-      onMap path $ MapLoop pat aux w lam arrs-+    onMap path $ MapLoop pat aux w lam arrs transformStm path (Let res_pat (StmAux cs _ _) (Op (Screma w form arrs)))   | Just scans <- isScanSOAC form,     Scan scan_lam nes <- singleScan scans,     Just do_iswim <- iswim res_pat w scan_lam $ zip nes arrs = do-      types <- asksScope scopeForSOACs-      transformStms path =<< (stmsToList . snd <$> runBinderT (certifying cs do_iswim) types)-+    types <- asksScope scopeForSOACs+    transformStms path =<< (stmsToList . snd <$> runBinderT (certifying cs do_iswim) types)   | Just (scans, map_lam) <- isScanomapSOAC form = runBinder_ $ do-      scan_ops <- forM scans $ \(Scan scan_lam nes) -> do-          (scan_lam', nes', shape) <- determineReduceOp scan_lam nes-          let scan_lam'' = soacsLambdaToKernels scan_lam'-          return $ SegBinOp Noncommutative scan_lam'' nes' shape-      let map_lam_sequential = soacsLambdaToKernels map_lam-      lvl <- segThreadCapped [w] "segscan" $ NoRecommendation SegNoVirt-      addStms =<<-        (fmap (certify cs) <$>-         segScan lvl res_pat w scan_ops map_lam_sequential arrs [] [])+    scan_ops <- forM scans $ \(Scan scan_lam nes) -> do+      (scan_lam', nes', shape) <- determineReduceOp scan_lam nes+      let scan_lam'' = soacsLambdaToKernels scan_lam'+      return $ SegBinOp Noncommutative scan_lam'' nes' shape+    let map_lam_sequential = soacsLambdaToKernels map_lam+    lvl <- segThreadCapped [w] "segscan" $ NoRecommendation SegNoVirt+    addStms+      =<< ( fmap (certify cs)+              <$> segScan lvl res_pat w scan_ops map_lam_sequential arrs [] []+          )    -- We are only willing to generate code for scanomaps that do not   -- involve array accumulators, and do not have parallelism in their@@ -382,170 +396,186 @@   -- we will still crash in code generation).  However, if the map   -- lambda is already identity, let's just go ahead here.   | Just (scans, map_lam) <- isScanomapSOAC form,-    (all primType (concatMap (lambdaReturnType . scanLambda) scans) &&-     not (lambdaContainsParallelism map_lam)) || isIdentityLambda map_lam = runBinder_ $ do--      scan_ops <- forM scans $ \(Scan scan_lam nes) -> do-        let scan_lam' = soacsLambdaToKernels scan_lam-        return $ SegBinOp Noncommutative scan_lam' nes mempty--      let map_lam' = soacsLambdaToKernels map_lam-      lvl <- segThreadCapped [w] "segscan" $ NoRecommendation SegNoVirt-      addStms =<< segScan lvl res_pat w scan_ops map_lam' arrs [] []+    ( all primType (concatMap (lambdaReturnType . scanLambda) scans)+        && not (lambdaContainsParallelism map_lam)+    )+      || isIdentityLambda map_lam = runBinder_ $ do+    scan_ops <- forM scans $ \(Scan scan_lam nes) -> do+      let scan_lam' = soacsLambdaToKernels scan_lam+      return $ SegBinOp Noncommutative scan_lam' nes mempty +    let map_lam' = soacsLambdaToKernels map_lam+    lvl <- segThreadCapped [w] "segscan" $ NoRecommendation SegNoVirt+    addStms =<< segScan lvl res_pat w scan_ops map_lam' arrs [] [] transformStm path (Let res_pat aux (Op (Screma w form arrs)))   | Just [Reduce comm red_fun nes] <- isReduceSOAC form,-    let comm' | commutativeLambda red_fun = Commutative-              | otherwise                 = comm,+    let comm'+          | commutativeLambda red_fun = Commutative+          | otherwise = comm,     Just do_irwim <- irwim res_pat w comm' red_fun $ zip nes arrs = do-      types <- asksScope scopeForSOACs-      (_, bnds) <- fst <$> runBinderT (simplifyStms =<< collectStms_ (auxing aux do_irwim)) types-      transformStms path $ stmsToList bnds-+    types <- asksScope scopeForSOACs+    (_, bnds) <- fst <$> runBinderT (simplifyStms =<< collectStms_ (auxing aux do_irwim)) types+    transformStms path $ stmsToList bnds transformStm path (Let pat aux@(StmAux cs _ _) (Op (Screma w form arrs)))   | Just (reds, map_lam) <- isRedomapSOAC form = do--  let paralleliseOuter = runBinder_ $ do-        red_ops <- forM reds $ \(Reduce comm red_lam nes) -> do-          (red_lam', nes', shape) <- determineReduceOp red_lam nes-          let comm' | commutativeLambda red_lam' = Commutative-                    | otherwise = comm-              red_lam'' = soacsLambdaToKernels red_lam'-          return $ SegBinOp comm' red_lam'' nes' shape-        let map_lam_sequential = soacsLambdaToKernels map_lam-        lvl <- segThreadCapped [w] "segred" $ NoRecommendation SegNoVirt-        addStms =<<-          (fmap (certify cs) <$>-           nonSegRed lvl pat w red_ops map_lam_sequential arrs)+    let paralleliseOuter = runBinder_ $ do+          red_ops <- forM reds $ \(Reduce comm red_lam nes) -> do+            (red_lam', nes', shape) <- determineReduceOp red_lam nes+            let comm'+                  | commutativeLambda red_lam' = Commutative+                  | otherwise = comm+                red_lam'' = soacsLambdaToKernels red_lam'+            return $ SegBinOp comm' red_lam'' nes' shape+          let map_lam_sequential = soacsLambdaToKernels map_lam+          lvl <- segThreadCapped [w] "segred" $ NoRecommendation SegNoVirt+          addStms+            =<< ( fmap (certify cs)+                    <$> nonSegRed lvl pat w red_ops map_lam_sequential arrs+                ) -      outerParallelBody =-        renameBody =<<-        (mkBody <$> paralleliseOuter <*> pure (map Var (patternNames pat)))+        outerParallelBody =+          renameBody+            =<< (mkBody <$> paralleliseOuter <*> pure (map Var (patternNames pat))) -      paralleliseInner path' = do-        (mapbnd, redbnd) <- redomapToMapAndReduce pat (w, comm', red_lam, map_lam, nes, arrs)-        transformStms path' [certify cs mapbnd, certify cs redbnd]-          where comm' | commutativeLambda red_lam = Commutative-                      | otherwise = comm-                (Reduce comm red_lam nes) = singleReduce reds+        paralleliseInner path' = do+          (mapbnd, redbnd) <- redomapToMapAndReduce pat (w, comm', red_lam, map_lam, nes, arrs)+          transformStms path' [certify cs mapbnd, certify cs redbnd]+          where+            comm'+              | commutativeLambda red_lam = Commutative+              | otherwise = comm+            (Reduce comm red_lam nes) = singleReduce reds -      innerParallelBody path' =-        renameBody =<<-        (mkBody <$> paralleliseInner path' <*> pure (map Var (patternNames pat)))+        innerParallelBody path' =+          renameBody+            =<< (mkBody <$> paralleliseInner path' <*> pure (map Var (patternNames pat))) -  if not (lambdaContainsParallelism map_lam) ||-     "sequential_inner" `inAttrs` stmAuxAttrs aux-    then paralleliseOuter-    else do-    ((outer_suff, outer_suff_key), suff_stms) <--      sufficientParallelism "suff_outer_redomap" [w] path Nothing+    if not (lambdaContainsParallelism map_lam)+      || "sequential_inner" `inAttrs` stmAuxAttrs aux+      then paralleliseOuter+      else do+        ((outer_suff, outer_suff_key), suff_stms) <-+          sufficientParallelism "suff_outer_redomap" [w] path Nothing -    outer_stms <- outerParallelBody-    inner_stms <- innerParallelBody ((outer_suff_key, False):path)+        outer_stms <- outerParallelBody+        inner_stms <- innerParallelBody ((outer_suff_key, False) : path) -    (suff_stms<>) <$> kernelAlternatives pat inner_stms [(outer_suff, outer_stms)]+        (suff_stms <>) <$> kernelAlternatives pat inner_stms [(outer_suff, outer_stms)]  -- Streams can be handled in two different ways - either we -- sequentialise the body or we keep it parallel and distribute. transformStm path (Let pat aux@(StmAux cs _ _) (Op (Stream w (Parallel _ _ _ []) map_fun arrs)))   | not ("sequential_inner" `inAttrs` stmAuxAttrs aux) = do-  -- No reduction part.  Remove the stream and leave the body-  -- parallel.  It will be distributed.-  types <- asksScope scopeForSOACs-  transformStms path =<<-    (stmsToList . snd <$> runBinderT (certifying cs $ sequentialStreamWholeArray pat w [] map_fun arrs) types)-+    -- No reduction part.  Remove the stream and leave the body+    -- parallel.  It will be distributed.+    types <- asksScope scopeForSOACs+    transformStms path+      =<< (stmsToList . snd <$> runBinderT (certifying cs $ sequentialStreamWholeArray pat w [] map_fun arrs) types) transformStm path (Let pat aux@(StmAux cs _ _) (Op (Stream w (Parallel o comm red_fun nes) fold_fun arrs)))   | "sequential_inner" `inAttrs` stmAuxAttrs aux =-      paralleliseOuter path+    paralleliseOuter path   | otherwise = do-      ((outer_suff, outer_suff_key), suff_stms) <--        sufficientParallelism "suff_outer_stream" [w] path Nothing--      outer_stms <- outerParallelBody ((outer_suff_key, True) : path)-      inner_stms <- innerParallelBody ((outer_suff_key, False) : path)+    ((outer_suff, outer_suff_key), suff_stms) <-+      sufficientParallelism "suff_outer_stream" [w] path Nothing -      (suff_stms<>) <$>-        kernelAlternatives pat inner_stms [(outer_suff, outer_stms)]+    outer_stms <- outerParallelBody ((outer_suff_key, True) : path)+    inner_stms <- innerParallelBody ((outer_suff_key, False) : path) +    (suff_stms <>)+      <$> kernelAlternatives pat inner_stms [(outer_suff, outer_stms)]   where     paralleliseOuter path'       | not $ all primType $ lambdaReturnType red_fun = do-          -- Split into a chunked map and a reduction, with the latter-          -- further transformed.-          let fold_fun' = soacsLambdaToKernels fold_fun--          let (red_pat_elems, concat_pat_elems) =-                splitAt (length nes) $ patternValueElements pat-              red_pat = Pattern [] red_pat_elems+        -- Split into a chunked map and a reduction, with the latter+        -- further transformed.+        let fold_fun' = soacsLambdaToKernels fold_fun -          ((num_threads, red_results), stms) <--            streamMap segThreadCapped-            (map (baseString . patElemName) red_pat_elems) concat_pat_elems w-            Noncommutative fold_fun' nes arrs+        let (red_pat_elems, concat_pat_elems) =+              splitAt (length nes) $ patternValueElements pat+            red_pat = Pattern [] red_pat_elems -          reduce_soac <- reduceSOAC [Reduce comm' red_fun nes]+        ((num_threads, red_results), stms) <-+          streamMap+            segThreadCapped+            (map (baseString . patElemName) red_pat_elems)+            concat_pat_elems+            w+            Noncommutative+            fold_fun'+            nes+            arrs -          (stms<>) <$>-            inScopeOf stms-            (transformStm path' $ Let red_pat aux { stmAuxAttrs = mempty } $-             Op (Screma num_threads reduce_soac red_results))+        reduce_soac <- reduceSOAC [Reduce comm' red_fun nes] +        (stms <>)+          <$> inScopeOf+            stms+            ( transformStm path' $+                Let red_pat aux {stmAuxAttrs = mempty} $+                  Op (Screma num_threads reduce_soac red_results)+            )       | otherwise = do-          let red_fun_sequential = soacsLambdaToKernels red_fun-              fold_fun_sequential = soacsLambdaToKernels fold_fun-          fmap (certify cs) <$>-            streamRed segThreadCapped-            pat w comm' red_fun_sequential fold_fun_sequential nes arrs+        let red_fun_sequential = soacsLambdaToKernels red_fun+            fold_fun_sequential = soacsLambdaToKernels fold_fun+        fmap (certify cs)+          <$> streamRed+            segThreadCapped+            pat+            w+            comm'+            red_fun_sequential+            fold_fun_sequential+            nes+            arrs      outerParallelBody path' =-      renameBody =<<-      (mkBody <$> paralleliseOuter path' <*> pure (map Var (patternNames pat)))+      renameBody+        =<< (mkBody <$> paralleliseOuter path' <*> pure (map Var (patternNames pat)))      paralleliseInner path' = do       types <- asksScope scopeForSOACs-      transformStms path' . fmap (certify cs) =<<-        (stmsToList . snd <$> runBinderT (sequentialStreamWholeArray pat w nes fold_fun arrs) types)+      transformStms path' . fmap (certify cs)+        =<< (stmsToList . snd <$> runBinderT (sequentialStreamWholeArray pat w nes fold_fun arrs) types)      innerParallelBody path' =-      renameBody =<<-      (mkBody <$> paralleliseInner path' <*> pure (map Var (patternNames pat)))--    comm' | commutativeLambda red_fun, o /= InOrder = Commutative-          | otherwise                               = comm+      renameBody+        =<< (mkBody <$> paralleliseInner path' <*> pure (map Var (patternNames pat))) +    comm'+      | commutativeLambda red_fun, o /= InOrder = Commutative+      | otherwise = comm transformStm path (Let pat (StmAux cs _ _) (Op (Screma w form arrs))) = do   -- This screma is too complicated for us to immediately do   -- anything, so split it up and try again.   scope <- asksScope scopeForSOACs-  transformStms path . map (certify cs) . stmsToList . snd =<<-    runBinderT (dissectScrema pat w form arrs) scope-+  transformStms path . map (certify cs) . stmsToList . snd+    =<< runBinderT (dissectScrema pat w form arrs) scope transformStm path (Let pat _ (Op (Stream w (Sequential nes) fold_fun arrs))) = do   -- Remove the stream and leave the body parallel.  It will be   -- distributed.   types <- asksScope scopeForSOACs-  transformStms path =<<-    (stmsToList . snd <$>-      runBinderT (sequentialStreamWholeArray pat w nes fold_fun arrs) types)-+  transformStms path+    =<< ( stmsToList . snd+            <$> runBinderT (sequentialStreamWholeArray pat w nes fold_fun arrs) types+        ) transformStm _ (Let pat (StmAux cs _ _) (Op (Scatter w lam ivs as))) = runBinder_ $ do   let lam' = soacsLambdaToKernels lam   write_i <- newVName "write_i"   let (as_ws, as_ns, as_vs) = unzip3 as       (i_res, v_res) = splitAt (sum as_ns) $ bodyResult $ lambdaBody lam'       kstms = bodyStms $ lambdaBody lam'-      krets = do (a_w, a, is_vs) <- zip3 as_ws as_vs $ chunks as_ns $ zip i_res v_res-                 return $ WriteReturns [a_w] a [ ([DimFix i],v) | (i,v) <- is_vs ]+      krets = do+        (a_w, a, is_vs) <- zip3 as_ws as_vs $ chunks as_ns $ zip i_res v_res+        return $ WriteReturns [a_w] a [([DimFix i], v) | (i, v) <- is_vs]       body = KernelBody () kstms krets-      inputs = do (p, p_a) <- zip (lambdaParams lam') ivs-                  return $ KernelInput (paramName p) (paramType p) p_a [Var write_i]+      inputs = do+        (p, p_a) <- zip (lambdaParams lam') ivs+        return $ KernelInput (paramName p) (paramType p) p_a [Var write_i]   (kernel, stms) <--    mapKernel segThreadCapped [(write_i,w)] inputs (map rowType $ patternTypes pat) body+    mapKernel segThreadCapped [(write_i, w)] inputs (map rowType $ patternTypes pat) body   certifying cs $ do     addStms stms     letBind pat $ Op $ SegOp kernel- transformStm _ (Let orig_pat (StmAux cs _ _) (Op (Hist w ops bucket_fun imgs))) = do   let bfun' = soacsLambdaToKernels bucket_fun @@ -555,13 +585,17 @@   runBinder_ $ do     lvl <- segThreadCapped [w] "seghist" $ NoRecommendation SegNoVirt     addStms =<< histKernel onLambda lvl orig_pat [] [] cs w ops bfun' imgs-  where onLambda = pure . soacsLambdaToKernels-+  where+    onLambda = pure . soacsLambdaToKernels transformStm _ bnd =   runBinder_ $ FOT.transformStmRecursively bnd -sufficientParallelism :: String -> [SubExp] -> KernelPath -> Maybe Int32-                      -> DistribM ((SubExp, Name), Out.Stms Out.Kernels)+sufficientParallelism ::+  String ->+  [SubExp] ->+  KernelPath ->+  Maybe Int64 ->+  DistribM ((SubExp, Name), Out.Stms Out.Kernels) sufficientParallelism desc ws path def =   cmpSizeLe desc (Out.SizeThreshold path def) ws @@ -570,73 +604,78 @@ -- parallelism inside a loop. worthIntraGroup :: Lambda -> Bool worthIntraGroup lam = bodyInterest (lambdaBody lam) > 1-  where bodyInterest body =-          sum $ interest <$> bodyStms body-        interest stm-          | "sequential" `inAttrs` attrs =-              0::Int-          | Op (Screma w form _) <- stmExp stm,-            Just lam' <- isMapSOAC form =-              mapLike w lam'-          | Op (Scatter w lam' _ _) <- stmExp stm =-              mapLike w lam'-          | DoLoop _ _ _ body <- stmExp stm =-              bodyInterest body * 10-          | If _ tbody fbody _ <- stmExp stm =-              max (bodyInterest tbody) (bodyInterest fbody)-          | Op (Screma w (ScremaForm _ _ lam') _) <- stmExp stm =-              zeroIfTooSmall w + bodyInterest (lambdaBody lam')-          | Op (Stream _ (Sequential _) lam' _) <- stmExp stm =-              bodyInterest $ lambdaBody lam'-          | otherwise =-              0--          where attrs = stmAuxAttrs $ stmAux stm-                sequential_inner = "sequential_inner" `inAttrs` attrs+  where+    bodyInterest body =+      sum $ interest <$> bodyStms body+    interest stm+      | "sequential" `inAttrs` attrs =+        0 :: Int+      | Op (Screma w form _) <- stmExp stm,+        Just lam' <- isMapSOAC form =+        mapLike w lam'+      | Op (Scatter w lam' _ _) <- stmExp stm =+        mapLike w lam'+      | DoLoop _ _ _ body <- stmExp stm =+        bodyInterest body * 10+      | If _ tbody fbody _ <- stmExp stm =+        max (bodyInterest tbody) (bodyInterest fbody)+      | Op (Screma w (ScremaForm _ _ lam') _) <- stmExp stm =+        zeroIfTooSmall w + bodyInterest (lambdaBody lam')+      | Op (Stream _ (Sequential _) lam' _) <- stmExp stm =+        bodyInterest $ lambdaBody lam'+      | otherwise =+        0+      where+        attrs = stmAuxAttrs $ stmAux stm+        sequential_inner = "sequential_inner" `inAttrs` attrs -                zeroIfTooSmall (Constant (IntValue x))-                  | intToInt64 x < 32 = 0-                zeroIfTooSmall _ = 1+        zeroIfTooSmall (Constant (IntValue x))+          | intToInt64 x < 32 = 0+        zeroIfTooSmall _ = 1 -                mapLike w lam' =-                  if sequential_inner-                  then 0-                  else max (zeroIfTooSmall w) (bodyInterest (lambdaBody lam'))+        mapLike w lam' =+          if sequential_inner+            then 0+            else max (zeroIfTooSmall w) (bodyInterest (lambdaBody lam'))  -- | A lambda is worth sequentialising if it contains enough nested -- parallelism of an interesting kind. worthSequentialising :: Lambda -> Bool worthSequentialising lam = bodyInterest (lambdaBody lam) > 1-  where bodyInterest body =-          sum $ interest <$> bodyStms body-        interest stm-          | "sequential" `inAttrs` attrs =-              0::Int-          | Op (Screma _ form@(ScremaForm _ _ lam') _) <- stmExp stm,-            isJust $ isMapSOAC form =-              if sequential_inner-              then 0-              else bodyInterest (lambdaBody lam')-          | Op Scatter{} <- stmExp stm =-              0 -- Basically a map.-          | DoLoop _ _ _ body <- stmExp stm =-              bodyInterest body * 10-          | Op (Screma _ form@(ScremaForm _ _ lam') _) <- stmExp stm =-              1 + bodyInterest (lambdaBody lam') +-              -- Give this a bigger score if it's a redomap, as these-              -- are often tileable and thus benefit more from-              -- sequentialisation.-              case isRedomapSOAC form of-                Just _ -> 1-                Nothing -> 0-          | otherwise =-              0--          where attrs = stmAuxAttrs $ stmAux stm-                sequential_inner = "sequential_inner" `inAttrs` attrs+  where+    bodyInterest body =+      sum $ interest <$> bodyStms body+    interest stm+      | "sequential" `inAttrs` attrs =+        0 :: Int+      | Op (Screma _ form@(ScremaForm _ _ lam') _) <- stmExp stm,+        isJust $ isMapSOAC form =+        if sequential_inner+          then 0+          else bodyInterest (lambdaBody lam')+      | Op Scatter {} <- stmExp stm =+        0 -- Basically a map.+      | DoLoop _ _ _ body <- stmExp stm =+        bodyInterest body * 10+      | Op (Screma _ form@(ScremaForm _ _ lam') _) <- stmExp stm =+        1 + bodyInterest (lambdaBody lam')+          ++          -- Give this a bigger score if it's a redomap, as these+          -- are often tileable and thus benefit more from+          -- sequentialisation.+          case isRedomapSOAC form of+            Just _ -> 1+            Nothing -> 0+      | otherwise =+        0+      where+        attrs = stmAuxAttrs $ stmAux stm+        sequential_inner = "sequential_inner" `inAttrs` attrs -onTopLevelStms :: KernelPath -> Stms SOACS-               -> DistNestT Out.Kernels DistribM KernelsStms+onTopLevelStms ::+  KernelPath ->+  Stms SOACS ->+  DistNestT Out.Kernels DistribM KernelsStms onTopLevelStms path stms =   liftInner $ transformStms path $ stmsToList stms @@ -644,30 +683,36 @@ onMap path (MapLoop pat aux w lam arrs) = do   types <- askScope   let loopnest = MapNesting pat aux w $ zip (lambdaParams lam) arrs-      env path' = DistEnv-                  { distNest = singleNesting (Nesting mempty loopnest)-                  , distScope = scopeOfPattern pat <>-                                scopeForKernels (scopeOf lam) <>-                                types-                  , distOnInnerMap = onInnerMap path'-                  , distOnTopLevelStms = onTopLevelStms path'-                  , distSegLevel = segThreadCapped-                  , distOnSOACSStms = pure . oneStm . soacsStmToKernels-                  , distOnSOACSLambda = pure . soacsLambdaToKernels-                  }+      env path' =+        DistEnv+          { distNest = singleNesting (Nesting mempty loopnest),+            distScope =+              scopeOfPattern pat+                <> scopeForKernels (scopeOf lam)+                <> types,+            distOnInnerMap = onInnerMap path',+            distOnTopLevelStms = onTopLevelStms path',+            distSegLevel = segThreadCapped,+            distOnSOACSStms = pure . oneStm . soacsStmToKernels,+            distOnSOACSLambda = pure . soacsLambdaToKernels+          }       exploitInnerParallelism path' =         runDistNestT (env path') $-        distributeMapBodyStms acc (bodyStms $ lambdaBody lam)+          distributeMapBodyStms acc (bodyStms $ lambdaBody lam)    let exploitOuterParallelism path' = do         let lam' = soacsLambdaToKernels lam-        runDistNestT (env path') $ distribute $-          addStmsToAcc (bodyStms $ lambdaBody lam') acc+        runDistNestT (env path') $+          distribute $+            addStmsToAcc (bodyStms $ lambdaBody lam') acc    onMap' (newKernel loopnest) path exploitOuterParallelism exploitInnerParallelism pat lam-  where acc = DistAcc { distTargets = singleTarget (pat, bodyResult $ lambdaBody lam)-                      , distStms = mempty-                      }+  where+    acc =+      DistAcc+        { distTargets = singleTarget (pat, bodyResult $ lambdaBody lam),+          distStms = mempty+        }  onlyExploitIntra :: Attrs -> Bool onlyExploitIntra attrs =@@ -676,27 +721,29 @@ mayExploitOuter :: Attrs -> Bool mayExploitOuter attrs =   not $-  AttrComp "incremental_flattening" ["no_outer"] `inAttrs` attrs ||-  AttrComp "incremental_flattening" ["only_inner"] `inAttrs` attrs+    AttrComp "incremental_flattening" ["no_outer"] `inAttrs` attrs+      || AttrComp "incremental_flattening" ["only_inner"] `inAttrs` attrs  mayExploitIntra :: Attrs -> Bool mayExploitIntra attrs =   not $-  AttrComp "incremental_flattening" ["no_intra"] `inAttrs` attrs ||-  AttrComp "incremental_flattening" ["only_inner"] `inAttrs` attrs+    AttrComp "incremental_flattening" ["no_intra"] `inAttrs` attrs+      || AttrComp "incremental_flattening" ["only_inner"] `inAttrs` attrs  -- The minimum amount of inner parallelism we require (by default) in -- intra-group versions.  Less than this is usually pointless on a GPU -- (but we allow tuning to change it).-intraMinInnerPar :: Int32+intraMinInnerPar :: Int64 intraMinInnerPar = 32 -- One NVIDIA warp -onMap' :: KernelNest -> KernelPath-       -> (KernelPath -> DistribM (Out.Stms Out.Kernels))-       -> (KernelPath -> DistribM (Out.Stms Out.Kernels))-       -> Pattern-       -> Lambda-       -> DistribM (Out.Stms Out.Kernels)+onMap' ::+  KernelNest ->+  KernelPath ->+  (KernelPath -> DistribM (Out.Stms Out.Kernels)) ->+  (KernelPath -> DistribM (Out.Stms Out.Kernels)) ->+  Pattern ->+  Lambda ->+  DistribM (Out.Stms Out.Kernels) onMap' loopnest path mk_seq_stms mk_par_stms pat lam = do   let nest_ws = kernelNestWidths loopnest       res = map Var $ patternNames pat@@ -707,41 +754,49 @@   ((outer_suff, outer_suff_key), outer_suff_stms) <-     sufficientParallelism "suff_outer_par" nest_ws path Nothing -  intra <- if onlyExploitIntra (stmAuxAttrs aux) ||-              (worthIntraGroup lam && mayExploitIntra attrs) then-             flip runReaderT types $ intraGroupParallelise loopnest lam-           else return Nothing-  seq_body <- renameBody =<< mkBody <$>-              mk_seq_stms ((outer_suff_key, True) : path) <*> pure res-  let seq_alts = [(outer_suff, seq_body)-                 | worthSequentialising lam, mayExploitOuter attrs]+  intra <-+    if onlyExploitIntra (stmAuxAttrs aux)+      || (worthIntraGroup lam && mayExploitIntra attrs)+      then flip runReaderT types $ intraGroupParallelise loopnest lam+      else return Nothing+  seq_body <-+    renameBody =<< mkBody+      <$> mk_seq_stms ((outer_suff_key, True) : path) <*> pure res+  let seq_alts =+        [ (outer_suff, seq_body)+          | worthSequentialising lam,+            mayExploitOuter attrs+        ]    case intra of     Nothing -> do-      par_body <- renameBody =<< mkBody <$>-                  mk_par_stms ((outer_suff_key, False) : path) <*> pure res+      par_body <-+        renameBody =<< mkBody+          <$> mk_par_stms ((outer_suff_key, False) : path) <*> pure res        if "sequential_inner" `inAttrs` attrs         then kernelAlternatives pat seq_body []-        else (outer_suff_stms<>) <$> kernelAlternatives pat par_body seq_alts-+        else (outer_suff_stms <>) <$> kernelAlternatives pat par_body seq_alts     Just ((_intra_min_par, intra_avail_par), group_size, log, intra_prelude, intra_stms) -> do       addLog log       -- We must check that all intra-group parallelism fits in a group.       ((intra_ok, intra_suff_key), intra_suff_stms) <- do-         ((intra_suff, suff_key), check_suff_stms) <--          sufficientParallelism "suff_intra_par" [intra_avail_par]-          ((outer_suff_key, False) : path) (Just intraMinInnerPar)+          sufficientParallelism+            "suff_intra_par"+            [intra_avail_par]+            ((outer_suff_key, False) : path)+            (Just intraMinInnerPar)          runBinder $ do-           addStms intra_prelude            max_group_size <-             letSubExp "max_group_size" $ Op $ SizeOp $ Out.GetSizeMax Out.SizeGroup-          fits <- letSubExp "fits" $ BasicOp $-                  CmpOp (CmpSle Int32) group_size max_group_size+          fits <-+            letSubExp "fits" $+              BasicOp $+                CmpOp (CmpSle Int64) group_size max_group_size            addStms check_suff_stms @@ -750,36 +805,45 @@        group_par_body <- renameBody $ mkBody intra_stms res -      par_body <- renameBody =<< mkBody <$>-                  mk_par_stms ([(outer_suff_key, False),-                                (intra_suff_key, False)]-                                ++ path) <*> pure res+      par_body <-+        renameBody =<< mkBody+          <$> mk_par_stms+            ( [ (outer_suff_key, False),+                (intra_suff_key, False)+              ]+                ++ path+            )+            <*> pure res        if "sequential_inner" `inAttrs` attrs         then kernelAlternatives pat seq_body []         else-        if onlyExploitIntra attrs-        then (intra_suff_stms<>) <$> kernelAlternatives pat group_par_body []-        else ((outer_suff_stms<>intra_suff_stms)<>) <$>-             kernelAlternatives pat par_body (seq_alts ++ [(intra_ok, group_par_body)])+          if onlyExploitIntra attrs+            then (intra_suff_stms <>) <$> kernelAlternatives pat group_par_body []+            else+              ((outer_suff_stms <> intra_suff_stms) <>)+                <$> kernelAlternatives pat par_body (seq_alts ++ [(intra_ok, group_par_body)]) -onInnerMap :: KernelPath -> MapLoop -> DistAcc Out.Kernels-           -> DistNestT Out.Kernels DistribM (DistAcc Out.Kernels)+onInnerMap ::+  KernelPath ->+  MapLoop ->+  DistAcc Out.Kernels ->+  DistNestT Out.Kernels DistribM (DistAcc Out.Kernels) onInnerMap path maploop@(MapLoop pat aux w lam arrs) acc-  | unbalancedLambda lam, lambdaContainsParallelism lam =-      addStmToAcc (mapLoopStm maploop) acc+  | unbalancedLambda lam,+    lambdaContainsParallelism lam =+    addStmToAcc (mapLoopStm maploop) acc   | otherwise =-      distributeSingleStm acc (mapLoopStm maploop) >>= \case+    distributeSingleStm acc (mapLoopStm maploop) >>= \case       Just (post_kernels, res, nest, acc')         | Just (perm, _pat_unused) <- permutationAndMissing pat res -> do-            addPostStms post_kernels-            multiVersion perm nest acc'+          addPostStms post_kernels+          multiVersion perm nest acc'       _ -> distributeMap maploop acc-   where     discardTargets acc' =       -- FIXME: work around bogus targets.-      acc' { distTargets = singleTarget (mempty, mempty) }+      acc' {distTargets = singleTarget (mempty, mempty)}      multiVersion perm nest acc' = do       -- The kernel can be distributed by itself, so now we can@@ -788,40 +852,46 @@       dist_env <- ask       let extra_scope = targetsScope $ distTargets acc' -      stms <- liftInner $ localScope extra_scope $ do-        let maploop' = MapLoop pat aux w lam arrs+      stms <- liftInner $+        localScope extra_scope $ do+          let maploop' = MapLoop pat aux w lam arrs -            exploitInnerParallelism path' = do-              let dist_env' =-                    dist_env { distOnTopLevelStms = onTopLevelStms path'-                             , distOnInnerMap = onInnerMap path'-                             }-              runDistNestT dist_env' $-                inNesting nest $ localScope extra_scope $-                discardTargets <$> distributeMap maploop' acc { distStms = mempty }+              exploitInnerParallelism path' = do+                let dist_env' =+                      dist_env+                        { distOnTopLevelStms = onTopLevelStms path',+                          distOnInnerMap = onInnerMap path'+                        }+                runDistNestT dist_env' $+                  inNesting nest $+                    localScope extra_scope $+                      discardTargets <$> distributeMap maploop' acc {distStms = mempty} -        -- Normally the permutation is for the output pattern, but-        -- we can't really change that, so we change the result-        -- order instead.-        let lam_res' = rearrangeShape perm $ bodyResult $ lambdaBody lam-            lam' = lam { lambdaBody = (lambdaBody lam) { bodyResult = lam_res' } }-            map_nesting = MapNesting pat aux w $ zip (lambdaParams lam) arrs-            nest' = pushInnerKernelNesting (pat, lam_res') map_nesting nest+          -- Normally the permutation is for the output pattern, but+          -- we can't really change that, so we change the result+          -- order instead.+          let lam_res' = rearrangeShape perm $ bodyResult $ lambdaBody lam+              lam' = lam {lambdaBody = (lambdaBody lam) {bodyResult = lam_res'}}+              map_nesting = MapNesting pat aux w $ zip (lambdaParams lam) arrs+              nest' = pushInnerKernelNesting (pat, lam_res') map_nesting nest -        -- XXX: we do not construct a new KernelPath when-        -- sequentialising.  This is only OK as long as further-        -- versioning does not take place down that branch (it currently-        -- does not).-        (sequentialised_kernel, nestw_bnds) <- localScope extra_scope $ do-          let sequentialised_lam = soacsLambdaToKernels lam'-          constructKernel segThreadCapped nest' $ lambdaBody sequentialised_lam+          -- XXX: we do not construct a new KernelPath when+          -- sequentialising.  This is only OK as long as further+          -- versioning does not take place down that branch (it currently+          -- does not).+          (sequentialised_kernel, nestw_bnds) <- localScope extra_scope $ do+            let sequentialised_lam = soacsLambdaToKernels lam'+            constructKernel segThreadCapped nest' $ lambdaBody sequentialised_lam -        let outer_pat = loopNestingPattern $ fst nest-        (nestw_bnds<>) <$>-          onMap' nest' path-          (const $ return $ oneStm sequentialised_kernel)-          exploitInnerParallelism-          outer_pat lam'+          let outer_pat = loopNestingPattern $ fst nest+          (nestw_bnds <>)+            <$> onMap'+              nest'+              path+              (const $ return $ oneStm sequentialised_kernel)+              exploitInnerParallelism+              outer_pat+              lam'        postStm stms       return acc'
src/Futhark/Pass/ExtractKernels/BlockedKernel.hs view
@@ -1,45 +1,44 @@+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}-module Futhark.Pass.ExtractKernels.BlockedKernel-       ( DistLore-       , MkSegLevel-       , ThreadRecommendation(..)-       , segRed-       , nonSegRed-       , segScan-       , segHist-       , segMap -       , mapKernel-       , KernelInput(..)-       , readKernelInput--       , mkSegSpace-       , dummyDim-       )-       where+module Futhark.Pass.ExtractKernels.BlockedKernel+  ( DistLore,+    MkSegLevel,+    ThreadRecommendation (..),+    segRed,+    nonSegRed,+    segScan,+    segHist,+    segMap,+    mapKernel,+    KernelInput (..),+    readKernelInput,+    mkSegSpace,+    dummyDim,+  )+where  import Control.Monad import Control.Monad.Writer import Data.List ()--import Prelude hiding (quot)- import Futhark.Analysis.PrimExp import Futhark.IR import Futhark.IR.SegOp import Futhark.MonadFreshNames import Futhark.Tools import Futhark.Transform.Rename+import Prelude hiding (quot)  -- | Constraints pertinent to performing distribution/flattening.-type DistLore lore = (Bindable lore,-                      HasSegOp lore,-                      BinderOps lore,-                      LetDec lore ~ Type,-                      ExpDec lore ~ (),-                      BodyDec lore ~ ())+type DistLore lore =+  ( Bindable lore,+    HasSegOp lore,+    BinderOps lore,+    LetDec lore ~ Type,+    ExpDec lore ~ (),+    BodyDec lore ~ ()+  )  data ThreadRecommendation = ManyThreads | NoRecommendation SegVirt @@ -49,71 +48,86 @@ mkSegSpace :: MonadFreshNames m => [(VName, SubExp)] -> m SegSpace mkSegSpace dims = SegSpace <$> newVName "phys_tid" <*> pure dims -prepareRedOrScan :: (MonadBinder m, DistLore (Lore m)) =>-                    SubExp-                 -> Lambda (Lore m)-                 -> [VName] -> [(VName, SubExp)] -> [KernelInput]-                 -> m (SegSpace, KernelBody (Lore m))+prepareRedOrScan ::+  (MonadBinder m, DistLore (Lore m)) =>+  SubExp ->+  Lambda (Lore m) ->+  [VName] ->+  [(VName, SubExp)] ->+  [KernelInput] ->+  m (SegSpace, KernelBody (Lore m)) prepareRedOrScan w map_lam arrs ispace inps = do   gtid <- newVName "gtid"   space <- mkSegSpace $ ispace ++ [(gtid, w)]-  kbody <- fmap (uncurry (flip (KernelBody ()))) $ runBinder $-           localScope (scopeOfSegSpace space) $ do-    mapM_ readKernelInput inps-    forM_ (zip (lambdaParams map_lam) arrs) $ \(p, arr) -> do-      arr_t <- lookupType arr-      letBindNames [paramName p] $-        BasicOp $ Index arr $ fullSlice arr_t [DimFix $ Var gtid]-    map (Returns ResultMaySimplify) <$> bodyBind (lambdaBody map_lam)+  kbody <- fmap (uncurry (flip (KernelBody ()))) $+    runBinder $+      localScope (scopeOfSegSpace space) $ do+        mapM_ readKernelInput inps+        forM_ (zip (lambdaParams map_lam) arrs) $ \(p, arr) -> do+          arr_t <- lookupType arr+          letBindNames [paramName p] $+            BasicOp $ Index arr $ fullSlice arr_t [DimFix $ Var gtid]+        map (Returns ResultMaySimplify) <$> bodyBind (lambdaBody map_lam)    return (space, kbody) -segRed :: (MonadFreshNames m, DistLore lore, HasScope lore m) =>-          SegOpLevel lore-       -> Pattern lore-       -> SubExp -- segment size-       -> [SegBinOp lore]-       -> Lambda lore-       -> [VName]-       -> [(VName, SubExp)] -- ispace = pair of (gtid, size) for the maps on "top" of this reduction-       -> [KernelInput]     -- inps = inputs that can be looked up by using the gtids from ispace-       -> m (Stms lore)+segRed ::+  (MonadFreshNames m, DistLore lore, HasScope lore m) =>+  SegOpLevel lore ->+  Pattern lore ->+  SubExp -> -- segment size+  [SegBinOp lore] ->+  Lambda lore ->+  [VName] ->+  [(VName, SubExp)] -> -- ispace = pair of (gtid, size) for the maps on "top" of this reduction+  [KernelInput] -> -- inps = inputs that can be looked up by using the gtids from ispace+  m (Stms lore) segRed lvl pat w ops map_lam arrs ispace inps = runBinder_ $ do   (kspace, kbody) <- prepareRedOrScan w map_lam arrs ispace inps-  letBind pat $ Op $ segOp $-    SegRed lvl kspace ops (lambdaReturnType map_lam) kbody+  letBind pat $+    Op $+      segOp $+        SegRed lvl kspace ops (lambdaReturnType map_lam) kbody -segScan :: (MonadFreshNames m, DistLore lore, HasScope lore m) =>-           SegOpLevel lore-        -> Pattern lore-        -> SubExp -- segment size-        -> [SegBinOp lore] -> Lambda lore-        -> [VName]-        -> [(VName, SubExp)] -- ispace = pair of (gtid, size) for the maps on "top" of this scan-        -> [KernelInput]     -- inps = inputs that can be looked up by using the gtids from ispace-        -> m (Stms lore)+segScan ::+  (MonadFreshNames m, DistLore lore, HasScope lore m) =>+  SegOpLevel lore ->+  Pattern lore ->+  SubExp -> -- segment size+  [SegBinOp lore] ->+  Lambda lore ->+  [VName] ->+  [(VName, SubExp)] -> -- ispace = pair of (gtid, size) for the maps on "top" of this scan+  [KernelInput] -> -- inps = inputs that can be looked up by using the gtids from ispace+  m (Stms lore) segScan lvl pat w ops map_lam arrs ispace inps = runBinder_ $ do   (kspace, kbody) <- prepareRedOrScan w map_lam arrs ispace inps-  letBind pat $ Op $ segOp $-    SegScan lvl kspace ops (lambdaReturnType map_lam) kbody+  letBind pat $+    Op $+      segOp $+        SegScan lvl kspace ops (lambdaReturnType map_lam) kbody -segMap :: (MonadFreshNames m, DistLore lore, HasScope lore m) =>-          SegOpLevel lore-       -> Pattern lore-       -> SubExp -- segment size-       -> Lambda lore-       -> [VName]-       -> [(VName, SubExp)] -- ispace = pair of (gtid, size) for the maps on "top" of this map-       -> [KernelInput]     -- inps = inputs that can be looked up by using the gtids from ispace-       -> m (Stms lore)+segMap ::+  (MonadFreshNames m, DistLore lore, HasScope lore m) =>+  SegOpLevel lore ->+  Pattern lore ->+  SubExp -> -- segment size+  Lambda lore ->+  [VName] ->+  [(VName, SubExp)] -> -- ispace = pair of (gtid, size) for the maps on "top" of this map+  [KernelInput] -> -- inps = inputs that can be looked up by using the gtids from ispace+  m (Stms lore) segMap lvl pat w map_lam arrs ispace inps = runBinder_ $ do   (kspace, kbody) <- prepareRedOrScan w map_lam arrs ispace inps-  letBind pat $ Op $ segOp $-    SegMap lvl kspace (lambdaReturnType map_lam) kbody+  letBind pat $+    Op $+      segOp $+        SegMap lvl kspace (lambdaReturnType map_lam) kbody -dummyDim :: (MonadFreshNames m, MonadBinder m, DistLore (Lore m)) =>-            Pattern (Lore m)-         -> m (Pattern (Lore m), [(VName, SubExp)], m ())+dummyDim ::+  (MonadFreshNames m, MonadBinder m, DistLore (Lore m)) =>+  Pattern (Lore m) ->+  m (Pattern (Lore m), [(VName, SubExp)], m ()) dummyDim pat = do   -- We add a unit-size segment on top to ensure that the result   -- of the SegRed is an array, which we then immediately index.@@ -121,68 +135,83 @@   -- device afterwards, as this may save an expensive   -- host-device copy (scalars are kept on the host, but arrays   -- may be on the device).-  let addDummyDim t = t `arrayOfRow` intConst Int32 1+  let addDummyDim t = t `arrayOfRow` intConst Int64 1   pat' <- fmap addDummyDim <$> renamePattern pat   dummy <- newVName "dummy"-  let ispace = [(dummy, intConst Int32 1)]+  let ispace = [(dummy, intConst Int64 1)] -  return (pat', ispace,-          forM_ (zip (patternNames pat') (patternNames pat)) $ \(from, to) -> do-             from_t <- lookupType from-             letBindNames [to] $ BasicOp $ Index from $-               fullSlice from_t [DimFix $ intConst Int32 0])+  return+    ( pat',+      ispace,+      forM_ (zip (patternNames pat') (patternNames pat)) $ \(from, to) -> do+        from_t <- lookupType from+        letBindNames [to] $+          BasicOp $+            Index from $+              fullSlice from_t [DimFix $ intConst Int64 0]+    ) -nonSegRed :: (MonadFreshNames m, DistLore lore, HasScope lore m) =>-             SegOpLevel lore-          -> Pattern lore-          -> SubExp-          -> [SegBinOp lore]-          -> Lambda lore-          -> [VName]-          -> m (Stms lore)+nonSegRed ::+  (MonadFreshNames m, DistLore lore, HasScope lore m) =>+  SegOpLevel lore ->+  Pattern lore ->+  SubExp ->+  [SegBinOp lore] ->+  Lambda lore ->+  [VName] ->+  m (Stms lore) nonSegRed lvl pat w ops map_lam arrs = runBinder_ $ do   (pat', ispace, read_dummy) <- dummyDim pat   addStms =<< segRed lvl pat' w ops map_lam arrs ispace []   read_dummy -segHist :: (DistLore lore, MonadFreshNames m, HasScope lore m) =>-           SegOpLevel lore-        -> Pattern lore-        -> SubExp-        -> [(VName,SubExp)] -- ^ Segment indexes and sizes.-        -> [KernelInput]-        -> [HistOp lore]-        -> Lambda lore -> [VName]-        -> m (Stms lore)+segHist ::+  (DistLore lore, MonadFreshNames m, HasScope lore m) =>+  SegOpLevel lore ->+  Pattern lore ->+  SubExp ->+  -- | Segment indexes and sizes.+  [(VName, SubExp)] ->+  [KernelInput] ->+  [HistOp lore] ->+  Lambda lore ->+  [VName] ->+  m (Stms lore) segHist lvl pat arr_w ispace inps ops lam arrs = runBinder_ $ do   gtid <- newVName "gtid"   space <- mkSegSpace $ ispace ++ [(gtid, arr_w)] -  kbody <- fmap (uncurry (flip $ KernelBody ())) $ runBinder $-           localScope (scopeOfSegSpace space) $ do-    mapM_ readKernelInput inps-    forM_ (zip (lambdaParams lam) arrs) $ \(p, arr) -> do-      arr_t <- lookupType arr-      letBindNames [paramName p] $-        BasicOp $ Index arr $ fullSlice arr_t [DimFix $ Var gtid]-    map (Returns ResultMaySimplify) <$> bodyBind (lambdaBody lam)+  kbody <- fmap (uncurry (flip $ KernelBody ())) $+    runBinder $+      localScope (scopeOfSegSpace space) $ do+        mapM_ readKernelInput inps+        forM_ (zip (lambdaParams lam) arrs) $ \(p, arr) -> do+          arr_t <- lookupType arr+          letBindNames [paramName p] $+            BasicOp $ Index arr $ fullSlice arr_t [DimFix $ Var gtid]+        map (Returns ResultMaySimplify) <$> bodyBind (lambdaBody lam)    letBind pat $ Op $ segOp $ SegHist lvl space ops (lambdaReturnType lam) kbody -mapKernelSkeleton :: (DistLore lore, HasScope lore m, MonadFreshNames m) =>-                     [(VName, SubExp)] -> [KernelInput]-                  -> m (SegSpace, Stms lore)+mapKernelSkeleton ::+  (DistLore lore, HasScope lore m, MonadFreshNames m) =>+  [(VName, SubExp)] ->+  [KernelInput] ->+  m (SegSpace, Stms lore) mapKernelSkeleton ispace inputs = do   read_input_bnds <- runBinder_ $ mapM readKernelInput inputs    space <- mkSegSpace ispace   return (space, read_input_bnds) -mapKernel :: (DistLore lore, HasScope lore m, MonadFreshNames m) =>-             MkSegLevel lore m-          -> [(VName, SubExp)] -> [KernelInput]-          -> [Type] -> KernelBody lore-          -> m (SegOp (SegOpLevel lore) lore, Stms lore)+mapKernel ::+  (DistLore lore, HasScope lore m, MonadFreshNames m) =>+  MkSegLevel lore m ->+  [(VName, SubExp)] ->+  [KernelInput] ->+  [Type] ->+  KernelBody lore ->+  m (SegOp (SegOpLevel lore) lore, Stms lore) mapKernel mk_lvl ispace inputs rts (KernelBody () kstms krets) = runBinderT' $ do   (space, read_input_stms) <- mapKernelSkeleton ispace inputs @@ -199,18 +228,22 @@    return $ SegMap lvl space rts kbody' -data KernelInput = KernelInput { kernelInputName :: VName-                               , kernelInputType :: Type-                               , kernelInputArray :: VName-                               , kernelInputIndices :: [SubExp]-                               }-                 deriving (Show)+data KernelInput = KernelInput+  { kernelInputName :: VName,+    kernelInputType :: Type,+    kernelInputArray :: VName,+    kernelInputIndices :: [SubExp]+  }+  deriving (Show) -readKernelInput :: (DistLore (Lore m), MonadBinder m) =>-                   KernelInput -> m ()+readKernelInput ::+  (DistLore (Lore m), MonadBinder m) =>+  KernelInput ->+  m () readKernelInput inp = do   let pe = PatElem (kernelInputName inp) $ kernelInputType inp   arr_t <- lookupType $ kernelInputArray inp   letBind (Pattern [] [pe]) $-    BasicOp $ Index (kernelInputArray inp) $-    fullSlice arr_t $ map DimFix $ kernelInputIndices inp+    BasicOp $+      Index (kernelInputArray inp) $+        fullSlice arr_t $ map DimFix $ kernelInputIndices inp
src/Futhark/Pass/ExtractKernels/DistributeNests.hs view
@@ -1,1065 +1,1202 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE OverloadedStrings #-}-module Futhark.Pass.ExtractKernels.DistributeNests-  ( MapLoop(..)-  , mapLoopStm--  , bodyContainsParallelism-  , lambdaContainsParallelism-  , determineReduceOp-  , histKernel--  , DistEnv (..)-  , DistAcc (..)-  , runDistNestT-  , DistNestT-  , liftInner--  , distributeMap--  , distribute-  , distributeSingleStm-  , distributeMapBodyStms-  , addStmsToAcc-  , addStmToAcc-  , permutationAndMissing-  , addPostStms-  , postStm-  , inNesting-  )-where--import Control.Arrow (first)-import Control.Monad.Identity-import Control.Monad.RWS.Strict-import Control.Monad.Reader-import Control.Monad.Writer.Strict-import Control.Monad.Trans.Maybe-import Data.Maybe-import Data.List (find, partition, tails)-import qualified Data.Map as M--import Futhark.IR-import qualified Futhark.IR.SOACS as SOACS-import Futhark.IR.SOACS.SOAC hiding (HistOp, histDest)-import Futhark.IR.SOACS (SOACS)-import Futhark.IR.SOACS.Simplify (simpleSOACS, simplifyStms)-import Futhark.IR.SegOp-import Futhark.MonadFreshNames-import Futhark.Tools-import Futhark.Transform.Rename-import Futhark.Transform.CopyPropagate-import qualified Futhark.Transform.FirstOrderTransform as FOT-import Futhark.Pass.ExtractKernels.Distribution-import Futhark.Pass.ExtractKernels.ISRWIM-import Futhark.Pass.ExtractKernels.BlockedKernel-import Futhark.Pass.ExtractKernels.Interchange-import Futhark.Util-import Futhark.Util.Log--scopeForSOACs :: SameScope lore SOACS => Scope lore -> Scope SOACS-scopeForSOACs = castScope--data MapLoop = MapLoop SOACS.Pattern (StmAux ()) SubExp SOACS.Lambda [VName]--mapLoopStm :: MapLoop -> Stm SOACS-mapLoopStm (MapLoop pat aux w lam arrs) =-  Let pat aux $ Op $ Screma w (mapSOAC lam) arrs--data DistEnv lore m =-  DistEnv { distNest :: Nestings-          , distScope :: Scope lore-          , distOnTopLevelStms :: Stms SOACS -> DistNestT lore m (Stms lore)-          , distOnInnerMap :: MapLoop -> DistAcc lore-                           -> DistNestT lore m (DistAcc lore)-          , distOnSOACSStms :: Stm SOACS -> Binder lore (Stms lore)-          , distOnSOACSLambda :: Lambda SOACS -> Binder lore (Lambda lore)-          , distSegLevel :: MkSegLevel lore m-          }--data DistAcc lore =-  DistAcc { distTargets :: Targets-          , distStms :: Stms lore-          }--data DistRes lore =-  DistRes { accPostStms :: PostStms lore-          , accLog :: Log-          }--instance Semigroup (DistRes lore) where-  DistRes ks1 log1 <> DistRes ks2 log2 =-    DistRes (ks1 <> ks2) (log1 <> log2)--instance Monoid (DistRes lore) where-  mempty = DistRes mempty mempty--newtype PostStms lore = PostStms { unPostStms :: Stms lore }--instance Semigroup (PostStms lore) where-  PostStms xs <> PostStms ys = PostStms $ ys <> xs--instance Monoid (PostStms lore) where-  mempty = PostStms mempty--typeEnvFromDistAcc :: DistLore lore => DistAcc lore -> Scope lore-typeEnvFromDistAcc = scopeOfPattern . fst . outerTarget . distTargets--addStmsToAcc :: Stms lore -> DistAcc lore -> DistAcc lore-addStmsToAcc stms acc =-  acc { distStms = stms <> distStms acc }--addStmToAcc :: (MonadFreshNames m, DistLore lore) =>-               Stm SOACS -> DistAcc lore-            -> DistNestT lore m (DistAcc lore)-addStmToAcc stm acc = do-  onSoacs <- asks distOnSOACSStms-  (stm', _) <- runBinder $ onSoacs stm-  return acc { distStms = stm' <> distStms acc }--soacsLambda :: (MonadFreshNames m, DistLore lore) =>-               Lambda SOACS -> DistNestT lore m (Lambda lore)-soacsLambda lam = do-  onLambda <- asks distOnSOACSLambda-  fst <$> runBinder (onLambda lam)--newtype DistNestT lore m a =-  DistNestT (ReaderT (DistEnv lore m) (WriterT (DistRes lore) m) a)-  deriving (Functor, Applicative, Monad,-            MonadReader (DistEnv lore m),-            MonadWriter (DistRes lore))--liftInner :: (LocalScope lore m, DistLore lore) => m a -> DistNestT lore m a-liftInner m = do-  outer_scope <- askScope-  DistNestT $ lift $ lift $ do-    inner_scope <- askScope-    localScope (outer_scope `M.difference` inner_scope) m--instance MonadFreshNames m => MonadFreshNames (DistNestT lore m) where-  getNameSource = DistNestT $ lift getNameSource-  putNameSource = DistNestT . lift . putNameSource--instance (Monad m, ASTLore lore) => HasScope lore (DistNestT lore m) where-  askScope = asks distScope--instance (Monad m, ASTLore lore) => LocalScope lore (DistNestT lore m) where-  localScope types = local $ \env ->-    env { distScope = types <> distScope env }--instance Monad m => MonadLogger (DistNestT lore m) where-  addLog msgs = tell mempty { accLog = msgs }--runDistNestT :: (MonadLogger m, DistLore lore) =>-                DistEnv lore m -> DistNestT lore m (DistAcc lore) -> m (Stms lore)-runDistNestT env (DistNestT m) = do-  (acc, res) <- runWriterT $ runReaderT m env-  addLog $ accLog res-  -- There may be a few final targets remaining - these correspond to-  -- arrays that are identity mapped, and must have statements-  -- inserted here.-  return $-    unPostStms (accPostStms res) <>-    identityStms (outerTarget $ distTargets acc)-  where outermost = nestingLoop $-                    case distNest env of (nest, []) -> nest-                                         (_, nest : _) -> nest-        params_to_arrs = map (first paramName) $-                         loopNestingParamsAndArrs outermost--        identityStms (rem_pat, res) =-          stmsFromList $ zipWith identityStm (patternValueElements rem_pat) res-        identityStm pe (Var v)-          | Just arr <- lookup v params_to_arrs =-              Let (Pattern [] [pe]) (defAux ()) $ BasicOp $ Copy arr-        identityStm pe se =-          Let (Pattern [] [pe]) (defAux ()) $ BasicOp $-          Replicate (Shape [loopNestingWidth outermost]) se--addPostStms :: Monad m => PostStms lore -> DistNestT lore m ()-addPostStms ks = tell $ mempty { accPostStms = ks }--postStm :: Monad m => Stms lore -> DistNestT lore m ()-postStm stms = addPostStms $ PostStms stms--withStm :: (Monad m, DistLore lore) =>-           Stm SOACS -> DistNestT lore m a -> DistNestT lore m a-withStm stm = local $ \env ->-  env { distScope =-          castScope (scopeOf stm) <> distScope env-      , distNest =-          letBindInInnerNesting provided $-          distNest env-      }-  where provided = namesFromList $ patternNames $ stmPattern stm--leavingNesting :: (MonadFreshNames m, DistLore lore) =>-                  DistAcc lore -> DistNestT lore m (DistAcc lore)--leavingNesting acc =-  case popInnerTarget $ distTargets acc of-   Nothing ->-     error "The kernel targets list is unexpectedly small"--   Just ((pat, res), newtargets)-     | not $ null $ distStms acc -> do-         -- Any statements left over correspond to something that-         -- could not be distributed because it would cause irregular-         -- arrays.  These must be reconstructed into a a Map SOAC-         -- that will be sequentialised. XXX: life would be better if-         -- we were able to distribute irregular parallelism.-         (Nesting _ inner, _) <- asks distNest-         let MapNesting _ aux w params_and_arrs = inner-             body = Body () (distStms acc) res-             used_in_body = freeIn body-             (used_params, used_arrs) =-               unzip $-               filter ((`nameIn` used_in_body) . paramName . fst) params_and_arrs-             lam' = Lambda { lambdaParams = used_params-                           , lambdaBody = body-                           , lambdaReturnType = map rowType $ patternTypes pat-                           }-         stms <- runBinder_ $ auxing aux $ FOT.transformSOAC pat $-                 Screma w (mapSOAC lam') used_arrs--         return $ acc { distTargets = newtargets, distStms = stms }--     | otherwise -> do-         -- Any results left over correspond to a Replicate or a Copy in-         -- the parent nesting, depending on whether the argument is a-         -- parameter of the innermost nesting.-         (Nesting _ inner_nesting, _) <- asks distNest-         let w = loopNestingWidth inner_nesting-             aux = loopNestingAux inner_nesting-             inps = loopNestingParamsAndArrs inner_nesting--             remnantStm pe (Var v)-               | Just (_, arr) <- find ((==v) . paramName . fst) inps =-                   Let (Pattern [] [pe]) aux $-                   BasicOp $ Copy arr-             remnantStm pe se =-               Let (Pattern [] [pe]) aux $-               BasicOp $ Replicate (Shape [w]) se--             stms =-               stmsFromList $ zipWith remnantStm (patternElements pat) res--         return $ acc { distTargets = newtargets, distStms = stms }--mapNesting :: (MonadFreshNames m, DistLore lore) =>-              PatternT Type -> StmAux () -> SubExp -> Lambda SOACS -> [VName]-           -> DistNestT lore m (DistAcc lore)-           -> DistNestT lore m (DistAcc lore)-mapNesting pat aux w lam arrs m =-  local extend $ leavingNesting =<< m-  where nest = Nesting mempty $-               MapNesting pat aux w $-               zip (lambdaParams lam) arrs-        extend env = env { distNest = pushInnerNesting nest $ distNest env-                         , distScope =  castScope (scopeOf lam) <> distScope env-                         }--inNesting :: (Monad m, DistLore lore) =>-             KernelNest -> DistNestT lore m a -> DistNestT lore m a-inNesting (outer, nests) = local $ \env ->-  env { distNest = (inner, nests')-      , distScope =  foldMap scopeOfLoopNesting (outer : nests) <> distScope env-      }-  where (inner, nests') =-          case reverse nests of-            []            -> (asNesting outer, [])-            (inner' : ns) -> (asNesting inner', map asNesting $ outer : reverse ns)-        asNesting = Nesting mempty--bodyContainsParallelism :: Body SOACS -> Bool-bodyContainsParallelism = any isParallelStm . bodyStms-  where isParallelStm stm =-          isMap (stmExp stm) &&-          not ("sequential" `inAttrs` stmAuxAttrs (stmAux stm))-        isMap Op{} = True-        isMap _ = False--lambdaContainsParallelism :: Lambda SOACS -> Bool-lambdaContainsParallelism = bodyContainsParallelism . lambdaBody--distributeMapBodyStms :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-                         DistAcc lore -> Stms SOACS -> DistNestT lore m (DistAcc lore)-distributeMapBodyStms orig_acc = distribute <=< onStms orig_acc . stmsToList-  where-    onStms acc [] = return acc--    onStms acc (Let pat (StmAux cs _ _) (Op (Stream w (Sequential accs) lam arrs)):stms) = do-      types <- asksScope scopeForSOACs-      stream_stms <--        snd <$> runBinderT (sequentialStreamWholeArray pat w accs lam arrs) types-      (_, stream_stms') <--        runReaderT (copyPropagateInStms simpleSOACS types stream_stms) types-      onStms acc $ stmsToList (fmap (certify cs) stream_stms') ++ stms--    onStms acc (stm:stms) =-      -- It is important that stm is in scope if 'maybeDistributeStm'-      -- wants to distribute, even if this causes the slightly silly-      -- situation that stm is in scope of itself.-      withStm stm $ maybeDistributeStm stm =<< onStms acc stms--onInnerMap :: Monad m => MapLoop -> DistAcc lore -> DistNestT lore m (DistAcc lore)-onInnerMap loop acc = do-  f <- asks distOnInnerMap-  f loop acc--onTopLevelStms :: Monad m => Stms SOACS -> DistNestT lore m ()-onTopLevelStms stms = do-  f <- asks distOnTopLevelStms-  postStm =<< f stms--maybeDistributeStm :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-                      Stm SOACS -> DistAcc lore-                   -> DistNestT lore m (DistAcc lore)--maybeDistributeStm stm acc-  | "sequential" `inAttrs` stmAuxAttrs (stmAux stm) =-      addStmToAcc stm acc--maybeDistributeStm (Let pat aux (Op soac)) acc-  | "sequential_outer" `inAttrs` stmAuxAttrs aux =-      distributeMapBodyStms acc . fmap (certify (stmAuxCerts aux)) =<<-      runBinder_ (FOT.transformSOAC pat soac)--maybeDistributeStm stm@(Let pat _ (Op (Screma w form arrs))) acc-  | Just lam <- isMapSOAC form =-  -- Only distribute inside the map if we can distribute everything-  -- following the map.-  distributeIfPossible acc >>= \case-    Nothing -> addStmToAcc stm acc-    Just acc' -> distribute =<< onInnerMap (MapLoop pat (stmAux stm) w lam arrs) acc'--maybeDistributeStm bnd@(Let pat _ (DoLoop [] val form@ForLoop{} body)) acc-  | null (patternContextElements pat), bodyContainsParallelism body =-  distributeSingleStm acc bnd >>= \case-    Just (kernels, res, nest, acc')-      | not $ freeIn form `namesIntersect` boundInKernelNest nest,-        Just (perm, pat_unused) <- permutationAndMissing pat res ->-          -- We need to pretend pat_unused was used anyway, by adding-          -- it to the kernel nest.-          localScope (typeEnvFromDistAcc acc') $ do-          addPostStms kernels-          nest' <- expandKernelNest pat_unused nest-          types <- asksScope scopeForSOACs--          -- Simplification is key to hoisting out statements that-          -- were variant to the loop, but invariant to the outer maps-          -- (which are now innermost).-          stms <--            (`runReaderT` types) $-            fmap snd . simplifyStms =<<-            interchangeLoops nest' (SeqLoop perm pat val form body)-          onTopLevelStms stms-          return acc'-    _ ->-      addStmToAcc bnd acc--maybeDistributeStm stm@(Let pat _ (If cond tbranch fbranch ret)) acc-  | null (patternContextElements pat),-    bodyContainsParallelism tbranch || bodyContainsParallelism fbranch ||-    not (all primType (ifReturns ret)) =-    distributeSingleStm acc stm >>= \case-      Just (kernels, res, nest, acc')-        | not $-          (freeIn cond <> freeIn ret) `namesIntersect` boundInKernelNest nest,-          Just (perm, pat_unused) <- permutationAndMissing pat res ->-            -- We need to pretend pat_unused was used anyway, by adding-            -- it to the kernel nest.-            localScope (typeEnvFromDistAcc acc') $ do-            nest' <- expandKernelNest pat_unused nest-            addPostStms kernels-            types <- asksScope scopeForSOACs-            let branch = Branch perm pat cond tbranch fbranch ret-            stms <--              (`runReaderT` types) $-              fmap snd . simplifyStms =<<-              interchangeBranch nest' branch-            onTopLevelStms stms-            return acc'-      _ ->-        addStmToAcc stm acc--maybeDistributeStm (Let pat aux (Op (Screma w form arrs))) acc-  | Just [Reduce comm lam nes] <- isReduceSOAC form,-    Just m <- irwim pat w comm lam $ zip nes arrs = do-      types <- asksScope scopeForSOACs-      (_, bnds) <- runBinderT (auxing aux m) types-      distributeMapBodyStms acc bnds---- Parallelise segmented scatters.-maybeDistributeStm bnd@(Let pat (StmAux cs _ _) (Op (Scatter w lam ivs as))) acc =-  distributeSingleStm acc bnd >>= \case-    Just (kernels, res, nest, acc')-      | Just (perm, pat_unused) <- permutationAndMissing pat res ->-        localScope (typeEnvFromDistAcc acc') $ do-          nest' <- expandKernelNest pat_unused nest-          lam' <- soacsLambda lam-          addPostStms kernels-          postStm =<< segmentedScatterKernel nest' perm pat cs w lam' ivs as-          return acc'-    _ ->-      addStmToAcc bnd acc---- Parallelise segmented Hist.-maybeDistributeStm bnd@(Let pat (StmAux cs _ _) (Op (Hist w ops lam as))) acc =-  distributeSingleStm acc bnd >>= \case-    Just (kernels, res, nest, acc')-      | Just (perm, pat_unused) <- permutationAndMissing pat res ->-        localScope (typeEnvFromDistAcc acc') $ do-          lam' <- soacsLambda lam-          nest' <- expandKernelNest pat_unused nest-          addPostStms kernels-          postStm =<< segmentedHistKernel nest' perm cs w ops lam' as-          return acc'-    _ ->-      addStmToAcc bnd acc---- Parallelise Index slices if the result is going to be returned--- directly from the kernel.  This is because we would otherwise have--- to sequentialise writing the result, which may be costly.-maybeDistributeStm stm@(Let (Pattern [] [pe])-                        aux-                        (BasicOp (Index arr slice))) acc-  | not $ null $ sliceDims slice,-    Var (patElemName pe) `elem`  snd (innerTarget (distTargets acc)) =-      distributeSingleStm acc stm >>= \case-      Just (kernels, _res, nest, acc') ->-        localScope (typeEnvFromDistAcc acc') $ do-        addPostStms kernels-        postStm =<< segmentedGatherKernel nest (stmAuxCerts aux) arr slice-        return acc'-      _ ->-        addStmToAcc stm acc---- If the scan can be distributed by itself, we will turn it into a--- segmented scan.------ If the scan cannot be distributed by itself, it will be--- sequentialised in the default case for this function.-maybeDistributeStm bnd@(Let pat (StmAux cs _ _) (Op (Screma w form arrs))) acc-  | Just (scans, map_lam) <- isScanomapSOAC form,-    Scan lam nes <- singleScan scans =-  distributeSingleStm acc bnd >>= \case-    Just (kernels, res, nest, acc')-      | Just (perm, pat_unused) <- permutationAndMissing pat res ->-          -- We need to pretend pat_unused was used anyway, by adding-          -- it to the kernel nest.-          localScope (typeEnvFromDistAcc acc') $ do-          nest' <- expandKernelNest pat_unused nest-          map_lam' <- soacsLambda map_lam-          lam' <- soacsLambda lam-          localScope (typeEnvFromDistAcc acc') $-            segmentedScanomapKernel nest' perm w lam' map_lam' nes arrs >>=-            kernelOrNot cs bnd acc kernels acc'-    _ ->-      addStmToAcc bnd acc---- if the reduction can be distributed by itself, we will turn it into a--- segmented reduce.------ If the reduction cannot be distributed by itself, it will be--- sequentialised in the default case for this function.-maybeDistributeStm bnd@(Let pat (StmAux cs _ _) (Op (Screma w form arrs))) acc-  | Just (reds, map_lam) <- isRedomapSOAC form,-    Reduce comm lam nes <- singleReduce reds =-  distributeSingleStm acc bnd >>= \case-    Just (kernels, res, nest, acc')-      | Just (perm, pat_unused) <- permutationAndMissing pat res ->-          -- We need to pretend pat_unused was used anyway, by adding-          -- it to the kernel nest.-          localScope (typeEnvFromDistAcc acc') $ do-          nest' <- expandKernelNest pat_unused nest--          lam' <- soacsLambda lam-          map_lam' <- soacsLambda map_lam--          let comm' | commutativeLambda lam = Commutative-                    | otherwise             = comm--          regularSegmentedRedomapKernel nest' perm w comm' lam' map_lam' nes arrs >>=-            kernelOrNot cs bnd acc kernels acc'-    _ ->-      addStmToAcc bnd acc--maybeDistributeStm (Let pat (StmAux cs _ _) (Op (Screma w form arrs))) acc = do-  -- This Screma is too complicated for us to immediately do-  -- anything, so split it up and try again.-  scope <- asksScope scopeForSOACs-  distributeMapBodyStms acc . fmap (certify cs) . snd =<<-    runBinderT (dissectScrema pat w form arrs) scope--maybeDistributeStm (Let pat aux (BasicOp (Replicate (Shape (d:ds)) v))) acc-  | [t] <- patternTypes pat = do-      tmp <- newVName "tmp"-      let rowt = rowType t-          newbnd = Let pat aux $ Op $ Screma d (mapSOAC lam) []-          tmpbnd = Let (Pattern [] [PatElem tmp rowt]) aux $-                   BasicOp $ Replicate (Shape ds) v-          lam = Lambda { lambdaReturnType = [rowt]-                       , lambdaParams = []-                       , lambdaBody = mkBody (oneStm tmpbnd) [Var tmp]-                       }-      maybeDistributeStm newbnd acc--maybeDistributeStm bnd@(Let _ aux (BasicOp Copy{})) acc =-  distributeSingleUnaryStm acc bnd $ \_ outerpat arr ->-  return $ oneStm $ Let outerpat aux $ BasicOp $ Copy arr---- Opaques are applied to the full array, because otherwise they can--- drastically inhibit parallelisation in some cases.-maybeDistributeStm bnd@(Let (Pattern [] [pe]) aux (BasicOp Opaque{})) acc-  | not $ primType $ typeOf pe =-      distributeSingleUnaryStm acc bnd $ \_ outerpat arr ->-      return $ oneStm $ Let outerpat aux $ BasicOp $ Copy arr--maybeDistributeStm bnd@(Let _ aux (BasicOp (Rearrange perm _))) acc =-  distributeSingleUnaryStm acc bnd $ \nest outerpat arr -> do-    let r = length (snd nest) + 1-        perm' = [0..r-1] ++ map (+r) perm-    -- We need to add a copy, because the original map nest-    -- will have produced an array without aliases, and so must we.-    arr' <- newVName $ baseString arr-    arr_t <- lookupType arr-    return $ stmsFromList-      [Let (Pattern [] [PatElem arr' arr_t]) aux $ BasicOp $ Copy arr,-       Let outerpat aux $ BasicOp $ Rearrange perm' arr']--maybeDistributeStm bnd@(Let _ aux (BasicOp (Reshape reshape _))) acc =-  distributeSingleUnaryStm acc bnd $ \nest outerpat arr -> do-    let reshape' = map DimNew (kernelNestWidths nest) ++-                   map DimNew (newDims reshape)-    return $ oneStm $ Let outerpat aux $ BasicOp $ Reshape reshape' arr--maybeDistributeStm stm@(Let _ aux (BasicOp (Rotate rots _))) acc =-  distributeSingleUnaryStm acc stm $ \nest outerpat arr -> do-    let rots' = map (const $ intConst Int32 0) (kernelNestWidths nest) ++ rots-    return $ oneStm $ Let outerpat aux $ BasicOp $ Rotate rots' arr--maybeDistributeStm stm@(Let pat aux (BasicOp (Update arr slice (Var v)))) acc-  | not $ null $ sliceDims slice =-    distributeSingleStm acc stm >>= \case-    Just (kernels, res, nest, acc')-      | res == map Var (patternNames $ stmPattern stm),-        Just (perm, pat_unused) <- permutationAndMissing pat res -> do-          addPostStms kernels-          localScope (typeEnvFromDistAcc acc') $ do-            nest' <- expandKernelNest pat_unused nest-            postStm =<<-              segmentedUpdateKernel nest' perm (stmAuxCerts aux) arr slice v-            return acc'--    _ -> addStmToAcc stm acc---- XXX?  This rule is present to avoid the case where an in-place--- update is distributed as its own kernel, as this would mean thread--- then writes the entire array that it updated.  This is problematic--- because the in-place updates is O(1), but writing the array is--- O(n).  It is OK if the in-place update is preceded, followed, or--- nested inside a sequential loop or similar, because that will--- probably be O(n) by itself.  As a hack, we only distribute if there--- does not appear to be a loop following.  The better solution is to--- depend on memory block merging for this optimisation, but it is not--- ready yet.-maybeDistributeStm (Let pat aux (BasicOp (Update arr [DimFix i] v))) acc-  | [t] <- patternTypes pat,-    arrayRank t == 1,-    not $ any (amortises . stmExp) $ distStms acc = do-      let w = arraySize 0 t-          et = stripArray 1 t-          lam = Lambda { lambdaParams = []-                       , lambdaReturnType = [Prim int32, et]-                       , lambdaBody = mkBody mempty [i, v] }-      maybeDistributeStm (Let pat aux $ Op $ Scatter (intConst Int32 1) lam [] [(w, 1, arr)]) acc-  where amortises DoLoop{} = True-        amortises Op{} = True-        amortises _ = False--maybeDistributeStm stm@(Let _ aux (BasicOp (Concat d x xs w))) acc =-  distributeSingleStm acc stm >>= \case-    Just (kernels, _, nest, acc') ->-      localScope (typeEnvFromDistAcc acc') $-      segmentedConcat nest >>=-      kernelOrNot (stmAuxCerts aux) stm acc kernels acc'-    _ ->-      addStmToAcc stm acc--  where segmentedConcat nest =-          isSegmentedOp nest [0] mempty mempty [] (x:xs) $-          \pat _ _ _ (x':xs') ->-            let d' = d + length (snd nest) + 1-            in addStm $ Let pat aux $ BasicOp $ Concat d' x' xs' w--maybeDistributeStm bnd acc =-  addStmToAcc bnd acc--distributeSingleUnaryStm :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-                            DistAcc lore -> Stm SOACS-                         -> (KernelNest -> PatternT Type -> VName -> DistNestT lore m (Stms lore))-                         -> DistNestT lore m (DistAcc lore)-distributeSingleUnaryStm acc bnd f =-  distributeSingleStm acc bnd >>= \case-    Just (kernels, res, nest, acc')-      | res == map Var (patternNames $ stmPattern bnd),-        (outer, _) <- nest,-        [(arr_p, arr)] <- loopNestingParamsAndArrs outer,-        boundInKernelNest nest `namesIntersection` freeIn bnd-        == oneName (paramName arr_p),-        perfectlyMapped arr nest -> do-          addPostStms kernels-          let outerpat = loopNestingPattern $ fst nest-          localScope (typeEnvFromDistAcc acc') $ do-            postStm =<< f nest outerpat arr-            return acc'-    _ -> addStmToAcc bnd acc--  where perfectlyMapped arr (outer, nest)-          | [(p, arr')] <- loopNestingParamsAndArrs outer,-            arr == arr' =-              case nest of [] -> True-                           x:xs -> perfectlyMapped (paramName p) (x, xs)-          | otherwise =-              False--distribute :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-              DistAcc lore -> DistNestT lore m (DistAcc lore)-distribute acc =-  fromMaybe acc <$> distributeIfPossible acc--mkSegLevel :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-              DistNestT lore m (MkSegLevel lore (DistNestT lore m))-mkSegLevel = do-  mk_lvl <- asks distSegLevel-  return $ \w desc r -> do-    (lvl, stms) <- lift $ liftInner $ runBinderT' $ mk_lvl w desc r-    addStms stms-    return lvl--distributeIfPossible :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-                        DistAcc lore -> DistNestT lore m (Maybe (DistAcc lore))-distributeIfPossible acc = do-  nest <- asks distNest-  mk_lvl <- mkSegLevel-  tryDistribute mk_lvl nest (distTargets acc) (distStms acc) >>= \case-    Nothing -> return Nothing-    Just (targets, kernel) -> do-      postStm kernel-      return $ Just DistAcc { distTargets = targets-                            , distStms = mempty-                            }--distributeSingleStm :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-                       DistAcc lore -> Stm SOACS-                    -> DistNestT lore m (Maybe (PostStms lore,-                                                Result,-                                                KernelNest,-                                                 DistAcc lore))-distributeSingleStm acc bnd = do-  nest <- asks distNest-  mk_lvl <- mkSegLevel-  tryDistribute mk_lvl nest (distTargets acc) (distStms acc) >>= \case-    Nothing -> return Nothing-    Just (targets, distributed_bnds) ->-      tryDistributeStm nest targets bnd >>= \case-        Nothing -> return Nothing-        Just (res, targets', new_kernel_nest) ->-          return $ Just (PostStms distributed_bnds,-                         res,-                         new_kernel_nest,-                         DistAcc { distTargets = targets'-                                 , distStms = mempty-                                 })--segmentedScatterKernel :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-                          KernelNest-                       -> [Int]-                       -> PatternT Type-                       -> Certificates-                       -> SubExp-                       -> Lambda lore-                       -> [VName] -> [(SubExp,Int,VName)]-                       -> DistNestT lore m (Stms lore)-segmentedScatterKernel nest perm scatter_pat cs scatter_w lam ivs dests = do-  -- We replicate some of the checking done by 'isSegmentedOp', but-  -- things are different because a scatter is not a reduction or-  -- scan.-  ---  -- First, pretend that the scatter is also part of the nesting.  The-  -- KernelNest we produce here is technically not sensible, but it's-  -- good enough for flatKernel to work.-  let nesting =-        MapNesting scatter_pat (StmAux cs mempty ()) scatter_w $ zip (lambdaParams lam) ivs-      nest' =-        pushInnerKernelNesting (scatter_pat, bodyResult $ lambdaBody lam) nesting nest-  (ispace, kernel_inps) <- flatKernel nest'--  let (as_ws, as_ns, as) = unzip3 dests--  -- The input/output arrays ('as') _must_ correspond to some kernel-  -- input, or else the original nested scatter would have been-  -- ill-typed.  Find them.-  as_inps <- mapM (findInput kernel_inps) as--  mk_lvl <- mkSegLevel--  let rts = concatMap (take 1) $ chunks as_ns $-            drop (sum as_ns) $ lambdaReturnType lam-      (is,vs) = splitAt (sum as_ns) $ bodyResult $ lambdaBody lam--  -- Maybe add certificates to the indices.-  (is', k_body_stms) <- runBinder $ do-    addStms $ bodyStms $ lambdaBody lam-    forM is $ \i ->-      if cs == mempty-      then return i-      else certifying cs $ letSubExp "scatter_i" $ BasicOp $ SubExp i--  let k_body = KernelBody () k_body_stms $-               map (inPlaceReturn ispace) $-               zip3 as_ws as_inps $ chunks as_ns $ zip is' vs--  (k, k_bnds) <- mapKernel mk_lvl ispace kernel_inps rts k_body--  traverse renameStm <=< runBinder_ $ do-    addStms k_bnds--    let pat = Pattern [] $ rearrangeShape perm $-              patternValueElements $ loopNestingPattern $ fst nest--    letBind pat $ Op $ segOp k-  where findInput kernel_inps a =-          maybe bad return $ find ((==a) . kernelInputName) kernel_inps-        bad = error "Ill-typed nested scatter encountered."--        inPlaceReturn ispace (aw, inp, is_vs) =-          WriteReturns (init ws++[aw]) (kernelInputArray inp)-          [ (map DimFix $ map Var (init gtids)++[i], v) | (i,v) <- is_vs ]-          where (gtids,ws) = unzip ispace--segmentedUpdateKernel :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-                         KernelNest-                      -> [Int]-                      -> Certificates-                      -> VName-                      -> Slice SubExp-                      -> VName-                      -> DistNestT lore m (Stms lore)-segmentedUpdateKernel nest perm cs arr slice v = do-  (base_ispace, kernel_inps) <- flatKernel nest-  let slice_dims = sliceDims slice-  slice_gtids <- replicateM (length slice_dims) (newVName "gtid_slice")--  let ispace = base_ispace ++ zip slice_gtids slice_dims--  ((res_t, res), kstms) <- runBinder $ do--    -- Compute indexes into full array.-    v' <- certifying cs $-          letSubExp "v" $ BasicOp $ Index v $ map (DimFix . Var) slice_gtids-    let pexp = primExpFromSubExp int32-    slice_is <- traverse (toSubExp "index") $-                fixSlice (map (fmap pexp) slice) $ map (pexp . Var) slice_gtids--    let write_is = map (Var . fst) base_ispace ++ slice_is-        arr' = maybe (error "incorrectly typed Update") kernelInputArray $-               find ((==arr) . kernelInputName) kernel_inps-    arr_t <- lookupType arr'-    v_t <- subExpType v'-    return (v_t,-            WriteReturns (arrayDims arr_t) arr' [(map DimFix write_is, v')])--  mk_lvl <- mkSegLevel-  (k, prestms) <- mapKernel mk_lvl ispace kernel_inps [res_t] $-                  KernelBody () kstms [res]--  traverse renameStm <=< runBinder_ $ do-    addStms prestms--    let pat = Pattern [] $ rearrangeShape perm $-              patternValueElements $ loopNestingPattern $ fst nest--    letBind pat $ Op $ segOp k--segmentedGatherKernel :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-                         KernelNest-                      -> Certificates-                      -> VName-                      -> Slice SubExp-                      -> DistNestT lore m (Stms lore)-segmentedGatherKernel nest cs arr slice = do-  let slice_dims = sliceDims slice-  slice_gtids <- replicateM (length slice_dims) (newVName "gtid_slice")--  (base_ispace, kernel_inps) <- flatKernel nest-  let ispace = base_ispace ++ zip slice_gtids slice_dims--  ((res_t, res), kstms) <- runBinder $ do-    -- Compute indexes into full array.-    slice'' <- subExpSlice $-               sliceSlice (primExpSlice slice) $-               primExpSlice $ map (DimFix . Var) slice_gtids-    v' <- certifying cs $ letSubExp "v" $ BasicOp $ Index arr slice''-    v_t <- subExpType v'-    return (v_t, Returns ResultMaySimplify v')--  mk_lvl <- mkSegLevel-  (k, prestms) <- mapKernel mk_lvl ispace kernel_inps [res_t] $-                  KernelBody () kstms [res]--  traverse renameStm <=< runBinder_ $ do-    addStms prestms--    let pat = Pattern [] $ patternValueElements $ loopNestingPattern $ fst nest--    letBind pat $ Op $ segOp k--segmentedHistKernel :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-                       KernelNest-                    -> [Int]-                    -> Certificates-                    -> SubExp-                    -> [SOACS.HistOp SOACS]-                    -> Lambda lore-                    -> [VName]-                    -> DistNestT lore m (Stms lore)-segmentedHistKernel nest perm cs hist_w ops lam arrs = do-  -- We replicate some of the checking done by 'isSegmentedOp', but-  -- things are different because a Hist is not a reduction or-  -- scan.-  (ispace, inputs) <- flatKernel nest-  let orig_pat = Pattern [] $ rearrangeShape perm $-                 patternValueElements $ loopNestingPattern $ fst nest--  -- The input/output arrays _must_ correspond to some kernel input,-  -- or else the original nested Hist would have been ill-typed.-  -- Find them.-  ops' <- forM ops $ \(SOACS.HistOp num_bins rf dests nes op) ->-    SOACS.HistOp num_bins rf-    <$> mapM (fmap kernelInputArray . findInput inputs) dests-    <*> pure nes-    <*> pure op--  mk_lvl <- asks distSegLevel-  onLambda <- asks distOnSOACSLambda-  let onLambda' = fmap fst . runBinder . onLambda-  liftInner $ runBinderT'_ $ do-    -- It is important not to launch unnecessarily many threads for-    -- histograms, because it may mean we unnecessarily need to reduce-    -- subhistograms as well.-    lvl <- mk_lvl (hist_w : map snd ispace) "seghist" $ NoRecommendation SegNoVirt-    addStms =<<-      histKernel onLambda' lvl orig_pat ispace inputs cs hist_w ops' lam arrs-  where findInput kernel_inps a =-          maybe bad return $ find ((==a) . kernelInputName) kernel_inps-        bad = error "Ill-typed nested Hist encountered."--histKernel :: (MonadBinder m, DistLore (Lore m)) =>-              (Lambda SOACS -> m (Lambda (Lore m)))-           -> SegOpLevel (Lore m)-           -> PatternT Type -> [(VName, SubExp)] -> [KernelInput]-           -> Certificates -> SubExp -> [SOACS.HistOp SOACS]-           -> Lambda (Lore m) -> [VName]-           -> m (Stms (Lore m))-histKernel onLambda lvl orig_pat ispace inputs cs hist_w ops lam arrs = runBinderT'_ $ do-  ops' <- forM ops $ \(SOACS.HistOp num_bins rf dests nes op) -> do-    (op', nes', shape) <- determineReduceOp op nes-    op'' <- lift $ onLambda op'-    return $ HistOp num_bins rf dests nes' shape op''--  let isDest = flip elem $ concatMap histDest ops'-      inputs' = filter (not . isDest . kernelInputArray) inputs--  certifying cs $-    addStms =<< traverse renameStm =<<-    segHist lvl orig_pat hist_w ispace inputs' ops' lam arrs--determineReduceOp :: (MonadBinder m, Lore m ~ lore) =>-                     Lambda SOACS -> [SubExp]-                  -> m (Lambda SOACS, [SubExp], Shape)-determineReduceOp lam nes =-  -- FIXME? We are assuming that the accumulator is a replicate, and-  -- we fish out its value in a gross way.-  case mapM subExpVar nes of-    Just ne_vs' -> do-      let (shape, lam') = isVectorMap lam-      nes' <- forM ne_vs' $ \ne_v -> do-        ne_v_t <- lookupType ne_v-        letSubExp "hist_ne" $-          BasicOp $ Index ne_v $ fullSlice ne_v_t $-          replicate (shapeRank shape) $ DimFix $ intConst Int32 0-      return (lam', nes', shape)--    Nothing ->-      return (lam, nes, mempty)--isVectorMap :: Lambda SOACS -> (Shape, Lambda SOACS)-isVectorMap lam-  | [Let (Pattern [] pes) _ (Op (Screma w form arrs))] <--      stmsToList $ bodyStms $ lambdaBody lam,-    bodyResult (lambdaBody lam) == map (Var . patElemName) pes,-    Just map_lam <- isMapSOAC form,-    arrs == map paramName (lambdaParams lam) =-      let (shape, lam') = isVectorMap map_lam-      in (Shape [w] <> shape, lam')-  | otherwise = (mempty, lam)--segmentedScanomapKernel :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-                           KernelNest-                        -> [Int]-                        -> SubExp-                        -> Lambda lore -> Lambda lore-                        -> [SubExp] -> [VName]-                        -> DistNestT lore m (Maybe (Stms lore))-segmentedScanomapKernel nest perm segment_size lam map_lam nes arrs = do-  mk_lvl <- asks distSegLevel-  isSegmentedOp nest perm (freeIn lam) (freeIn map_lam) nes [] $-    \pat ispace inps nes' _ -> do-    let scan_op = SegBinOp Noncommutative lam nes' mempty-    lvl <- mk_lvl (segment_size : map snd ispace) "segscan" $ NoRecommendation SegNoVirt-    addStms =<< traverse renameStm =<<-      segScan lvl pat segment_size [scan_op] map_lam arrs ispace inps--regularSegmentedRedomapKernel :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-                                 KernelNest-                              -> [Int]-                              -> SubExp -> Commutativity-                              -> Lambda lore -> Lambda lore-                              -> [SubExp] -> [VName]-                              -> DistNestT lore m (Maybe (Stms lore))-regularSegmentedRedomapKernel nest perm segment_size comm lam map_lam nes arrs = do-  mk_lvl <- asks distSegLevel-  isSegmentedOp nest perm (freeIn lam) (freeIn map_lam) nes [] $-    \pat ispace inps nes' _ -> do-      let red_op = SegBinOp comm lam nes' mempty-      lvl <- mk_lvl (segment_size : map snd ispace) "segred" $ NoRecommendation SegNoVirt-      addStms =<< traverse renameStm =<<-        segRed lvl pat segment_size [red_op] map_lam arrs ispace inps--isSegmentedOp :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-                 KernelNest-              -> [Int]-              -> Names -> Names-              -> [SubExp] -> [VName]-              -> (PatternT Type-                  -> [(VName, SubExp)]-                  -> [KernelInput]-                  -> [SubExp] -> [VName]-                  -> BinderT lore m ())-              -> DistNestT lore m (Maybe (Stms lore))-isSegmentedOp nest perm free_in_op _free_in_fold_op nes arrs m = runMaybeT $ do-  -- We must verify that array inputs to the operation are inputs to-  -- the outermost loop nesting or free in the loop nest.  Nothing-  -- free in the op may be bound by the nest.  Furthermore, the-  -- neutral elements must be free in the loop nest.-  ---  -- We must summarise any names from free_in_op that are bound in the-  -- nest, and describe how to obtain them given segment indices.--  let bound_by_nest = boundInKernelNest nest--  (ispace, kernel_inps) <- flatKernel nest--  when (free_in_op `namesIntersect` bound_by_nest) $-    fail "Non-fold lambda uses nest-bound parameters."--  let indices = map fst ispace--      prepareNe (Var v) | v `nameIn` bound_by_nest =-                            fail "Neutral element bound in nest"-      prepareNe ne = return ne--      prepareArr arr =-        case find ((==arr) . kernelInputName) kernel_inps of-          Just inp-            | kernelInputIndices inp == map Var indices ->-                return $ return $ kernelInputArray inp-          Nothing | not (arr `nameIn` bound_by_nest) ->-                      -- This input is something that is free inside-                      -- the loop nesting. We will have to replicate-                      -- it.-                      return $-                      letExp (baseString arr ++ "_repd")-                      (BasicOp $ Replicate (Shape $ map snd ispace) $ Var arr)-          _ ->-            fail "Input not free, perfectly mapped, or outermost."--  nes' <- mapM prepareNe nes--  mk_arrs <- mapM prepareArr arrs--  lift $ liftInner $ runBinderT'_ $ do-    nested_arrs <- sequence mk_arrs--    let pat = Pattern [] $ rearrangeShape perm $-              patternValueElements $ loopNestingPattern $ fst nest--    m pat ispace kernel_inps nes' nested_arrs--permutationAndMissing :: PatternT Type -> [SubExp] -> Maybe ([Int], [PatElemT Type])-permutationAndMissing pat res = do-  let pes = patternValueElements pat-      (_used,unused) =-        partition ((`nameIn` freeIn res) . patElemName) pes-      res_expanded = res ++ map (Var . patElemName) unused-  perm <- map (Var . patElemName) pes `isPermutationOf` res_expanded-  return (perm, unused)---- Add extra pattern elements to every kernel nesting level.-expandKernelNest :: MonadFreshNames m =>-                    [PatElemT Type] -> KernelNest -> m KernelNest-expandKernelNest pes (outer_nest, inner_nests) = do-  let outer_size = loopNestingWidth outer_nest :-                   map loopNestingWidth inner_nests-      inner_sizes = tails $ map loopNestingWidth inner_nests-  outer_nest' <- expandWith outer_nest outer_size-  inner_nests' <- zipWithM expandWith inner_nests inner_sizes-  return (outer_nest', inner_nests')-  where expandWith nest dims = do-           pes' <- mapM (expandPatElemWith dims) pes-           return nest { loopNestingPattern =-                           Pattern [] $-                           patternElements (loopNestingPattern nest) <> pes'-                       }--        expandPatElemWith dims pe = do-          name <- newVName $ baseString $ patElemName pe-          return pe { patElemName = name-                    , patElemDec = patElemType pe `arrayOfShape` Shape dims-                    }--kernelOrNot :: (MonadFreshNames m, DistLore lore) =>-               Certificates -> Stm SOACS -> DistAcc lore-            -> PostStms lore -> DistAcc lore -> Maybe (Stms lore)-            -> DistNestT lore m (DistAcc lore)-kernelOrNot cs bnd acc _ _ Nothing =-  addStmToAcc (certify cs bnd) acc-kernelOrNot cs _ _ kernels acc' (Just bnds) = do-  addPostStms kernels-  postStm $ fmap (certify cs) bnds-  return acc'--distributeMap :: (MonadFreshNames m, LocalScope lore m, DistLore lore) =>-                 MapLoop -> DistAcc lore-              -> DistNestT lore m (DistAcc lore)-distributeMap (MapLoop pat aux w lam arrs) acc =-  distribute =<<-  mapNesting pat aux w lam arrs-  (distribute =<< distributeMapBodyStms acc' lam_bnds)--  where acc' = DistAcc { distTargets = pushInnerTarget-                                       (pat, bodyResult $ lambdaBody lam) $-                                       distTargets acc-                       , distStms = mempty-                       }--        lam_bnds = bodyStms $ lambdaBody lam+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}++module Futhark.Pass.ExtractKernels.DistributeNests+  ( MapLoop (..),+    mapLoopStm,+    bodyContainsParallelism,+    lambdaContainsParallelism,+    determineReduceOp,+    histKernel,+    DistEnv (..),+    DistAcc (..),+    runDistNestT,+    DistNestT,+    liftInner,+    distributeMap,+    distribute,+    distributeSingleStm,+    distributeMapBodyStms,+    addStmsToAcc,+    addStmToAcc,+    permutationAndMissing,+    addPostStms,+    postStm,+    inNesting,+  )+where++import Control.Arrow (first)+import Control.Monad.Identity+import Control.Monad.RWS.Strict+import Control.Monad.Reader+import Control.Monad.Trans.Maybe+import Control.Monad.Writer.Strict+import Data.List (find, partition, tails)+import qualified Data.Map as M+import Data.Maybe+import Futhark.IR+import Futhark.IR.SOACS (SOACS)+import qualified Futhark.IR.SOACS as SOACS+import Futhark.IR.SOACS.SOAC hiding (HistOp, histDest)+import Futhark.IR.SOACS.Simplify (simpleSOACS, simplifyStms)+import Futhark.IR.SegOp+import Futhark.MonadFreshNames+import Futhark.Pass.ExtractKernels.BlockedKernel+import Futhark.Pass.ExtractKernels.Distribution+import Futhark.Pass.ExtractKernels.ISRWIM+import Futhark.Pass.ExtractKernels.Interchange+import Futhark.Tools+import Futhark.Transform.CopyPropagate+import qualified Futhark.Transform.FirstOrderTransform as FOT+import Futhark.Transform.Rename+import Futhark.Util+import Futhark.Util.Log++scopeForSOACs :: SameScope lore SOACS => Scope lore -> Scope SOACS+scopeForSOACs = castScope++data MapLoop = MapLoop SOACS.Pattern (StmAux ()) SubExp SOACS.Lambda [VName]++mapLoopStm :: MapLoop -> Stm SOACS+mapLoopStm (MapLoop pat aux w lam arrs) =+  Let pat aux $ Op $ Screma w (mapSOAC lam) arrs++data DistEnv lore m = DistEnv+  { distNest :: Nestings,+    distScope :: Scope lore,+    distOnTopLevelStms :: Stms SOACS -> DistNestT lore m (Stms lore),+    distOnInnerMap ::+      MapLoop ->+      DistAcc lore ->+      DistNestT lore m (DistAcc lore),+    distOnSOACSStms :: Stm SOACS -> Binder lore (Stms lore),+    distOnSOACSLambda :: Lambda SOACS -> Binder lore (Lambda lore),+    distSegLevel :: MkSegLevel lore m+  }++data DistAcc lore = DistAcc+  { distTargets :: Targets,+    distStms :: Stms lore+  }++data DistRes lore = DistRes+  { accPostStms :: PostStms lore,+    accLog :: Log+  }++instance Semigroup (DistRes lore) where+  DistRes ks1 log1 <> DistRes ks2 log2 =+    DistRes (ks1 <> ks2) (log1 <> log2)++instance Monoid (DistRes lore) where+  mempty = DistRes mempty mempty++newtype PostStms lore = PostStms {unPostStms :: Stms lore}++instance Semigroup (PostStms lore) where+  PostStms xs <> PostStms ys = PostStms $ ys <> xs++instance Monoid (PostStms lore) where+  mempty = PostStms mempty++typeEnvFromDistAcc :: DistLore lore => DistAcc lore -> Scope lore+typeEnvFromDistAcc = scopeOfPattern . fst . outerTarget . distTargets++addStmsToAcc :: Stms lore -> DistAcc lore -> DistAcc lore+addStmsToAcc stms acc =+  acc {distStms = stms <> distStms acc}++addStmToAcc ::+  (MonadFreshNames m, DistLore lore) =>+  Stm SOACS ->+  DistAcc lore ->+  DistNestT lore m (DistAcc lore)+addStmToAcc stm acc = do+  onSoacs <- asks distOnSOACSStms+  (stm', _) <- runBinder $ onSoacs stm+  return acc {distStms = stm' <> distStms acc}++soacsLambda ::+  (MonadFreshNames m, DistLore lore) =>+  Lambda SOACS ->+  DistNestT lore m (Lambda lore)+soacsLambda lam = do+  onLambda <- asks distOnSOACSLambda+  fst <$> runBinder (onLambda lam)++newtype DistNestT lore m a+  = DistNestT (ReaderT (DistEnv lore m) (WriterT (DistRes lore) m) a)+  deriving+    ( Functor,+      Applicative,+      Monad,+      MonadReader (DistEnv lore m),+      MonadWriter (DistRes lore)+    )++liftInner :: (LocalScope lore m, DistLore lore) => m a -> DistNestT lore m a+liftInner m = do+  outer_scope <- askScope+  DistNestT $+    lift $+      lift $ do+        inner_scope <- askScope+        localScope (outer_scope `M.difference` inner_scope) m++instance MonadFreshNames m => MonadFreshNames (DistNestT lore m) where+  getNameSource = DistNestT $ lift getNameSource+  putNameSource = DistNestT . lift . putNameSource++instance (Monad m, ASTLore lore) => HasScope lore (DistNestT lore m) where+  askScope = asks distScope++instance (Monad m, ASTLore lore) => LocalScope lore (DistNestT lore m) where+  localScope types = local $ \env ->+    env {distScope = types <> distScope env}++instance Monad m => MonadLogger (DistNestT lore m) where+  addLog msgs = tell mempty {accLog = msgs}++runDistNestT ::+  (MonadLogger m, DistLore lore) =>+  DistEnv lore m ->+  DistNestT lore m (DistAcc lore) ->+  m (Stms lore)+runDistNestT env (DistNestT m) = do+  (acc, res) <- runWriterT $ runReaderT m env+  addLog $ accLog res+  -- There may be a few final targets remaining - these correspond to+  -- arrays that are identity mapped, and must have statements+  -- inserted here.+  return $+    unPostStms (accPostStms res)+      <> identityStms (outerTarget $ distTargets acc)+  where+    outermost = nestingLoop $+      case distNest env of+        (nest, []) -> nest+        (_, nest : _) -> nest+    params_to_arrs =+      map (first paramName) $+        loopNestingParamsAndArrs outermost++    identityStms (rem_pat, res) =+      stmsFromList $ zipWith identityStm (patternValueElements rem_pat) res+    identityStm pe (Var v)+      | Just arr <- lookup v params_to_arrs =+        Let (Pattern [] [pe]) (defAux ()) $ BasicOp $ Copy arr+    identityStm pe se =+      Let (Pattern [] [pe]) (defAux ()) $+        BasicOp $+          Replicate (Shape [loopNestingWidth outermost]) se++addPostStms :: Monad m => PostStms lore -> DistNestT lore m ()+addPostStms ks = tell $ mempty {accPostStms = ks}++postStm :: Monad m => Stms lore -> DistNestT lore m ()+postStm stms = addPostStms $ PostStms stms++withStm ::+  (Monad m, DistLore lore) =>+  Stm SOACS ->+  DistNestT lore m a ->+  DistNestT lore m a+withStm stm = local $ \env ->+  env+    { distScope =+        castScope (scopeOf stm) <> distScope env,+      distNest =+        letBindInInnerNesting provided $+          distNest env+    }+  where+    provided = namesFromList $ patternNames $ stmPattern stm++leavingNesting ::+  (MonadFreshNames m, DistLore lore) =>+  DistAcc lore ->+  DistNestT lore m (DistAcc lore)+leavingNesting acc =+  case popInnerTarget $ distTargets acc of+    Nothing ->+      error "The kernel targets list is unexpectedly small"+    Just ((pat, res), newtargets)+      | not $ null $ distStms acc -> do+        -- Any statements left over correspond to something that+        -- could not be distributed because it would cause irregular+        -- arrays.  These must be reconstructed into a a Map SOAC+        -- that will be sequentialised. XXX: life would be better if+        -- we were able to distribute irregular parallelism.+        (Nesting _ inner, _) <- asks distNest+        let MapNesting _ aux w params_and_arrs = inner+            body = Body () (distStms acc) res+            used_in_body = freeIn body+            (used_params, used_arrs) =+              unzip $+                filter ((`nameIn` used_in_body) . paramName . fst) params_and_arrs+            lam' =+              Lambda+                { lambdaParams = used_params,+                  lambdaBody = body,+                  lambdaReturnType = map rowType $ patternTypes pat+                }+        stms <-+          runBinder_ $+            auxing aux $+              FOT.transformSOAC pat $+                Screma w (mapSOAC lam') used_arrs++        return $ acc {distTargets = newtargets, distStms = stms}+      | otherwise -> do+        -- Any results left over correspond to a Replicate or a Copy in+        -- the parent nesting, depending on whether the argument is a+        -- parameter of the innermost nesting.+        (Nesting _ inner_nesting, _) <- asks distNest+        let w = loopNestingWidth inner_nesting+            aux = loopNestingAux inner_nesting+            inps = loopNestingParamsAndArrs inner_nesting++            remnantStm pe (Var v)+              | Just (_, arr) <- find ((== v) . paramName . fst) inps =+                Let (Pattern [] [pe]) aux $+                  BasicOp $ Copy arr+            remnantStm pe se =+              Let (Pattern [] [pe]) aux $+                BasicOp $ Replicate (Shape [w]) se++            stms =+              stmsFromList $ zipWith remnantStm (patternElements pat) res++        return $ acc {distTargets = newtargets, distStms = stms}++mapNesting ::+  (MonadFreshNames m, DistLore lore) =>+  PatternT Type ->+  StmAux () ->+  SubExp ->+  Lambda SOACS ->+  [VName] ->+  DistNestT lore m (DistAcc lore) ->+  DistNestT lore m (DistAcc lore)+mapNesting pat aux w lam arrs m =+  local extend $ leavingNesting =<< m+  where+    nest =+      Nesting mempty $+        MapNesting pat aux w $+          zip (lambdaParams lam) arrs+    extend env =+      env+        { distNest = pushInnerNesting nest $ distNest env,+          distScope = castScope (scopeOf lam) <> distScope env+        }++inNesting ::+  (Monad m, DistLore lore) =>+  KernelNest ->+  DistNestT lore m a ->+  DistNestT lore m a+inNesting (outer, nests) = local $ \env ->+  env+    { distNest = (inner, nests'),+      distScope = foldMap scopeOfLoopNesting (outer : nests) <> distScope env+    }+  where+    (inner, nests') =+      case reverse nests of+        [] -> (asNesting outer, [])+        (inner' : ns) -> (asNesting inner', map asNesting $ outer : reverse ns)+    asNesting = Nesting mempty++bodyContainsParallelism :: Body SOACS -> Bool+bodyContainsParallelism = any isParallelStm . bodyStms+  where+    isParallelStm stm =+      isMap (stmExp stm)+        && not ("sequential" `inAttrs` stmAuxAttrs (stmAux stm))+    isMap Op {} = True+    isMap _ = False++lambdaContainsParallelism :: Lambda SOACS -> Bool+lambdaContainsParallelism = bodyContainsParallelism . lambdaBody++distributeMapBodyStms ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  DistAcc lore ->+  Stms SOACS ->+  DistNestT lore m (DistAcc lore)+distributeMapBodyStms orig_acc = distribute <=< onStms orig_acc . stmsToList+  where+    onStms acc [] = return acc+    onStms acc (Let pat (StmAux cs _ _) (Op (Stream w (Sequential accs) lam arrs)) : stms) = do+      types <- asksScope scopeForSOACs+      stream_stms <-+        snd <$> runBinderT (sequentialStreamWholeArray pat w accs lam arrs) types+      (_, stream_stms') <-+        runReaderT (copyPropagateInStms simpleSOACS types stream_stms) types+      onStms acc $ stmsToList (fmap (certify cs) stream_stms') ++ stms+    onStms acc (stm : stms) =+      -- It is important that stm is in scope if 'maybeDistributeStm'+      -- wants to distribute, even if this causes the slightly silly+      -- situation that stm is in scope of itself.+      withStm stm $ maybeDistributeStm stm =<< onStms acc stms++onInnerMap :: Monad m => MapLoop -> DistAcc lore -> DistNestT lore m (DistAcc lore)+onInnerMap loop acc = do+  f <- asks distOnInnerMap+  f loop acc++onTopLevelStms :: Monad m => Stms SOACS -> DistNestT lore m ()+onTopLevelStms stms = do+  f <- asks distOnTopLevelStms+  postStm =<< f stms++maybeDistributeStm ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  Stm SOACS ->+  DistAcc lore ->+  DistNestT lore m (DistAcc lore)+maybeDistributeStm stm acc+  | "sequential" `inAttrs` stmAuxAttrs (stmAux stm) =+    addStmToAcc stm acc+maybeDistributeStm (Let pat aux (Op soac)) acc+  | "sequential_outer" `inAttrs` stmAuxAttrs aux =+    distributeMapBodyStms acc . fmap (certify (stmAuxCerts aux))+      =<< runBinder_ (FOT.transformSOAC pat soac)+maybeDistributeStm stm@(Let pat _ (Op (Screma w form arrs))) acc+  | Just lam <- isMapSOAC form =+    -- Only distribute inside the map if we can distribute everything+    -- following the map.+    distributeIfPossible acc >>= \case+      Nothing -> addStmToAcc stm acc+      Just acc' -> distribute =<< onInnerMap (MapLoop pat (stmAux stm) w lam arrs) acc'+maybeDistributeStm bnd@(Let pat _ (DoLoop [] val form@ForLoop {} body)) acc+  | null (patternContextElements pat),+    bodyContainsParallelism body =+    distributeSingleStm acc bnd >>= \case+      Just (kernels, res, nest, acc')+        | not $ freeIn form `namesIntersect` boundInKernelNest nest,+          Just (perm, pat_unused) <- permutationAndMissing pat res ->+          -- We need to pretend pat_unused was used anyway, by adding+          -- it to the kernel nest.+          localScope (typeEnvFromDistAcc acc') $ do+            addPostStms kernels+            nest' <- expandKernelNest pat_unused nest+            types <- asksScope scopeForSOACs++            -- Simplification is key to hoisting out statements that+            -- were variant to the loop, but invariant to the outer maps+            -- (which are now innermost).+            stms <-+              (`runReaderT` types) $+                fmap snd . simplifyStms+                  =<< interchangeLoops nest' (SeqLoop perm pat val form body)+            onTopLevelStms stms+            return acc'+      _ ->+        addStmToAcc bnd acc+maybeDistributeStm stm@(Let pat _ (If cond tbranch fbranch ret)) acc+  | null (patternContextElements pat),+    bodyContainsParallelism tbranch || bodyContainsParallelism fbranch+      || not (all primType (ifReturns ret)) =+    distributeSingleStm acc stm >>= \case+      Just (kernels, res, nest, acc')+        | not $+            (freeIn cond <> freeIn ret) `namesIntersect` boundInKernelNest nest,+          Just (perm, pat_unused) <- permutationAndMissing pat res ->+          -- We need to pretend pat_unused was used anyway, by adding+          -- it to the kernel nest.+          localScope (typeEnvFromDistAcc acc') $ do+            nest' <- expandKernelNest pat_unused nest+            addPostStms kernels+            types <- asksScope scopeForSOACs+            let branch = Branch perm pat cond tbranch fbranch ret+            stms <-+              (`runReaderT` types) $+                fmap snd . simplifyStms+                  =<< interchangeBranch nest' branch+            onTopLevelStms stms+            return acc'+      _ ->+        addStmToAcc stm acc+maybeDistributeStm (Let pat aux (Op (Screma w form arrs))) acc+  | Just [Reduce comm lam nes] <- isReduceSOAC form,+    Just m <- irwim pat w comm lam $ zip nes arrs = do+    types <- asksScope scopeForSOACs+    (_, bnds) <- runBinderT (auxing aux m) types+    distributeMapBodyStms acc bnds++-- Parallelise segmented scatters.+maybeDistributeStm bnd@(Let pat (StmAux cs _ _) (Op (Scatter w lam ivs as))) acc =+  distributeSingleStm acc bnd >>= \case+    Just (kernels, res, nest, acc')+      | Just (perm, pat_unused) <- permutationAndMissing pat res ->+        localScope (typeEnvFromDistAcc acc') $ do+          nest' <- expandKernelNest pat_unused nest+          lam' <- soacsLambda lam+          addPostStms kernels+          postStm =<< segmentedScatterKernel nest' perm pat cs w lam' ivs as+          return acc'+    _ ->+      addStmToAcc bnd acc+-- Parallelise segmented Hist.+maybeDistributeStm bnd@(Let pat (StmAux cs _ _) (Op (Hist w ops lam as))) acc =+  distributeSingleStm acc bnd >>= \case+    Just (kernels, res, nest, acc')+      | Just (perm, pat_unused) <- permutationAndMissing pat res ->+        localScope (typeEnvFromDistAcc acc') $ do+          lam' <- soacsLambda lam+          nest' <- expandKernelNest pat_unused nest+          addPostStms kernels+          postStm =<< segmentedHistKernel nest' perm cs w ops lam' as+          return acc'+    _ ->+      addStmToAcc bnd acc+-- Parallelise Index slices if the result is going to be returned+-- directly from the kernel.  This is because we would otherwise have+-- to sequentialise writing the result, which may be costly.+maybeDistributeStm+  stm@( Let+          (Pattern [] [pe])+          aux+          (BasicOp (Index arr slice))+        )+  acc+    | not $ null $ sliceDims slice,+      Var (patElemName pe) `elem` snd (innerTarget (distTargets acc)) =+      distributeSingleStm acc stm >>= \case+        Just (kernels, _res, nest, acc') ->+          localScope (typeEnvFromDistAcc acc') $ do+            addPostStms kernels+            postStm =<< segmentedGatherKernel nest (stmAuxCerts aux) arr slice+            return acc'+        _ ->+          addStmToAcc stm acc+-- If the scan can be distributed by itself, we will turn it into a+-- segmented scan.+--+-- If the scan cannot be distributed by itself, it will be+-- sequentialised in the default case for this function.+maybeDistributeStm bnd@(Let pat (StmAux cs _ _) (Op (Screma w form arrs))) acc+  | Just (scans, map_lam) <- isScanomapSOAC form,+    Scan lam nes <- singleScan scans =+    distributeSingleStm acc bnd >>= \case+      Just (kernels, res, nest, acc')+        | Just (perm, pat_unused) <- permutationAndMissing pat res ->+          -- We need to pretend pat_unused was used anyway, by adding+          -- it to the kernel nest.+          localScope (typeEnvFromDistAcc acc') $ do+            nest' <- expandKernelNest pat_unused nest+            map_lam' <- soacsLambda map_lam+            lam' <- soacsLambda lam+            localScope (typeEnvFromDistAcc acc') $+              segmentedScanomapKernel nest' perm w lam' map_lam' nes arrs+                >>= kernelOrNot cs bnd acc kernels acc'+      _ ->+        addStmToAcc bnd acc+-- if the reduction can be distributed by itself, we will turn it into a+-- segmented reduce.+--+-- If the reduction cannot be distributed by itself, it will be+-- sequentialised in the default case for this function.+maybeDistributeStm bnd@(Let pat (StmAux cs _ _) (Op (Screma w form arrs))) acc+  | Just (reds, map_lam) <- isRedomapSOAC form,+    Reduce comm lam nes <- singleReduce reds =+    distributeSingleStm acc bnd >>= \case+      Just (kernels, res, nest, acc')+        | Just (perm, pat_unused) <- permutationAndMissing pat res ->+          -- We need to pretend pat_unused was used anyway, by adding+          -- it to the kernel nest.+          localScope (typeEnvFromDistAcc acc') $ do+            nest' <- expandKernelNest pat_unused nest++            lam' <- soacsLambda lam+            map_lam' <- soacsLambda map_lam++            let comm'+                  | commutativeLambda lam = Commutative+                  | otherwise = comm++            regularSegmentedRedomapKernel nest' perm w comm' lam' map_lam' nes arrs+              >>= kernelOrNot cs bnd acc kernels acc'+      _ ->+        addStmToAcc bnd acc+maybeDistributeStm (Let pat (StmAux cs _ _) (Op (Screma w form arrs))) acc = do+  -- This Screma is too complicated for us to immediately do+  -- anything, so split it up and try again.+  scope <- asksScope scopeForSOACs+  distributeMapBodyStms acc . fmap (certify cs) . snd+    =<< runBinderT (dissectScrema pat w form arrs) scope+maybeDistributeStm (Let pat aux (BasicOp (Replicate (Shape (d : ds)) v))) acc+  | [t] <- patternTypes pat = do+    tmp <- newVName "tmp"+    let rowt = rowType t+        newbnd = Let pat aux $ Op $ Screma d (mapSOAC lam) []+        tmpbnd =+          Let (Pattern [] [PatElem tmp rowt]) aux $+            BasicOp $ Replicate (Shape ds) v+        lam =+          Lambda+            { lambdaReturnType = [rowt],+              lambdaParams = [],+              lambdaBody = mkBody (oneStm tmpbnd) [Var tmp]+            }+    maybeDistributeStm newbnd acc+maybeDistributeStm bnd@(Let _ aux (BasicOp Copy {})) acc =+  distributeSingleUnaryStm acc bnd $ \_ outerpat arr ->+    return $ oneStm $ Let outerpat aux $ BasicOp $ Copy arr+-- Opaques are applied to the full array, because otherwise they can+-- drastically inhibit parallelisation in some cases.+maybeDistributeStm bnd@(Let (Pattern [] [pe]) aux (BasicOp Opaque {})) acc+  | not $ primType $ typeOf pe =+    distributeSingleUnaryStm acc bnd $ \_ outerpat arr ->+      return $ oneStm $ Let outerpat aux $ BasicOp $ Copy arr+maybeDistributeStm bnd@(Let _ aux (BasicOp (Rearrange perm _))) acc =+  distributeSingleUnaryStm acc bnd $ \nest outerpat arr -> do+    let r = length (snd nest) + 1+        perm' = [0 .. r -1] ++ map (+ r) perm+    -- We need to add a copy, because the original map nest+    -- will have produced an array without aliases, and so must we.+    arr' <- newVName $ baseString arr+    arr_t <- lookupType arr+    return $+      stmsFromList+        [ Let (Pattern [] [PatElem arr' arr_t]) aux $ BasicOp $ Copy arr,+          Let outerpat aux $ BasicOp $ Rearrange perm' arr'+        ]+maybeDistributeStm bnd@(Let _ aux (BasicOp (Reshape reshape _))) acc =+  distributeSingleUnaryStm acc bnd $ \nest outerpat arr -> do+    let reshape' =+          map DimNew (kernelNestWidths nest)+            ++ map DimNew (newDims reshape)+    return $ oneStm $ Let outerpat aux $ BasicOp $ Reshape reshape' arr+maybeDistributeStm stm@(Let _ aux (BasicOp (Rotate rots _))) acc =+  distributeSingleUnaryStm acc stm $ \nest outerpat arr -> do+    let rots' = map (const $ intConst Int64 0) (kernelNestWidths nest) ++ rots+    return $ oneStm $ Let outerpat aux $ BasicOp $ Rotate rots' arr+maybeDistributeStm stm@(Let pat aux (BasicOp (Update arr slice (Var v)))) acc+  | not $ null $ sliceDims slice =+    distributeSingleStm acc stm >>= \case+      Just (kernels, res, nest, acc')+        | res == map Var (patternNames $ stmPattern stm),+          Just (perm, pat_unused) <- permutationAndMissing pat res -> do+          addPostStms kernels+          localScope (typeEnvFromDistAcc acc') $ do+            nest' <- expandKernelNest pat_unused nest+            postStm+              =<< segmentedUpdateKernel nest' perm (stmAuxCerts aux) arr slice v+            return acc'+      _ -> addStmToAcc stm acc+-- XXX?  This rule is present to avoid the case where an in-place+-- update is distributed as its own kernel, as this would mean thread+-- then writes the entire array that it updated.  This is problematic+-- because the in-place updates is O(1), but writing the array is+-- O(n).  It is OK if the in-place update is preceded, followed, or+-- nested inside a sequential loop or similar, because that will+-- probably be O(n) by itself.  As a hack, we only distribute if there+-- does not appear to be a loop following.  The better solution is to+-- depend on memory block merging for this optimisation, but it is not+-- ready yet.+maybeDistributeStm (Let pat aux (BasicOp (Update arr [DimFix i] v))) acc+  | [t] <- patternTypes pat,+    arrayRank t == 1,+    not $ any (amortises . stmExp) $ distStms acc = do+    let w = arraySize 0 t+        et = stripArray 1 t+        lam =+          Lambda+            { lambdaParams = [],+              lambdaReturnType = [Prim int64, et],+              lambdaBody = mkBody mempty [i, v]+            }+    maybeDistributeStm (Let pat aux $ Op $ Scatter (intConst Int64 1) lam [] [(w, 1, arr)]) acc+  where+    amortises DoLoop {} = True+    amortises Op {} = True+    amortises _ = False+maybeDistributeStm stm@(Let _ aux (BasicOp (Concat d x xs w))) acc =+  distributeSingleStm acc stm >>= \case+    Just (kernels, _, nest, acc') ->+      localScope (typeEnvFromDistAcc acc') $+        segmentedConcat nest+          >>= kernelOrNot (stmAuxCerts aux) stm acc kernels acc'+    _ ->+      addStmToAcc stm acc+  where+    segmentedConcat nest =+      isSegmentedOp nest [0] mempty mempty [] (x : xs) $+        \pat _ _ _ (x' : xs') ->+          let d' = d + length (snd nest) + 1+           in addStm $ Let pat aux $ BasicOp $ Concat d' x' xs' w+maybeDistributeStm bnd acc =+  addStmToAcc bnd acc++distributeSingleUnaryStm ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  DistAcc lore ->+  Stm SOACS ->+  (KernelNest -> PatternT Type -> VName -> DistNestT lore m (Stms lore)) ->+  DistNestT lore m (DistAcc lore)+distributeSingleUnaryStm acc bnd f =+  distributeSingleStm acc bnd >>= \case+    Just (kernels, res, nest, acc')+      | res == map Var (patternNames $ stmPattern bnd),+        (outer, _) <- nest,+        [(arr_p, arr)] <- loopNestingParamsAndArrs outer,+        boundInKernelNest nest `namesIntersection` freeIn bnd+          == oneName (paramName arr_p),+        perfectlyMapped arr nest -> do+        addPostStms kernels+        let outerpat = loopNestingPattern $ fst nest+        localScope (typeEnvFromDistAcc acc') $ do+          postStm =<< f nest outerpat arr+          return acc'+    _ -> addStmToAcc bnd acc+  where+    perfectlyMapped arr (outer, nest)+      | [(p, arr')] <- loopNestingParamsAndArrs outer,+        arr == arr' =+        case nest of+          [] -> True+          x : xs -> perfectlyMapped (paramName p) (x, xs)+      | otherwise =+        False++distribute ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  DistAcc lore ->+  DistNestT lore m (DistAcc lore)+distribute acc =+  fromMaybe acc <$> distributeIfPossible acc++mkSegLevel ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  DistNestT lore m (MkSegLevel lore (DistNestT lore m))+mkSegLevel = do+  mk_lvl <- asks distSegLevel+  return $ \w desc r -> do+    (lvl, stms) <- lift $ liftInner $ runBinderT' $ mk_lvl w desc r+    addStms stms+    return lvl++distributeIfPossible ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  DistAcc lore ->+  DistNestT lore m (Maybe (DistAcc lore))+distributeIfPossible acc = do+  nest <- asks distNest+  mk_lvl <- mkSegLevel+  tryDistribute mk_lvl nest (distTargets acc) (distStms acc) >>= \case+    Nothing -> return Nothing+    Just (targets, kernel) -> do+      postStm kernel+      return $+        Just+          DistAcc+            { distTargets = targets,+              distStms = mempty+            }++distributeSingleStm ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  DistAcc lore ->+  Stm SOACS ->+  DistNestT+    lore+    m+    ( Maybe+        ( PostStms lore,+          Result,+          KernelNest,+          DistAcc lore+        )+    )+distributeSingleStm acc bnd = do+  nest <- asks distNest+  mk_lvl <- mkSegLevel+  tryDistribute mk_lvl nest (distTargets acc) (distStms acc) >>= \case+    Nothing -> return Nothing+    Just (targets, distributed_bnds) ->+      tryDistributeStm nest targets bnd >>= \case+        Nothing -> return Nothing+        Just (res, targets', new_kernel_nest) ->+          return $+            Just+              ( PostStms distributed_bnds,+                res,+                new_kernel_nest,+                DistAcc+                  { distTargets = targets',+                    distStms = mempty+                  }+              )++segmentedScatterKernel ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  KernelNest ->+  [Int] ->+  PatternT Type ->+  Certificates ->+  SubExp ->+  Lambda lore ->+  [VName] ->+  [(SubExp, Int, VName)] ->+  DistNestT lore m (Stms lore)+segmentedScatterKernel nest perm scatter_pat cs scatter_w lam ivs dests = do+  -- We replicate some of the checking done by 'isSegmentedOp', but+  -- things are different because a scatter is not a reduction or+  -- scan.+  --+  -- First, pretend that the scatter is also part of the nesting.  The+  -- KernelNest we produce here is technically not sensible, but it's+  -- good enough for flatKernel to work.+  let nesting =+        MapNesting scatter_pat (StmAux cs mempty ()) scatter_w $ zip (lambdaParams lam) ivs+      nest' =+        pushInnerKernelNesting (scatter_pat, bodyResult $ lambdaBody lam) nesting nest+  (ispace, kernel_inps) <- flatKernel nest'++  let (as_ws, as_ns, as) = unzip3 dests++  -- The input/output arrays ('as') _must_ correspond to some kernel+  -- input, or else the original nested scatter would have been+  -- ill-typed.  Find them.+  as_inps <- mapM (findInput kernel_inps) as++  mk_lvl <- mkSegLevel++  let rts =+        concatMap (take 1) $+          chunks as_ns $+            drop (sum as_ns) $ lambdaReturnType lam+      (is, vs) = splitAt (sum as_ns) $ bodyResult $ lambdaBody lam++  -- Maybe add certificates to the indices.+  (is', k_body_stms) <- runBinder $ do+    addStms $ bodyStms $ lambdaBody lam+    forM is $ \i ->+      if cs == mempty+        then return i+        else certifying cs $ letSubExp "scatter_i" $ BasicOp $ SubExp i++  let k_body =+        KernelBody () k_body_stms $+          map (inPlaceReturn ispace) $+            zip3 as_ws as_inps $ chunks as_ns $ zip is' vs++  (k, k_bnds) <- mapKernel mk_lvl ispace kernel_inps rts k_body++  traverse renameStm <=< runBinder_ $ do+    addStms k_bnds++    let pat =+          Pattern [] $+            rearrangeShape perm $+              patternValueElements $ loopNestingPattern $ fst nest++    letBind pat $ Op $ segOp k+  where+    findInput kernel_inps a =+      maybe bad return $ find ((== a) . kernelInputName) kernel_inps+    bad = error "Ill-typed nested scatter encountered."++    inPlaceReturn ispace (aw, inp, is_vs) =+      WriteReturns+        (init ws ++ [aw])+        (kernelInputArray inp)+        [(map DimFix $ map Var (init gtids) ++ [i], v) | (i, v) <- is_vs]+      where+        (gtids, ws) = unzip ispace++segmentedUpdateKernel ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  KernelNest ->+  [Int] ->+  Certificates ->+  VName ->+  Slice SubExp ->+  VName ->+  DistNestT lore m (Stms lore)+segmentedUpdateKernel nest perm cs arr slice v = do+  (base_ispace, kernel_inps) <- flatKernel nest+  let slice_dims = sliceDims slice+  slice_gtids <- replicateM (length slice_dims) (newVName "gtid_slice")++  let ispace = base_ispace ++ zip slice_gtids slice_dims++  ((res_t, res), kstms) <- runBinder $ do+    -- Compute indexes into full array.+    v' <-+      certifying cs $+        letSubExp "v" $ BasicOp $ Index v $ map (DimFix . Var) slice_gtids+    slice_is <-+      traverse (toSubExp "index") $+        fixSlice (map (fmap pe64) slice) $ map (pe64 . Var) slice_gtids++    let write_is = map (Var . fst) base_ispace ++ slice_is+        arr' =+          maybe (error "incorrectly typed Update") kernelInputArray $+            find ((== arr) . kernelInputName) kernel_inps+    arr_t <- lookupType arr'+    v_t <- subExpType v'+    return+      ( v_t,+        WriteReturns (arrayDims arr_t) arr' [(map DimFix write_is, v')]+      )++  mk_lvl <- mkSegLevel+  (k, prestms) <-+    mapKernel mk_lvl ispace kernel_inps [res_t] $+      KernelBody () kstms [res]++  traverse renameStm <=< runBinder_ $ do+    addStms prestms++    let pat =+          Pattern [] $+            rearrangeShape perm $+              patternValueElements $ loopNestingPattern $ fst nest++    letBind pat $ Op $ segOp k++segmentedGatherKernel ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  KernelNest ->+  Certificates ->+  VName ->+  Slice SubExp ->+  DistNestT lore m (Stms lore)+segmentedGatherKernel nest cs arr slice = do+  let slice_dims = sliceDims slice+  slice_gtids <- replicateM (length slice_dims) (newVName "gtid_slice")++  (base_ispace, kernel_inps) <- flatKernel nest+  let ispace = base_ispace ++ zip slice_gtids slice_dims++  ((res_t, res), kstms) <- runBinder $ do+    -- Compute indexes into full array.+    slice'' <-+      subExpSlice $+        sliceSlice (primExpSlice slice) $+          primExpSlice $ map (DimFix . Var) slice_gtids+    v' <- certifying cs $ letSubExp "v" $ BasicOp $ Index arr slice''+    v_t <- subExpType v'+    return (v_t, Returns ResultMaySimplify v')++  mk_lvl <- mkSegLevel+  (k, prestms) <-+    mapKernel mk_lvl ispace kernel_inps [res_t] $+      KernelBody () kstms [res]++  traverse renameStm <=< runBinder_ $ do+    addStms prestms++    let pat = Pattern [] $ patternValueElements $ loopNestingPattern $ fst nest++    letBind pat $ Op $ segOp k++segmentedHistKernel ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  KernelNest ->+  [Int] ->+  Certificates ->+  SubExp ->+  [SOACS.HistOp SOACS] ->+  Lambda lore ->+  [VName] ->+  DistNestT lore m (Stms lore)+segmentedHistKernel nest perm cs hist_w ops lam arrs = do+  -- We replicate some of the checking done by 'isSegmentedOp', but+  -- things are different because a Hist is not a reduction or+  -- scan.+  (ispace, inputs) <- flatKernel nest+  let orig_pat =+        Pattern [] $+          rearrangeShape perm $+            patternValueElements $ loopNestingPattern $ fst nest++  -- The input/output arrays _must_ correspond to some kernel input,+  -- or else the original nested Hist would have been ill-typed.+  -- Find them.+  ops' <- forM ops $ \(SOACS.HistOp num_bins rf dests nes op) ->+    SOACS.HistOp num_bins rf+      <$> mapM (fmap kernelInputArray . findInput inputs) dests+      <*> pure nes+      <*> pure op++  mk_lvl <- asks distSegLevel+  onLambda <- asks distOnSOACSLambda+  let onLambda' = fmap fst . runBinder . onLambda+  liftInner $+    runBinderT'_ $ do+      -- It is important not to launch unnecessarily many threads for+      -- histograms, because it may mean we unnecessarily need to reduce+      -- subhistograms as well.+      lvl <- mk_lvl (hist_w : map snd ispace) "seghist" $ NoRecommendation SegNoVirt+      addStms+        =<< histKernel onLambda' lvl orig_pat ispace inputs cs hist_w ops' lam arrs+  where+    findInput kernel_inps a =+      maybe bad return $ find ((== a) . kernelInputName) kernel_inps+    bad = error "Ill-typed nested Hist encountered."++histKernel ::+  (MonadBinder m, DistLore (Lore m)) =>+  (Lambda SOACS -> m (Lambda (Lore m))) ->+  SegOpLevel (Lore m) ->+  PatternT Type ->+  [(VName, SubExp)] ->+  [KernelInput] ->+  Certificates ->+  SubExp ->+  [SOACS.HistOp SOACS] ->+  Lambda (Lore m) ->+  [VName] ->+  m (Stms (Lore m))+histKernel onLambda lvl orig_pat ispace inputs cs hist_w ops lam arrs = runBinderT'_ $ do+  ops' <- forM ops $ \(SOACS.HistOp num_bins rf dests nes op) -> do+    (op', nes', shape) <- determineReduceOp op nes+    op'' <- lift $ onLambda op'+    return $ HistOp num_bins rf dests nes' shape op''++  let isDest = flip elem $ concatMap histDest ops'+      inputs' = filter (not . isDest . kernelInputArray) inputs++  certifying cs $+    addStms =<< traverse renameStm+      =<< segHist lvl orig_pat hist_w ispace inputs' ops' lam arrs++determineReduceOp ::+  (MonadBinder m, Lore m ~ lore) =>+  Lambda SOACS ->+  [SubExp] ->+  m (Lambda SOACS, [SubExp], Shape)+determineReduceOp lam nes =+  -- FIXME? We are assuming that the accumulator is a replicate, and+  -- we fish out its value in a gross way.+  case mapM subExpVar nes of+    Just ne_vs' -> do+      let (shape, lam') = isVectorMap lam+      nes' <- forM ne_vs' $ \ne_v -> do+        ne_v_t <- lookupType ne_v+        letSubExp "hist_ne" $+          BasicOp $+            Index ne_v $+              fullSlice ne_v_t $+                replicate (shapeRank shape) $ DimFix $ intConst Int64 0+      return (lam', nes', shape)+    Nothing ->+      return (lam, nes, mempty)++isVectorMap :: Lambda SOACS -> (Shape, Lambda SOACS)+isVectorMap lam+  | [Let (Pattern [] pes) _ (Op (Screma w form arrs))] <-+      stmsToList $ bodyStms $ lambdaBody lam,+    bodyResult (lambdaBody lam) == map (Var . patElemName) pes,+    Just map_lam <- isMapSOAC form,+    arrs == map paramName (lambdaParams lam) =+    let (shape, lam') = isVectorMap map_lam+     in (Shape [w] <> shape, lam')+  | otherwise = (mempty, lam)++segmentedScanomapKernel ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  KernelNest ->+  [Int] ->+  SubExp ->+  Lambda lore ->+  Lambda lore ->+  [SubExp] ->+  [VName] ->+  DistNestT lore m (Maybe (Stms lore))+segmentedScanomapKernel nest perm segment_size lam map_lam nes arrs = do+  mk_lvl <- asks distSegLevel+  isSegmentedOp nest perm (freeIn lam) (freeIn map_lam) nes [] $+    \pat ispace inps nes' _ -> do+      let scan_op = SegBinOp Noncommutative lam nes' mempty+      lvl <- mk_lvl (segment_size : map snd ispace) "segscan" $ NoRecommendation SegNoVirt+      addStms =<< traverse renameStm+        =<< segScan lvl pat segment_size [scan_op] map_lam arrs ispace inps++regularSegmentedRedomapKernel ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  KernelNest ->+  [Int] ->+  SubExp ->+  Commutativity ->+  Lambda lore ->+  Lambda lore ->+  [SubExp] ->+  [VName] ->+  DistNestT lore m (Maybe (Stms lore))+regularSegmentedRedomapKernel nest perm segment_size comm lam map_lam nes arrs = do+  mk_lvl <- asks distSegLevel+  isSegmentedOp nest perm (freeIn lam) (freeIn map_lam) nes [] $+    \pat ispace inps nes' _ -> do+      let red_op = SegBinOp comm lam nes' mempty+      lvl <- mk_lvl (segment_size : map snd ispace) "segred" $ NoRecommendation SegNoVirt+      addStms =<< traverse renameStm+        =<< segRed lvl pat segment_size [red_op] map_lam arrs ispace inps++isSegmentedOp ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  KernelNest ->+  [Int] ->+  Names ->+  Names ->+  [SubExp] ->+  [VName] ->+  ( PatternT Type ->+    [(VName, SubExp)] ->+    [KernelInput] ->+    [SubExp] ->+    [VName] ->+    BinderT lore m ()+  ) ->+  DistNestT lore m (Maybe (Stms lore))+isSegmentedOp nest perm free_in_op _free_in_fold_op nes arrs m = runMaybeT $ do+  -- We must verify that array inputs to the operation are inputs to+  -- the outermost loop nesting or free in the loop nest.  Nothing+  -- free in the op may be bound by the nest.  Furthermore, the+  -- neutral elements must be free in the loop nest.+  --+  -- We must summarise any names from free_in_op that are bound in the+  -- nest, and describe how to obtain them given segment indices.++  let bound_by_nest = boundInKernelNest nest++  (ispace, kernel_inps) <- flatKernel nest++  when (free_in_op `namesIntersect` bound_by_nest) $+    fail "Non-fold lambda uses nest-bound parameters."++  let indices = map fst ispace++      prepareNe (Var v)+        | v `nameIn` bound_by_nest =+          fail "Neutral element bound in nest"+      prepareNe ne = return ne++      prepareArr arr =+        case find ((== arr) . kernelInputName) kernel_inps of+          Just inp+            | kernelInputIndices inp == map Var indices ->+              return $ return $ kernelInputArray inp+          Nothing+            | not (arr `nameIn` bound_by_nest) ->+              -- This input is something that is free inside+              -- the loop nesting. We will have to replicate+              -- it.+              return $+                letExp+                  (baseString arr ++ "_repd")+                  (BasicOp $ Replicate (Shape $ map snd ispace) $ Var arr)+          _ ->+            fail "Input not free, perfectly mapped, or outermost."++  nes' <- mapM prepareNe nes++  mk_arrs <- mapM prepareArr arrs++  lift $+    liftInner $+      runBinderT'_ $ do+        nested_arrs <- sequence mk_arrs++        let pat =+              Pattern [] $+                rearrangeShape perm $+                  patternValueElements $ loopNestingPattern $ fst nest++        m pat ispace kernel_inps nes' nested_arrs++permutationAndMissing :: PatternT Type -> [SubExp] -> Maybe ([Int], [PatElemT Type])+permutationAndMissing pat res = do+  let pes = patternValueElements pat+      (_used, unused) =+        partition ((`nameIn` freeIn res) . patElemName) pes+      res_expanded = res ++ map (Var . patElemName) unused+  perm <- map (Var . patElemName) pes `isPermutationOf` res_expanded+  return (perm, unused)++-- Add extra pattern elements to every kernel nesting level.+expandKernelNest ::+  MonadFreshNames m =>+  [PatElemT Type] ->+  KernelNest ->+  m KernelNest+expandKernelNest pes (outer_nest, inner_nests) = do+  let outer_size =+        loopNestingWidth outer_nest :+        map loopNestingWidth inner_nests+      inner_sizes = tails $ map loopNestingWidth inner_nests+  outer_nest' <- expandWith outer_nest outer_size+  inner_nests' <- zipWithM expandWith inner_nests inner_sizes+  return (outer_nest', inner_nests')+  where+    expandWith nest dims = do+      pes' <- mapM (expandPatElemWith dims) pes+      return+        nest+          { loopNestingPattern =+              Pattern [] $+                patternElements (loopNestingPattern nest) <> pes'+          }++    expandPatElemWith dims pe = do+      name <- newVName $ baseString $ patElemName pe+      return+        pe+          { patElemName = name,+            patElemDec = patElemType pe `arrayOfShape` Shape dims+          }++kernelOrNot ::+  (MonadFreshNames m, DistLore lore) =>+  Certificates ->+  Stm SOACS ->+  DistAcc lore ->+  PostStms lore ->+  DistAcc lore ->+  Maybe (Stms lore) ->+  DistNestT lore m (DistAcc lore)+kernelOrNot cs bnd acc _ _ Nothing =+  addStmToAcc (certify cs bnd) acc+kernelOrNot cs _ _ kernels acc' (Just bnds) = do+  addPostStms kernels+  postStm $ fmap (certify cs) bnds+  return acc'++distributeMap ::+  (MonadFreshNames m, LocalScope lore m, DistLore lore) =>+  MapLoop ->+  DistAcc lore ->+  DistNestT lore m (DistAcc lore)+distributeMap (MapLoop pat aux w lam arrs) acc =+  distribute+    =<< mapNesting+      pat+      aux+      w+      lam+      arrs+      (distribute =<< distributeMapBodyStms acc' lam_bnds)+  where+    acc' =+      DistAcc+        { distTargets =+            pushInnerTarget+              (pat, bodyResult $ lambdaBody lam)+              $ distTargets acc,+          distStms = mempty+        }++    lam_bnds = bodyStms $ lambdaBody lam
src/Futhark/Pass/ExtractKernels/Distribution.hs view
@@ -1,67 +1,67 @@+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TupleSections #-} {-# LANGUAGE LambdaCase #-}-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ConstraintKinds #-}-module Futhark.Pass.ExtractKernels.Distribution-       (-         Target-       , Targets-       , ppTargets-       , singleTarget-       , outerTarget-       , innerTarget-       , pushInnerTarget-       , popInnerTarget-       , targetsScope--       , LoopNesting (..)-       , ppLoopNesting-       , scopeOfLoopNesting--       , Nesting (..)-       , Nestings-       , ppNestings-       , letBindInInnerNesting-       , singleNesting-       , pushInnerNesting--       , KernelNest-       , ppKernelNest-       , newKernel-       , innermostKernelNesting-       , pushKernelNesting-       , pushInnerKernelNesting-       , kernelNestLoops-       , kernelNestWidths-       , boundInKernelNest-       , boundInKernelNests-       , flatKernel-       , constructKernel+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-} -       , tryDistribute-       , tryDistributeStm-       )-       where+module Futhark.Pass.ExtractKernels.Distribution+  ( Target,+    Targets,+    ppTargets,+    singleTarget,+    outerTarget,+    innerTarget,+    pushInnerTarget,+    popInnerTarget,+    targetsScope,+    LoopNesting (..),+    ppLoopNesting,+    scopeOfLoopNesting,+    Nesting (..),+    Nestings,+    ppNestings,+    letBindInInnerNesting,+    singleNesting,+    pushInnerNesting,+    KernelNest,+    ppKernelNest,+    newKernel,+    innermostKernelNesting,+    pushKernelNesting,+    pushInnerKernelNesting,+    kernelNestLoops,+    kernelNestWidths,+    boundInKernelNest,+    boundInKernelNests,+    flatKernel,+    constructKernel,+    tryDistribute,+    tryDistributeStm,+  )+where  import Control.Monad.RWS.Strict import Control.Monad.Trans.Maybe-import qualified Data.Map.Strict as M import Data.Foldable-import Data.Maybe import Data.List (elemIndex, sortOn)-+import qualified Data.Map.Strict as M+import Data.Maybe import Futhark.IR import Futhark.IR.SegOp import Futhark.MonadFreshNames+import Futhark.Pass.ExtractKernels.BlockedKernel+  ( DistLore,+    KernelInput (..),+    MkSegLevel,+    mapKernel,+    readKernelInput,+  ) import Futhark.Tools-import Futhark.Util import Futhark.Transform.Rename+import Futhark.Util import Futhark.Util.Log-import Futhark.Pass.ExtractKernels.BlockedKernel-  (DistLore, mapKernel, KernelInput(..), readKernelInput, MkSegLevel)  type Target = (PatternT Type, Result) @@ -70,15 +70,17 @@ -- element of a pattern must be present as the result of the -- immediately enclosing target.  This is ensured by 'pushInnerTarget' -- by removing unused pattern elements.-data Targets = Targets { _innerTarget :: Target-                       , _outerTargets :: [Target]-                       }+data Targets = Targets+  { _innerTarget :: Target,+    _outerTargets :: [Target]+  }  ppTargets :: Targets -> String ppTargets (Targets target targets) =   unlines $ map ppTarget $ targets ++ [target]-  where ppTarget (pat, res) =-          pretty pat ++ " <- " ++ pretty res+  where+    ppTarget (pat, res) =+      pretty pat ++ " <- " ++ pretty res  singleTarget :: Target -> Targets singleTarget = flip Targets []@@ -97,16 +99,17 @@ pushInnerTarget :: Target -> Targets -> Targets pushInnerTarget (pat, res) (Targets inner_target targets) =   Targets (pat', res') (targets ++ [inner_target])-  where (pes', res') = unzip $ filter (used . fst) $ zip (patternElements pat) res-        pat' = Pattern [] pes'-        inner_used = freeIn $ snd inner_target-        used pe = patElemName pe `nameIn` inner_used+  where+    (pes', res') = unzip $ filter (used . fst) $ zip (patternElements pat) res+    pat' = Pattern [] pes'+    inner_used = freeIn $ snd inner_target+    used pe = patElemName pe `nameIn` inner_used  popInnerTarget :: Targets -> Maybe (Target, Targets) popInnerTarget (Targets t ts) =   case reverse ts of-    x:xs -> Just (t, Targets x $ reverse xs)-    []   -> Nothing+    x : xs -> Just (t, Targets x $ reverse xs)+    [] -> Nothing  targetScope :: DistLore lore => Target -> Scope lore targetScope = scopeOfPattern . fst@@ -114,50 +117,53 @@ targetsScope :: DistLore lore => Targets -> Scope lore targetsScope (Targets t ts) = mconcat $ map targetScope $ t : ts -data LoopNesting = MapNesting { loopNestingPattern :: PatternT Type-                              , loopNestingAux :: StmAux ()-                              , loopNestingWidth :: SubExp-                              , loopNestingParamsAndArrs :: [(Param Type, VName)]-                              }-                 deriving (Show)+data LoopNesting = MapNesting+  { loopNestingPattern :: PatternT Type,+    loopNestingAux :: StmAux (),+    loopNestingWidth :: SubExp,+    loopNestingParamsAndArrs :: [(Param Type, VName)]+  }+  deriving (Show)  scopeOfLoopNesting :: DistLore lore => LoopNesting -> Scope lore scopeOfLoopNesting = scopeOfLParams . map fst . loopNestingParamsAndArrs  ppLoopNesting :: LoopNesting -> String ppLoopNesting (MapNesting _ _ _ params_and_arrs) =-  pretty (map fst params_and_arrs) ++-  " <- " ++-  pretty (map snd params_and_arrs)+  pretty (map fst params_and_arrs)+    ++ " <- "+    ++ pretty (map snd params_and_arrs)  loopNestingParams :: LoopNesting -> [Param Type]-loopNestingParams  = map fst . loopNestingParamsAndArrs+loopNestingParams = map fst . loopNestingParamsAndArrs  instance FreeIn LoopNesting where   freeIn' (MapNesting pat aux w params_and_arrs) =-    freeIn' pat <>-    freeIn' aux <>-    freeIn' w <>-    freeIn' params_and_arrs+    freeIn' pat+      <> freeIn' aux+      <> freeIn' w+      <> freeIn' params_and_arrs -data Nesting = Nesting { nestingLetBound :: Names-                       , nestingLoop :: LoopNesting-                       }-             deriving (Show)+data Nesting = Nesting+  { nestingLetBound :: Names,+    nestingLoop :: LoopNesting+  }+  deriving (Show)  letBindInNesting :: Names -> Nesting -> Nesting letBindInNesting newnames (Nesting oldnames loop) =   Nesting (oldnames <> newnames) loop- -- ^ First pair element is the very innermost ("current") nest.  In -- the list, the outermost nest comes first.+ type Nestings = (Nesting, [Nesting])  ppNestings :: Nestings -> String ppNestings (nesting, nestings) =   unlines $ map ppNesting $ nestings ++ [nesting]-  where ppNesting (Nesting _ loop) =-          ppLoopNesting loop+  where+    ppNesting (Nesting _ loop) =+      ppLoopNesting loop  singleNesting :: Nesting -> Nestings singleNesting = (,[])@@ -169,15 +175,15 @@ -- | Both parameters and let-bound. boundInNesting :: Nesting -> Names boundInNesting nesting =-  namesFromList (map paramName (loopNestingParams loop)) <>-  nestingLetBound nesting-  where loop = nestingLoop nesting+  namesFromList (map paramName (loopNestingParams loop))+    <> nestingLetBound nesting+  where+    loop = nestingLoop nesting  letBindInInnerNesting :: Names -> Nestings -> Nestings letBindInInnerNesting names (nest, nestings) =   (letBindInNesting names nest, nestings) - -- | Note: first element is *outermost* nesting.  This is different -- from the similar types elsewhere! type KernelNest = (LoopNesting, [LoopNesting])@@ -195,8 +201,9 @@ -- list, also taking care to swap patterns if necessary. pushKernelNesting :: Target -> LoopNesting -> KernelNest -> KernelNest pushKernelNesting target newnest (nest, nests) =-  (fixNestingPatternOrder newnest target (loopNestingPattern nest),-   nest : nests)+  ( fixNestingPatternOrder newnest target (loopNestingPattern nest),+    nest : nests+  )  -- | Add new innermost nesting, pushing the current outermost to the -- list.  It is important that the 'Target' has the right order@@ -204,18 +211,20 @@ pushInnerKernelNesting :: Target -> LoopNesting -> KernelNest -> KernelNest pushInnerKernelNesting target newnest (nest, nests) =   (nest, nests ++ [fixNestingPatternOrder newnest target (loopNestingPattern innermost)])-  where innermost = case reverse nests of-          []  -> nest-          n:_ -> n+  where+    innermost = case reverse nests of+      [] -> nest+      n : _ -> n  fixNestingPatternOrder :: LoopNesting -> Target -> PatternT Type -> LoopNesting-fixNestingPatternOrder nest (_,res) inner_pat =-  nest { loopNestingPattern = basicPattern [] pat' }-  where pat = loopNestingPattern nest-        pat' = map fst fixed_target-        fixed_target = sortOn posInInnerPat $ zip (patternValueIdents pat) res-        posInInnerPat (_, Var v) = fromMaybe 0 $ elemIndex v $ patternNames inner_pat-        posInInnerPat _          = 0+fixNestingPatternOrder nest (_, res) inner_pat =+  nest {loopNestingPattern = basicPattern [] pat'}+  where+    pat = loopNestingPattern nest+    pat' = map fst fixed_target+    fixed_target = sortOn posInInnerPat $ zip (patternValueIdents pat) res+    posInInnerPat (_, Var v) = fromMaybe 0 $ elemIndex v $ patternNames inner_pat+    posInInnerPat _ = 0  newKernel :: LoopNesting -> KernelNest newKernel nest = (nest, [])@@ -227,17 +236,23 @@ boundInKernelNest = mconcat . boundInKernelNests  boundInKernelNests :: KernelNest -> [Names]-boundInKernelNests = map (namesFromList .-                          map (paramName . fst) .-                          loopNestingParamsAndArrs) .-                     kernelNestLoops+boundInKernelNests =+  map+    ( namesFromList+        . map (paramName . fst)+        . loopNestingParamsAndArrs+    )+    . kernelNestLoops  kernelNestWidths :: KernelNest -> [SubExp] kernelNestWidths = map loopNestingWidth . kernelNestLoops -constructKernel :: (DistLore lore, MonadFreshNames m, LocalScope lore m) =>-                   MkSegLevel lore m -> KernelNest -> Body lore-                -> m (Stm lore, Stms lore)+constructKernel ::+  (DistLore lore, MonadFreshNames m, LocalScope lore m) =>+  MkSegLevel lore m ->+  KernelNest ->+  Body lore ->+  m (Stm lore, Stms lore) constructKernel mk_lvl kernel_nest inner_body = runBinderT' $ do   (ispace, inps) <- flatKernel kernel_nest   let aux = loopNestingAux first_nest@@ -245,10 +260,11 @@       pat = loopNestingPattern first_nest       rts = map (stripArray (length ispace)) $ patternTypes pat -  inner_body' <- fmap (uncurry (flip (KernelBody ()))) $ runBinder $-                 localScope ispace_scope $ do-    mapM_ readKernelInput $ filter inputIsUsed inps-    map (Returns ResultMaySimplify) <$> bodyBind inner_body+  inner_body' <- fmap (uncurry (flip (KernelBody ()))) $+    runBinder $+      localScope ispace_scope $ do+        mapM_ readKernelInput $ filter inputIsUsed inps+        map (Returns ResultMaySimplify) <$> bodyBind inner_body    (segop, aux_stms) <- lift $ mapKernel mk_lvl ispace [] rts inner_body' @@ -264,260 +280,323 @@ --  (1) The index space. -- --  (2) The kernel inputs - note that some of these may be unused.-flatKernel :: MonadFreshNames m =>-              KernelNest-           -> m ([(VName, SubExp)],-                 [KernelInput])+flatKernel ::+  MonadFreshNames m =>+  KernelNest ->+  m+    ( [(VName, SubExp)],+      [KernelInput]+    ) flatKernel (MapNesting _ _ nesting_w params_and_arrs, []) = do   i <- newVName "gtid"-  let inps = [ KernelInput pname ptype arr [Var i] |-               (Param pname ptype, arr) <- params_and_arrs ]-  return ([(i,nesting_w)], inps)-+  let inps =+        [ KernelInput pname ptype arr [Var i]+          | (Param pname ptype, arr) <- params_and_arrs+        ]+  return ([(i, nesting_w)], inps) flatKernel (MapNesting _ _ nesting_w params_and_arrs, nest : nests) = do   i <- newVName "gtid"   (ispace, inps) <- flatKernel (nest, nests)    let inps' = map fixupInput inps       isParam inp =-        snd <$> find ((==kernelInputArray inp) . paramName . fst) params_and_arrs+        snd <$> find ((== kernelInputArray inp) . paramName . fst) params_and_arrs       fixupInput inp         | Just arr <- isParam inp =-            inp { kernelInputArray = arr-                , kernelInputIndices = Var i : kernelInputIndices inp }+          inp+            { kernelInputArray = arr,+              kernelInputIndices = Var i : kernelInputIndices inp+            }         | otherwise =-            inp+          inp    return ((i, nesting_w) : ispace, extra_inps i <> inps')-  where extra_inps i =-          [ KernelInput pname ptype arr [Var i] |-            (Param pname ptype, arr) <- params_and_arrs ]+  where+    extra_inps i =+      [ KernelInput pname ptype arr [Var i]+        | (Param pname ptype, arr) <- params_and_arrs+      ]  -- | Description of distribution to do.-data DistributionBody = DistributionBody {-    distributionTarget :: Targets-  , distributionFreeInBody :: Names-  , distributionIdentityMap :: M.Map VName Ident-  , distributionExpandTarget :: Target -> Target-    -- ^ Also related to avoiding identity mapping.+data DistributionBody = DistributionBody+  { distributionTarget :: Targets,+    distributionFreeInBody :: Names,+    distributionIdentityMap :: M.Map VName Ident,+    -- | Also related to avoiding identity mapping.+    distributionExpandTarget :: Target -> Target   }  distributionInnerPattern :: DistributionBody -> PatternT Type distributionInnerPattern = fst . innerTarget . distributionTarget -distributionBodyFromStms :: ASTLore lore =>-                            Targets -> Stms lore -> (DistributionBody, Result)+distributionBodyFromStms ::+  ASTLore lore =>+  Targets ->+  Stms lore ->+  (DistributionBody, Result) distributionBodyFromStms (Targets (inner_pat, inner_res) targets) stms =   let bound_by_stms = namesFromList $ M.keys $ scopeOf stms       (inner_pat', inner_res', inner_identity_map, inner_expand_target) =         removeIdentityMappingGeneral bound_by_stms inner_pat inner_res-  in (DistributionBody-      { distributionTarget = Targets (inner_pat', inner_res') targets-      , distributionFreeInBody = foldMap freeIn stms `namesSubtract` bound_by_stms-      , distributionIdentityMap = inner_identity_map-      , distributionExpandTarget = inner_expand_target-      },-      inner_res')+   in ( DistributionBody+          { distributionTarget = Targets (inner_pat', inner_res') targets,+            distributionFreeInBody = foldMap freeIn stms `namesSubtract` bound_by_stms,+            distributionIdentityMap = inner_identity_map,+            distributionExpandTarget = inner_expand_target+          },+        inner_res'+      ) -distributionBodyFromStm :: ASTLore lore =>-                           Targets -> Stm lore -> (DistributionBody, Result)+distributionBodyFromStm ::+  ASTLore lore =>+  Targets ->+  Stm lore ->+  (DistributionBody, Result) distributionBodyFromStm targets bnd =   distributionBodyFromStms targets $ oneStm bnd -createKernelNest :: (MonadFreshNames m, HasScope t m) =>-                    Nestings-                 -> DistributionBody-                 -> m (Maybe (Targets, KernelNest))+createKernelNest ::+  (MonadFreshNames m, HasScope t m) =>+  Nestings ->+  DistributionBody ->+  m (Maybe (Targets, KernelNest)) createKernelNest (inner_nest, nests) distrib_body = do   let Targets target targets = distributionTarget distrib_body   unless (length nests == length targets) $-    error $ "Nests and targets do not match!\n" ++-    "nests: " ++ ppNestings (inner_nest, nests) ++-    "\ntargets:" ++ ppTargets (Targets target targets)+    error $+      "Nests and targets do not match!\n"+        ++ "nests: "+        ++ ppNestings (inner_nest, nests)+        ++ "\ntargets:"+        ++ ppTargets (Targets target targets)   runMaybeT $ fmap prepare $ recurse $ zip nests targets+  where+    prepare (x, _, z) = (z, x)+    bound_in_nest =+      mconcat $ map boundInNesting $ inner_nest : nests -  where prepare (x, _, z) = (z, x)-        bound_in_nest =-          mconcat $ map boundInNesting $ inner_nest : nests-        -- | Can something of this type be taken outside the nest?-        -- I.e. are none of its dimensions bound inside the nest.-        distributableType =-          (==mempty) . namesIntersection bound_in_nest . freeIn . arrayDims+    distributableType =+      (== mempty) . namesIntersection bound_in_nest . freeIn . arrayDims -        distributeAtNesting :: (HasScope t m, MonadFreshNames m) =>-                               Nesting-                            -> PatternT Type-                            -> (LoopNesting -> KernelNest, Names)-                            -> M.Map VName Ident-                            -> [Ident]-                            -> (Target -> Targets)-                            -> MaybeT m (KernelNest, Names, Targets)-        distributeAtNesting-          (Nesting nest_let_bound nest)-          pat-          (add_to_kernel, free_in_kernel)-          identity_map-          inner_returned_arrs-          addTarget = do-          let nest'@(MapNesting _ aux w params_and_arrs) =-                removeUnusedNestingParts free_in_kernel nest-              (params,arrs) = unzip params_and_arrs-              param_names = namesFromList $ map paramName params-              free_in_kernel' =-                (freeIn nest' <> free_in_kernel) `namesSubtract` param_names-              required_from_nest =-                free_in_kernel' `namesIntersection` nest_let_bound+    distributeAtNesting ::+      (HasScope t m, MonadFreshNames m) =>+      Nesting ->+      PatternT Type ->+      (LoopNesting -> KernelNest, Names) ->+      M.Map VName Ident ->+      [Ident] ->+      (Target -> Targets) ->+      MaybeT m (KernelNest, Names, Targets)+    distributeAtNesting+      (Nesting nest_let_bound nest)+      pat+      (add_to_kernel, free_in_kernel)+      identity_map+      inner_returned_arrs+      addTarget = do+        let nest'@(MapNesting _ aux w params_and_arrs) =+              removeUnusedNestingParts free_in_kernel nest+            (params, arrs) = unzip params_and_arrs+            param_names = namesFromList $ map paramName params+            free_in_kernel' =+              (freeIn nest' <> free_in_kernel) `namesSubtract` param_names+            required_from_nest =+              free_in_kernel' `namesIntersection` nest_let_bound -          required_from_nest_idents <--            forM (namesToList required_from_nest) $ \name -> do-              t <- lift $ lookupType name-              return $ Ident name t+        required_from_nest_idents <-+          forM (namesToList required_from_nest) $ \name -> do+            t <- lift $ lookupType name+            return $ Ident name t -          (free_params, free_arrs, bind_in_target) <--            fmap unzip3 $-            forM (inner_returned_arrs++required_from_nest_idents) $-            \(Ident pname ptype) ->-              case M.lookup pname identity_map of-                Nothing -> do-                  arr <- newIdent (baseString pname ++ "_r") $-                         arrayOfRow ptype w-                  return (Param pname ptype,-                          arr,-                          True)-                Just arr ->-                  return (Param pname ptype,-                          arr,-                          False)+        (free_params, free_arrs, bind_in_target) <-+          fmap unzip3 $+            forM (inner_returned_arrs ++ required_from_nest_idents) $+              \(Ident pname ptype) ->+                case M.lookup pname identity_map of+                  Nothing -> do+                    arr <-+                      newIdent (baseString pname ++ "_r") $+                        arrayOfRow ptype w+                    return+                      ( Param pname ptype,+                        arr,+                        True+                      )+                  Just arr ->+                    return+                      ( Param pname ptype,+                        arr,+                        False+                      ) -          let free_arrs_pat =-                basicPattern [] $ map snd $-                filter fst $ zip bind_in_target free_arrs-              free_params_pat =-                map snd $ filter fst $ zip bind_in_target free_params+        let free_arrs_pat =+              basicPattern [] $+                map snd $+                  filter fst $ zip bind_in_target free_arrs+            free_params_pat =+              map snd $ filter fst $ zip bind_in_target free_params -              (actual_params, actual_arrs) =-                (params++free_params,-                 arrs++map identName free_arrs)-              actual_param_names =-                namesFromList $ map paramName actual_params+            (actual_params, actual_arrs) =+              ( params ++ free_params,+                arrs ++ map identName free_arrs+              )+            actual_param_names =+              namesFromList $ map paramName actual_params -              nest'' =-                removeUnusedNestingParts free_in_kernel $+            nest'' =+              removeUnusedNestingParts free_in_kernel $                 MapNesting pat aux w $ zip actual_params actual_arrs -              free_in_kernel'' =-                (freeIn nest'' <> free_in_kernel) `namesSubtract` actual_param_names--          unless (all (distributableType . paramType) $-                  loopNestingParams nest'') $-            fail "Would induce irregular array"-          return (add_to_kernel nest'',--                  free_in_kernel'',--                  addTarget (free_arrs_pat, map (Var . paramName) free_params_pat))+            free_in_kernel'' =+              (freeIn nest'' <> free_in_kernel) `namesSubtract` actual_param_names -        recurse :: (HasScope t m, MonadFreshNames m) =>-                   [(Nesting,Target)]-                -> MaybeT m (KernelNest, Names, Targets)-        recurse [] =-          distributeAtNesting-          inner_nest-          (distributionInnerPattern distrib_body)-          (newKernel,-           distributionFreeInBody distrib_body `namesIntersection` bound_in_nest)-          (distributionIdentityMap distrib_body)-          [] $-          singleTarget . distributionExpandTarget distrib_body+        unless+          ( all (distributableType . paramType) $+              loopNestingParams nest''+          )+          $ fail "Would induce irregular array"+        return+          ( add_to_kernel nest'',+            free_in_kernel'',+            addTarget (free_arrs_pat, map (Var . paramName) free_params_pat)+          ) -        recurse ((nest, (pat,res)) : nests') = do-          (kernel@(outer, _), kernel_free, kernel_targets) <- recurse nests'+    recurse ::+      (HasScope t m, MonadFreshNames m) =>+      [(Nesting, Target)] ->+      MaybeT m (KernelNest, Names, Targets)+    recurse [] =+      distributeAtNesting+        inner_nest+        (distributionInnerPattern distrib_body)+        ( newKernel,+          distributionFreeInBody distrib_body `namesIntersection` bound_in_nest+        )+        (distributionIdentityMap distrib_body)+        []+        $ singleTarget . distributionExpandTarget distrib_body+    recurse ((nest, (pat, res)) : nests') = do+      (kernel@(outer, _), kernel_free, kernel_targets) <- recurse nests' -          let (pat', res', identity_map, expand_target) =-                removeIdentityMappingFromNesting-                (namesFromList $ patternNames $ loopNestingPattern outer) pat res+      let (pat', res', identity_map, expand_target) =+            removeIdentityMappingFromNesting+              (namesFromList $ patternNames $ loopNestingPattern outer)+              pat+              res -          distributeAtNesting-            nest-            pat'-            (\k -> pushKernelNesting (pat',res') k kernel,-             kernel_free)-            identity_map-            (patternIdents $ fst $ outerTarget kernel_targets)-            ((`pushOuterTarget` kernel_targets) . expand_target)+      distributeAtNesting+        nest+        pat'+        ( \k -> pushKernelNesting (pat', res') k kernel,+          kernel_free+        )+        identity_map+        (patternIdents $ fst $ outerTarget kernel_targets)+        ((`pushOuterTarget` kernel_targets) . expand_target)  removeUnusedNestingParts :: Names -> LoopNesting -> LoopNesting removeUnusedNestingParts used (MapNesting pat aux w params_and_arrs) =   MapNesting pat aux w $ zip used_params used_arrs-  where (params,arrs) = unzip params_and_arrs-        (used_params, used_arrs) =-          unzip $-          filter ((`nameIn` used) . paramName . fst) $+  where+    (params, arrs) = unzip params_and_arrs+    (used_params, used_arrs) =+      unzip $+        filter ((`nameIn` used) . paramName . fst) $           zip params arrs -removeIdentityMappingGeneral :: Names -> PatternT Type -> Result-                             -> (PatternT Type,-                                 Result,-                                 M.Map VName Ident,-                                 Target -> Target)+removeIdentityMappingGeneral ::+  Names ->+  PatternT Type ->+  Result ->+  ( PatternT Type,+    Result,+    M.Map VName Ident,+    Target -> Target+  ) removeIdentityMappingGeneral bound pat res =   let (identities, not_identities) =         mapEither isIdentity $ zip (patternElements pat) res       (not_identity_patElems, not_identity_res) = unzip not_identities       (identity_patElems, identity_res) = unzip identities       expandTarget (tpat, tres) =-        (Pattern [] $ patternElements tpat ++ identity_patElems,-         tres ++ map Var identity_res)-      identity_map = M.fromList $ zip identity_res $-                      map patElemIdent identity_patElems-  in (Pattern [] not_identity_patElems,-      not_identity_res,-      identity_map,-      expandTarget)-  where isIdentity (patElem, Var v)-          | not (v `nameIn` bound) = Left (patElem, v)-        isIdentity x               = Right x+        ( Pattern [] $ patternElements tpat ++ identity_patElems,+          tres ++ map Var identity_res+        )+      identity_map =+        M.fromList $+          zip identity_res $+            map patElemIdent identity_patElems+   in ( Pattern [] not_identity_patElems,+        not_identity_res,+        identity_map,+        expandTarget+      )+  where+    isIdentity (patElem, Var v)+      | not (v `nameIn` bound) = Left (patElem, v)+    isIdentity x = Right x -removeIdentityMappingFromNesting :: Names -> PatternT Type -> Result-                                 -> (PatternT Type,-                                     Result,-                                     M.Map VName Ident,-                                     Target -> Target)+removeIdentityMappingFromNesting ::+  Names ->+  PatternT Type ->+  Result ->+  ( PatternT Type,+    Result,+    M.Map VName Ident,+    Target -> Target+  ) removeIdentityMappingFromNesting bound_in_nesting pat res =   let (pat', res', identity_map, expand_target) =         removeIdentityMappingGeneral bound_in_nesting pat res-  in (pat', res', identity_map, expand_target)+   in (pat', res', identity_map, expand_target) -tryDistribute :: (DistLore lore, MonadFreshNames m,-                  LocalScope lore m, MonadLogger m) =>-                 MkSegLevel lore m -> Nestings -> Targets -> Stms lore-              -> m (Maybe (Targets, Stms lore))-tryDistribute _ _ targets stms | null stms =-  -- No point in distributing an empty kernel.-  return $ Just (targets, mempty)+tryDistribute ::+  ( DistLore lore,+    MonadFreshNames m,+    LocalScope lore m,+    MonadLogger m+  ) =>+  MkSegLevel lore m ->+  Nestings ->+  Targets ->+  Stms lore ->+  m (Maybe (Targets, Stms lore))+tryDistribute _ _ targets stms+  | null stms =+    -- No point in distributing an empty kernel.+    return $ Just (targets, mempty) tryDistribute mk_lvl nest targets stms =-  createKernelNest nest dist_body >>=-  \case-    Just (targets', distributed) -> do-      (kernel_bnd, w_bnds) <--        localScope (targetsScope targets') $-        constructKernel mk_lvl distributed $ mkBody stms inner_body_res-      distributed' <- renameStm kernel_bnd-      logMsg $ "distributing\n" ++-        unlines (map pretty $ stmsToList stms) ++-        pretty (snd $ innerTarget targets) ++-        "\nas\n" ++ pretty distributed' ++-        "\ndue to targets\n" ++ ppTargets targets ++-        "\nand with new targets\n" ++ ppTargets targets'-      return $ Just (targets', w_bnds <> oneStm distributed')-    Nothing ->-      return Nothing-  where (dist_body, inner_body_res) = distributionBodyFromStms targets stms+  createKernelNest nest dist_body+    >>= \case+      Just (targets', distributed) -> do+        (kernel_bnd, w_bnds) <-+          localScope (targetsScope targets') $+            constructKernel mk_lvl distributed $ mkBody stms inner_body_res+        distributed' <- renameStm kernel_bnd+        logMsg $+          "distributing\n"+            ++ unlines (map pretty $ stmsToList stms)+            ++ pretty (snd $ innerTarget targets)+            ++ "\nas\n"+            ++ pretty distributed'+            ++ "\ndue to targets\n"+            ++ ppTargets targets+            ++ "\nand with new targets\n"+            ++ ppTargets targets'+        return $ Just (targets', w_bnds <> oneStm distributed')+      Nothing ->+        return Nothing+  where+    (dist_body, inner_body_res) = distributionBodyFromStms targets stms -tryDistributeStm :: (MonadFreshNames m, HasScope t m, ASTLore lore) =>-                    Nestings -> Targets -> Stm lore-                 -> m (Maybe (Result, Targets, KernelNest))+tryDistributeStm ::+  (MonadFreshNames m, HasScope t m, ASTLore lore) =>+  Nestings ->+  Targets ->+  Stm lore ->+  m (Maybe (Result, Targets, KernelNest)) tryDistributeStm nest targets bnd =   fmap addRes <$> createKernelNest nest dist_body-  where (dist_body, res) = distributionBodyFromStm targets bnd-        addRes (targets', kernel_nest) = (res, targets', kernel_nest)+  where+    (dist_body, res) = distributionBodyFromStm targets bnd+    addRes (targets', kernel_nest) = (res, targets', kernel_nest)
src/Futhark/Pass/ExtractKernels/ISRWIM.hs view
@@ -1,124 +1,152 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}+ -- | Interchanging scans with inner maps. module Futhark.Pass.ExtractKernels.ISRWIM-       ( iswim-       , irwim-       , rwimPossible-       )-       where+  ( iswim,+    irwim,+    rwimPossible,+  )+where  import Control.Arrow (first) import Control.Monad.State--import Futhark.MonadFreshNames import Futhark.IR.SOACS+import Futhark.MonadFreshNames import Futhark.Tools  -- | Interchange Scan With Inner Map. Tries to turn a @scan(map)@ into a -- @map(scan)-iswim :: (MonadBinder m, Lore m ~ SOACS) =>-         Pattern-      -> SubExp-      -> Lambda-      -> [(SubExp, VName)]-      -> Maybe (m ())+iswim ::+  (MonadBinder m, Lore m ~ SOACS) =>+  Pattern ->+  SubExp ->+  Lambda ->+  [(SubExp, VName)] ->+  Maybe (m ()) iswim res_pat w scan_fun scan_input   | Just (map_pat, map_cs, map_w, map_fun) <- rwimPossible scan_fun = Just $ do-      let (accs, arrs) = unzip scan_input-      arrs' <- transposedArrays arrs-      accs' <- mapM (letExp "acc" . BasicOp . SubExp) accs+    let (accs, arrs) = unzip scan_input+    arrs' <- transposedArrays arrs+    accs' <- mapM (letExp "acc" . BasicOp . SubExp) accs -      let map_arrs' = accs' ++ arrs'-          (scan_acc_params, scan_elem_params) =-            splitAt (length arrs) $ lambdaParams scan_fun-          map_params = map removeParamOuterDim scan_acc_params ++-                       map (setParamOuterDimTo w) scan_elem_params-          map_rettype = map (setOuterDimTo w) $ lambdaReturnType scan_fun+    let map_arrs' = accs' ++ arrs'+        (scan_acc_params, scan_elem_params) =+          splitAt (length arrs) $ lambdaParams scan_fun+        map_params =+          map removeParamOuterDim scan_acc_params+            ++ map (setParamOuterDimTo w) scan_elem_params+        map_rettype = map (setOuterDimTo w) $ lambdaReturnType scan_fun -          scan_params = lambdaParams map_fun-          scan_body = lambdaBody map_fun-          scan_rettype = lambdaReturnType map_fun-          scan_fun' = Lambda scan_params scan_body scan_rettype-          scan_input' = map (first Var) $-                        uncurry zip $ splitAt (length arrs') $ map paramName map_params-          (nes', scan_arrs) = unzip scan_input'+        scan_params = lambdaParams map_fun+        scan_body = lambdaBody map_fun+        scan_rettype = lambdaReturnType map_fun+        scan_fun' = Lambda scan_params scan_body scan_rettype+        scan_input' =+          map (first Var) $+            uncurry zip $ splitAt (length arrs') $ map paramName map_params+        (nes', scan_arrs) = unzip scan_input' -      scan_soac <- scanSOAC [Scan scan_fun' nes']-      let map_body = mkBody (oneStm $ Let (setPatternOuterDimTo w map_pat) (defAux ()) $-                             Op $ Screma w scan_soac scan_arrs) $-                            map Var $ patternNames map_pat-          map_fun' = Lambda map_params map_body map_rettype+    scan_soac <- scanSOAC [Scan scan_fun' nes']+    let map_body =+          mkBody+            ( oneStm $+                Let (setPatternOuterDimTo w map_pat) (defAux ()) $+                  Op $ Screma w scan_soac scan_arrs+            )+            $ map Var $ patternNames map_pat+        map_fun' = Lambda map_params map_body map_rettype -      res_pat' <- fmap (basicPattern []) $-                  mapM (newIdent' (<>"_transposed") . transposeIdentType) $-                  patternValueIdents res_pat+    res_pat' <-+      fmap (basicPattern []) $+        mapM (newIdent' (<> "_transposed") . transposeIdentType) $+          patternValueIdents res_pat -      addStm $ Let res_pat' (StmAux map_cs mempty ()) $ Op $ Screma map_w-        (mapSOAC map_fun') map_arrs'+    addStm $+      Let res_pat' (StmAux map_cs mempty ()) $+        Op $+          Screma+            map_w+            (mapSOAC map_fun')+            map_arrs' -      forM_ (zip (patternValueIdents res_pat)-                 (patternValueIdents res_pat')) $ \(to, from) -> do-        let perm = [1,0] ++ [2..arrayRank (identType from)-1]-        addStm $ Let (basicPattern [] [to]) (defAux ()) $-                     BasicOp $ Rearrange perm $ identName from+    forM_+      ( zip+          (patternValueIdents res_pat)+          (patternValueIdents res_pat')+      )+      $ \(to, from) -> do+        let perm = [1, 0] ++ [2 .. arrayRank (identType from) -1]+        addStm $+          Let (basicPattern [] [to]) (defAux ()) $+            BasicOp $ Rearrange perm $ identName from   | otherwise = Nothing  -- | Interchange Reduce With Inner Map. Tries to turn a @reduce(map)@ into a -- @map(reduce)-irwim :: (MonadBinder m, Lore m ~ SOACS) =>-         Pattern-      -> SubExp-      -> Commutativity -> Lambda-      -> [(SubExp, VName)]-      -> Maybe (m ())+irwim ::+  (MonadBinder m, Lore m ~ SOACS) =>+  Pattern ->+  SubExp ->+  Commutativity ->+  Lambda ->+  [(SubExp, VName)] ->+  Maybe (m ()) irwim res_pat w comm red_fun red_input   | Just (map_pat, map_cs, map_w, map_fun) <- rwimPossible red_fun = Just $ do-      let (accs, arrs) = unzip red_input-      arrs' <- transposedArrays arrs-      -- FIXME?  Can we reasonably assume that the accumulator is a-      -- replicate?  We also assume that it is non-empty.-      let indexAcc (Var v) = do-            v_t <- lookupType v-            letSubExp "acc" $ BasicOp $ Index v $-              fullSlice v_t [DimFix $ intConst Int32 0]-          indexAcc Constant{} =-            error "irwim: array accumulator is a constant."-      accs' <- mapM indexAcc accs+    let (accs, arrs) = unzip red_input+    arrs' <- transposedArrays arrs+    -- FIXME?  Can we reasonably assume that the accumulator is a+    -- replicate?  We also assume that it is non-empty.+    let indexAcc (Var v) = do+          v_t <- lookupType v+          letSubExp "acc" $+            BasicOp $+              Index v $+                fullSlice v_t [DimFix $ intConst Int64 0]+        indexAcc Constant {} =+          error "irwim: array accumulator is a constant."+    accs' <- mapM indexAcc accs -      let (_red_acc_params, red_elem_params) =-            splitAt (length arrs) $ lambdaParams red_fun-          map_rettype = map rowType $ lambdaReturnType red_fun-          map_params = map (setParamOuterDimTo w) red_elem_params+    let (_red_acc_params, red_elem_params) =+          splitAt (length arrs) $ lambdaParams red_fun+        map_rettype = map rowType $ lambdaReturnType red_fun+        map_params = map (setParamOuterDimTo w) red_elem_params -          red_params = lambdaParams map_fun-          red_body = lambdaBody map_fun-          red_rettype = lambdaReturnType map_fun-          red_fun' = Lambda red_params red_body red_rettype-          red_input' = zip accs' $ map paramName map_params-          red_pat = stripPatternOuterDim map_pat+        red_params = lambdaParams map_fun+        red_body = lambdaBody map_fun+        red_rettype = lambdaReturnType map_fun+        red_fun' = Lambda red_params red_body red_rettype+        red_input' = zip accs' $ map paramName map_params+        red_pat = stripPatternOuterDim map_pat -      map_body <--        case irwim red_pat w comm red_fun' red_input' of-          Nothing -> do-            reduce_soac <- reduceSOAC [Reduce comm red_fun' $ map fst red_input']-            return $ mkBody (oneStm $ Let red_pat (defAux ()) $-                              Op $ Screma w reduce_soac $ map snd red_input') $-              map Var $ patternNames map_pat-          Just m -> localScope (scopeOfLParams map_params) $ do-            map_body_bnds <- collectStms_ m-            return $ mkBody map_body_bnds $ map Var $ patternNames map_pat+    map_body <-+      case irwim red_pat w comm red_fun' red_input' of+        Nothing -> do+          reduce_soac <- reduceSOAC [Reduce comm red_fun' $ map fst red_input']+          return $+            mkBody+              ( oneStm $+                  Let red_pat (defAux ()) $+                    Op $ Screma w reduce_soac $ map snd red_input'+              )+              $ map Var $ patternNames map_pat+        Just m -> localScope (scopeOfLParams map_params) $ do+          map_body_bnds <- collectStms_ m+          return $ mkBody map_body_bnds $ map Var $ patternNames map_pat -      let map_fun' = Lambda map_params map_body map_rettype+    let map_fun' = Lambda map_params map_body map_rettype -      addStm $ Let res_pat (StmAux map_cs mempty ()) $+    addStm $+      Let res_pat (StmAux map_cs mempty ()) $         Op $ Screma map_w (mapSOAC map_fun') arrs'   | otherwise = Nothing  -- | Does this reduce operator contain an inner map, and if so, what -- does that map look like?-rwimPossible :: Lambda-             -> Maybe (Pattern, Certificates, SubExp, Lambda)+rwimPossible ::+  Lambda ->+  Maybe (Pattern, Certificates, SubExp, Lambda) rwimPossible fun   | Body _ stms res <- lambdaBody fun,     [bnd] <- stmsToList stms, -- Body has a single binding@@ -127,30 +155,30 @@     Op (Screma map_w form map_arrs) <- stmExp bnd,     Just map_fun <- isMapSOAC form,     map paramName (lambdaParams fun) == map_arrs =-      Just (map_pat, stmCerts bnd, map_w, map_fun)+    Just (map_pat, stmCerts bnd, map_w, map_fun)   | otherwise =-      Nothing+    Nothing  transposedArrays :: MonadBinder m => [VName] -> m [VName] transposedArrays arrs = forM arrs $ \arr -> do   t <- lookupType arr-  let perm = [1,0] ++ [2..arrayRank t-1]+  let perm = [1, 0] ++ [2 .. arrayRank t -1]   letExp (baseString arr) $ BasicOp $ Rearrange perm arr  removeParamOuterDim :: LParam -> LParam removeParamOuterDim param =   let t = rowType $ paramType param-  in param { paramDec = t }+   in param {paramDec = t}  setParamOuterDimTo :: SubExp -> LParam -> LParam setParamOuterDimTo w param =   let t = setOuterDimTo w $ paramType param-  in param { paramDec = t }+   in param {paramDec = t}  setIdentOuterDimTo :: SubExp -> Ident -> Ident setIdentOuterDimTo w ident =   let t = setOuterDimTo w $ identType ident-  in ident { identType = t }+   in ident {identType = t}  setOuterDimTo :: SubExp -> Type -> Type setOuterDimTo w t =@@ -162,11 +190,11 @@  transposeIdentType :: Ident -> Ident transposeIdentType ident =-  ident { identType = transposeType $ identType ident }+  ident {identType = transposeType $ identType ident}  stripIdentOuterDim :: Ident -> Ident stripIdentOuterDim ident =-  ident { identType = rowType $ identType ident }+  ident {identType = rowType $ identType ident}  stripPatternOuterDim :: Pattern -> Pattern stripPatternOuterDim pat =
src/Futhark/Pass/ExtractKernels/Interchange.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}+ -- | It is well known that fully parallel loops can always be -- interchanged inwards with a sequential loop.  This module -- implements that transformation.@@ -7,23 +8,25 @@ -- This is also where we implement loop-switching (for branches), -- which is semantically similar to interchange. module Futhark.Pass.ExtractKernels.Interchange-       (-         SeqLoop (..)-       , interchangeLoops-       , Branch (..)-       , interchangeBranch-       ) where+  ( SeqLoop (..),+    interchangeLoops,+    Branch (..),+    interchangeBranch,+  )+where  import Control.Monad.RWS.Strict-import Data.Maybe import Data.List (find)--import Futhark.Pass.ExtractKernels.Distribution-  (LoopNesting(..), KernelNest, kernelNestLoops)+import Data.Maybe import Futhark.IR.SOACS import Futhark.MonadFreshNames-import Futhark.Transform.Rename+import Futhark.Pass.ExtractKernels.Distribution+  ( KernelNest,+    LoopNesting (..),+    kernelNestLoops,+  ) import Futhark.Tools+import Futhark.Transform.Rename  -- | An encoding of a sequential do-loop with no existential context, -- alongside its result pattern.@@ -33,21 +36,26 @@ seqLoopStm (SeqLoop _ pat merge form body) =   Let pat (defAux ()) $ DoLoop [] merge form body -interchangeLoop :: (MonadBinder m, LocalScope SOACS m) =>-                   (VName -> Maybe VName) -> SeqLoop -> LoopNesting-                -> m SeqLoop+interchangeLoop ::+  (MonadBinder m, LocalScope SOACS m) =>+  (VName -> Maybe VName) ->+  SeqLoop ->+  LoopNesting ->+  m SeqLoop interchangeLoop   isMapParameter   (SeqLoop perm loop_pat merge form body)   (MapNesting pat aux w params_and_arrs) = do     merge_expanded <-       localScope (scopeOfLParams $ map fst params_and_arrs) $-      mapM expand merge+        mapM expand merge      let loop_pat_expanded =           Pattern [] $ map expandPatElem $ patternElements loop_pat-        new_params = [ Param pname $ fromDecl ptype-                     | (Param pname ptype, _) <- merge ]+        new_params =+          [ Param pname $ fromDecl ptype+            | (Param pname ptype, _) <- merge+          ]         new_arrs = map (paramName . fst) merge_expanded         rettype = map rowType $ patternTypes loop_pat_expanded @@ -57,59 +65,68 @@     -- it is not used anymore.  This might happen if the parameter was     -- used just as the inital value of a merge parameter.     ((params', arrs'), pre_copy_bnds) <--      runBinder $ localScope (scopeOfLParams new_params) $-      unzip . catMaybes <$> mapM copyOrRemoveParam params_and_arrs+      runBinder $+        localScope (scopeOfLParams new_params) $+          unzip . catMaybes <$> mapM copyOrRemoveParam params_and_arrs -    let lam = Lambda (params'<>new_params) body rettype-        map_bnd = Let loop_pat_expanded aux $-                  Op $ Screma w (mapSOAC lam) $ arrs' <> new_arrs+    let lam = Lambda (params' <> new_params) body rettype+        map_bnd =+          Let loop_pat_expanded aux $+            Op $ Screma w (mapSOAC lam) $ arrs' <> new_arrs         res = map Var $ patternNames loop_pat_expanded         pat' = Pattern [] $ rearrangeShape perm $ patternValueElements pat      return $       SeqLoop perm pat' merge_expanded form $-      mkBody (pre_copy_bnds<>oneStm map_bnd) res-  where free_in_body = freeIn body+        mkBody (pre_copy_bnds <> oneStm map_bnd) res+    where+      free_in_body = freeIn body -        copyOrRemoveParam (param, arr)-          | not (paramName param `nameIn` free_in_body) =-            return Nothing-          | otherwise =-            return $ Just (param, arr)+      copyOrRemoveParam (param, arr)+        | not (paramName param `nameIn` free_in_body) =+          return Nothing+        | otherwise =+          return $ Just (param, arr) -        expandedInit _ (Var v)-          | Just arr <- isMapParameter v =-              return $ Var arr-        expandedInit param_name se =-          letSubExp (param_name <> "_expanded_init") $-            BasicOp $ Replicate (Shape [w]) se+      expandedInit _ (Var v)+        | Just arr <- isMapParameter v =+          return $ Var arr+      expandedInit param_name se =+        letSubExp (param_name <> "_expanded_init") $+          BasicOp $ Replicate (Shape [w]) se -        expand (merge_param, merge_init) = do-          expanded_param <--            newParam (param_name <> "_expanded") $+      expand (merge_param, merge_init) = do+        expanded_param <-+          newParam (param_name <> "_expanded") $             arrayOf (paramDeclType merge_param) (Shape [w]) $-            uniqueness $ declTypeOf merge_param-          expanded_init <- expandedInit param_name merge_init-          return (expanded_param, expanded_init)-            where param_name = baseString $ paramName merge_param+              uniqueness $ declTypeOf merge_param+        expanded_init <- expandedInit param_name merge_init+        return (expanded_param, expanded_init)+        where+          param_name = baseString $ paramName merge_param -        expandPatElem (PatElem name t) =-          PatElem name $ arrayOfRow t w+      expandPatElem (PatElem name t) =+        PatElem name $ arrayOfRow t w  -- | Given a (parallel) map nesting and an inner sequential loop, move -- the maps inside the sequential loop.  The result is several -- statements - one of these will be the loop, which will then contain -- statements with @map@ expressions.-interchangeLoops :: (MonadFreshNames m, HasScope SOACS m) =>-                    KernelNest -> SeqLoop-                 -> m (Stms SOACS)+interchangeLoops ::+  (MonadFreshNames m, HasScope SOACS m) =>+  KernelNest ->+  SeqLoop ->+  m (Stms SOACS) interchangeLoops nest loop = do   (loop', bnds) <--    runBinder $ foldM (interchangeLoop isMapParameter) loop $-    reverse $ kernelNestLoops nest+    runBinder $+      foldM (interchangeLoop isMapParameter) loop $+        reverse $ kernelNestLoops nest   return $ bnds <> oneStm (seqLoopStm loop')-  where isMapParameter v =-          fmap snd $ find ((==v) . paramName . fst) $+  where+    isMapParameter v =+      fmap snd $+        find ((== v) . paramName . fst) $           concatMap loopNestingParamsAndArrs $ kernelNestLoops nest  data Branch = Branch [Int] Pattern SubExp Body Body (IfDec (BranchType SOACS))@@ -118,8 +135,11 @@ branchStm (Branch _ pat cond tbranch fbranch ret) =   Let pat (defAux ()) $ If cond tbranch fbranch ret -interchangeBranch1 :: (MonadBinder m) =>-                      Branch -> LoopNesting -> m Branch+interchangeBranch1 ::+  (MonadBinder m) =>+  Branch ->+  LoopNesting ->+  m Branch interchangeBranch1   (Branch perm branch_pat cond tbranch fbranch (IfDec ret if_sort))   (MapNesting pat aux w params_and_arrs) = do@@ -132,7 +152,7 @@         branch_pat' =           Pattern [] $ map (fmap (`arrayOfRow` w)) $ patternElements branch_pat -        mkBranch branch = (renameBody=<<) $ do+        mkBranch branch = (renameBody =<<) $ do           let lam = Lambda params branch lam_ret               res = map Var $ patternNames branch_pat'               map_bnd = Let branch_pat' aux $ Op $ Screma w (mapSOAC lam) arrs@@ -140,11 +160,15 @@      tbranch' <- mkBranch tbranch     fbranch' <- mkBranch fbranch-    return $ Branch [0..patternSize pat-1] pat' cond tbranch' fbranch' $-      IfDec ret' if_sort+    return $+      Branch [0 .. patternSize pat -1] pat' cond tbranch' fbranch' $+        IfDec ret' if_sort -interchangeBranch :: (MonadFreshNames m, HasScope SOACS m) =>-                     KernelNest -> Branch -> m (Stms SOACS)+interchangeBranch ::+  (MonadFreshNames m, HasScope SOACS m) =>+  KernelNest ->+  Branch ->+  m (Stms SOACS) interchangeBranch nest loop = do   (loop', bnds) <-     runBinder $ foldM interchangeBranch1 loop $ reverse $ kernelNestLoops nest
src/Futhark/Pass/ExtractKernels/Intragroup.hs view
@@ -1,34 +1,31 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}+ -- | Extract limited nested parallelism for execution inside -- individual kernel workgroups.-module Futhark.Pass.ExtractKernels.Intragroup-  (intraGroupParallelise)-where+module Futhark.Pass.ExtractKernels.Intragroup (intraGroupParallelise) where  import Control.Monad.Identity import Control.Monad.RWS import Control.Monad.Trans.Maybe import qualified Data.Map.Strict as M import qualified Data.Set as S--import Prelude hiding (log)- import Futhark.Analysis.PrimExp.Convert-import Futhark.IR.SOACS import qualified Futhark.IR.Kernels as Out import Futhark.IR.Kernels.Kernel hiding (HistOp)+import Futhark.IR.SOACS import Futhark.MonadFreshNames-import Futhark.Tools+import Futhark.Pass.ExtractKernels.BlockedKernel import Futhark.Pass.ExtractKernels.DistributeNests import Futhark.Pass.ExtractKernels.Distribution-import Futhark.Pass.ExtractKernels.BlockedKernel import Futhark.Pass.ExtractKernels.ToKernels+import Futhark.Tools import qualified Futhark.Transform.FirstOrderTransform as FOT import Futhark.Util (chunks) import Futhark.Util.Log+import Prelude hiding (log)  -- | Convert the statements inside a map nest to kernel statements, -- attempting to parallelise any remaining (top-level) parallel@@ -42,16 +39,27 @@ -- -- We distinguish between "minimum group size" and "maximum -- exploitable parallelism".-intraGroupParallelise :: (MonadFreshNames m, LocalScope Out.Kernels m) =>-                         KernelNest -> Lambda-                      -> m (Maybe ((SubExp, SubExp), SubExp, Log,-                                   Out.Stms Out.Kernels, Out.Stms Out.Kernels))+intraGroupParallelise ::+  (MonadFreshNames m, LocalScope Out.Kernels m) =>+  KernelNest ->+  Lambda ->+  m+    ( Maybe+        ( (SubExp, SubExp),+          SubExp,+          Log,+          Out.Stms Out.Kernels,+          Out.Stms Out.Kernels+        )+    ) intraGroupParallelise knest lam = runMaybeT $ do   (ispace, inps) <- lift $ flatKernel knest -  (num_groups, w_stms) <- lift $ runBinder $-    letSubExp "intra_num_groups" =<<-    foldBinOp (Mul Int32 OverflowUndef) (intConst Int32 1) (map snd ispace)+  (num_groups, w_stms) <-+    lift $+      runBinder $+        letSubExp "intra_num_groups"+          =<< foldBinOp (Mul Int64 OverflowUndef) (intConst Int64 1) (map snd ispace)    let body = lambdaBody lam @@ -59,66 +67,80 @@   let intra_lvl = SegThread (Count num_groups) (Count $ Var group_size) SegNoVirt    (wss_min, wss_avail, log, kbody) <--    lift $ localScope (scopeOfLParams $ lambdaParams lam) $-    intraGroupParalleliseBody intra_lvl body+    lift $+      localScope (scopeOfLParams $ lambdaParams lam) $+        intraGroupParalleliseBody intra_lvl body    outside_scope <- lift askScope   -- outside_scope may also contain the inputs, even though those are   -- not actually available outside the kernel.   let available v =-        v `M.member` outside_scope &&-        v `notElem` map kernelInputName inps+        v `M.member` outside_scope+          && v `notElem` map kernelInputName inps   unless (all available $ namesToList $ freeIn (wss_min ++ wss_avail)) $     fail "Irregular parallelism" -  ((intra_avail_par, kspace, read_input_stms), prelude_stms) <- lift $ runBinder $ do-    let foldBinOp' _    []    = eSubExp $ intConst Int32 0-        foldBinOp' bop (x:xs) = foldBinOp bop x xs-    ws_min <- mapM (letSubExp "one_intra_par_min" <=< foldBinOp' (Mul Int32 OverflowUndef)) $-              filter (not . null) wss_min-    ws_avail <- mapM (letSubExp "one_intra_par_avail" <=< foldBinOp' (Mul Int32 OverflowUndef)) $-                filter (not . null) wss_avail+  ((intra_avail_par, kspace, read_input_stms), prelude_stms) <- lift $+    runBinder $ do+      let foldBinOp' _ [] = eSubExp $ intConst Int64 0+          foldBinOp' bop (x : xs) = foldBinOp bop x xs+      ws_min <-+        mapM (letSubExp "one_intra_par_min" <=< foldBinOp' (Mul Int64 OverflowUndef)) $+          filter (not . null) wss_min+      ws_avail <-+        mapM (letSubExp "one_intra_par_avail" <=< foldBinOp' (Mul Int64 OverflowUndef)) $+          filter (not . null) wss_avail -    -- The amount of parallelism available *in the worst case* is-    -- equal to the smallest parallel loop.-    intra_avail_par <- letSubExp "intra_avail_par" =<< foldBinOp' (SMin Int32) ws_avail+      -- The amount of parallelism available *in the worst case* is+      -- equal to the smallest parallel loop.+      intra_avail_par <- letSubExp "intra_avail_par" =<< foldBinOp' (SMin Int64) ws_avail -    -- The group size is either the maximum of the minimum parallelism-    -- exploited, or the desired parallelism (bounded by the max group-    -- size) in case there is no minimum.-    letBindNames [group_size] =<<-      if null ws_min-      then eBinOp (SMin Int32)-           (eSubExp =<< letSubExp "max_group_size" (Op $ SizeOp $ Out.GetSizeMax Out.SizeGroup))-           (eSubExp intra_avail_par)-      else foldBinOp' (SMax Int32) ws_min+      -- The group size is either the maximum of the minimum parallelism+      -- exploited, or the desired parallelism (bounded by the max group+      -- size) in case there is no minimum.+      letBindNames [group_size]+        =<< if null ws_min+          then+            eBinOp+              (SMin Int64)+              (eSubExp =<< letSubExp "max_group_size" (Op $ SizeOp $ Out.GetSizeMax Out.SizeGroup))+              (eSubExp intra_avail_par)+          else foldBinOp' (SMax Int64) ws_min -    let inputIsUsed input = kernelInputName input `nameIn` freeIn body-        used_inps = filter inputIsUsed inps+      let inputIsUsed input = kernelInputName input `nameIn` freeIn body+          used_inps = filter inputIsUsed inps -    addStms w_stms-    read_input_stms <- runBinder_ $ mapM readKernelInput used_inps-    space <- mkSegSpace ispace-    return (intra_avail_par, space, read_input_stms)+      addStms w_stms+      read_input_stms <- runBinder_ $ mapM readKernelInput used_inps+      space <- mkSegSpace ispace+      return (intra_avail_par, space, read_input_stms) -  let kbody' = kbody { kernelBodyStms = read_input_stms <> kernelBodyStms kbody }+  let kbody' = kbody {kernelBodyStms = read_input_stms <> kernelBodyStms kbody}    let nested_pat = loopNestingPattern first_nest       rts = map (length ispace `stripArray`) $ patternTypes nested_pat       lvl = SegGroup (Count num_groups) (Count $ Var group_size) SegNoVirt-      kstm = Let nested_pat aux $-             Op $ SegOp $ SegMap lvl kspace rts kbody'+      kstm =+        Let nested_pat aux $+          Op $ SegOp $ SegMap lvl kspace rts kbody'    let intra_min_par = intra_avail_par-  return ((intra_min_par, intra_avail_par), Var group_size, log,-           prelude_stms, oneStm kstm)-  where first_nest = fst knest-        aux = loopNestingAux first_nest+  return+    ( (intra_min_par, intra_avail_par),+      Var group_size,+      log,+      prelude_stms,+      oneStm kstm+    )+  where+    first_nest = fst knest+    aux = loopNestingAux first_nest -data Acc = Acc { accMinPar :: S.Set [SubExp]-               , accAvailPar :: S.Set [SubExp]-               , accLog :: Log-               }+data Acc = Acc+  { accMinPar :: S.Set [SubExp],+    accAvailPar :: S.Set [SubExp],+    accLog :: Log+  }  instance Semigroup Acc where   Acc min_x avail_x log_x <> Acc min_y avail_y log_y =@@ -131,20 +153,25 @@   BinderT Out.Kernels (RWS () Acc VNameSource)  instance MonadLogger IntraGroupM where-  addLog log = tell mempty { accLog = log }+  addLog log = tell mempty {accLog = log} -runIntraGroupM :: (MonadFreshNames m, HasScope Out.Kernels m) =>-                  IntraGroupM () -> m (Acc, Out.Stms Out.Kernels)+runIntraGroupM ::+  (MonadFreshNames m, HasScope Out.Kernels m) =>+  IntraGroupM () ->+  m (Acc, Out.Stms Out.Kernels) runIntraGroupM m = do   scope <- castScope <$> askScope   modifyNameSource $ \src ->     let (((), kstms), src', acc) = runRWS (runBinderT m scope) () src-    in ((acc, kstms), src')+     in ((acc, kstms), src')  parallelMin :: [SubExp] -> IntraGroupM ()-parallelMin ws = tell mempty { accMinPar = S.singleton ws-                             , accAvailPar = S.singleton ws-                             }+parallelMin ws =+  tell+    mempty+      { accMinPar = S.singleton ws,+        accAvailPar = S.singleton ws+      }  intraGroupBody :: SegLevel -> Body -> IntraGroupM (Out.Body Out.Kernels) intraGroupBody lvl body = do@@ -159,71 +186,70 @@   case e of     DoLoop ctx val form loopbody ->       localScope (scopeOf form') $-      localScope (scopeOfFParams $ map fst $ ctx ++ val) $ do-      loopbody' <- intraGroupBody lvl loopbody-      certifying (stmAuxCerts aux) $-        letBind pat $ DoLoop ctx val form' loopbody'-          where form' = case form of-                          ForLoop i it bound inps -> ForLoop i it bound inps-                          WhileLoop cond          -> WhileLoop cond-+        localScope (scopeOfFParams $ map fst $ ctx ++ val) $ do+          loopbody' <- intraGroupBody lvl loopbody+          certifying (stmAuxCerts aux) $+            letBind pat $ DoLoop ctx val form' loopbody'+      where+        form' = case form of+          ForLoop i it bound inps -> ForLoop i it bound inps+          WhileLoop cond -> WhileLoop cond     If cond tbody fbody ifdec -> do       tbody' <- intraGroupBody lvl tbody       fbody' <- intraGroupBody lvl fbody       certifying (stmAuxCerts aux) $         letBind pat $ If cond tbody' fbody' ifdec-     Op soac       | "sequential_outer" `inAttrs` stmAuxAttrs aux ->-          intraGroupStms lvl . fmap (certify (stmAuxCerts aux)) =<<-          runBinder_ (FOT.transformSOAC pat soac)-+        intraGroupStms lvl . fmap (certify (stmAuxCerts aux))+          =<< runBinder_ (FOT.transformSOAC pat soac)     Op (Screma w form arrs)       | Just lam <- isMapSOAC form -> do-      let loopnest = MapNesting pat aux w $ zip (lambdaParams lam) arrs-          env = DistEnv { distNest =-                            singleNesting $ Nesting mempty loopnest-                        , distScope =-                            scopeOfPattern pat <>-                            scopeForKernels (scopeOf lam) <> scope-                        , distOnInnerMap =-                            distributeMap-                        , distOnTopLevelStms =-                            liftInner . collectStms_ . intraGroupStms lvl-                        , distSegLevel = \minw _ _ -> do-                            lift $ parallelMin minw-                            return lvl-                        , distOnSOACSStms =-                            pure . oneStm . soacsStmToKernels-                        , distOnSOACSLambda =-                            pure . soacsLambdaToKernels-                        }-          acc = DistAcc { distTargets = singleTarget (pat, bodyResult $ lambdaBody lam)-                        , distStms = mempty-                        }--      addStms =<<-        runDistNestT env (distributeMapBodyStms acc (bodyStms $ lambdaBody lam))+        let loopnest = MapNesting pat aux w $ zip (lambdaParams lam) arrs+            env =+              DistEnv+                { distNest =+                    singleNesting $ Nesting mempty loopnest,+                  distScope =+                    scopeOfPattern pat+                      <> scopeForKernels (scopeOf lam)+                      <> scope,+                  distOnInnerMap =+                    distributeMap,+                  distOnTopLevelStms =+                    liftInner . collectStms_ . intraGroupStms lvl,+                  distSegLevel = \minw _ _ -> do+                    lift $ parallelMin minw+                    return lvl,+                  distOnSOACSStms =+                    pure . oneStm . soacsStmToKernels,+                  distOnSOACSLambda =+                    pure . soacsLambdaToKernels+                }+            acc =+              DistAcc+                { distTargets = singleTarget (pat, bodyResult $ lambdaBody lam),+                  distStms = mempty+                } +        addStms+          =<< runDistNestT env (distributeMapBodyStms acc (bodyStms $ lambdaBody lam))     Op (Screma w form arrs)       | Just (scans, mapfun) <- isScanomapSOAC form,         Scan scanfun nes <- singleScan scans -> do-      let scanfun' = soacsLambdaToKernels scanfun-          mapfun' = soacsLambdaToKernels mapfun-      certifying (stmAuxCerts aux) $-        addStms =<< segScan lvl' pat w [SegBinOp Noncommutative scanfun' nes mempty] mapfun' arrs [] []-      parallelMin [w]-+        let scanfun' = soacsLambdaToKernels scanfun+            mapfun' = soacsLambdaToKernels mapfun+        certifying (stmAuxCerts aux) $+          addStms =<< segScan lvl' pat w [SegBinOp Noncommutative scanfun' nes mempty] mapfun' arrs [] []+        parallelMin [w]     Op (Screma w form arrs)       | Just (reds, map_lam) <- isRedomapSOAC form,         Reduce comm red_lam nes <- singleReduce reds -> do-      let red_lam' = soacsLambdaToKernels red_lam-          map_lam' = soacsLambdaToKernels map_lam-      certifying (stmAuxCerts aux) $-        addStms =<< segRed lvl' pat w [SegBinOp comm red_lam' nes mempty] map_lam' arrs [] []-      parallelMin [w]--+        let red_lam' = soacsLambdaToKernels red_lam+            map_lam' = soacsLambdaToKernels map_lam+        certifying (stmAuxCerts aux) $+          addStms =<< segRed lvl' pat w [SegBinOp comm red_lam' nes mempty] map_lam' arrs [] []+        parallelMin [w]     Op (Hist w ops bucket_fun arrs) -> do       ops' <- forM ops $ \(HistOp num_bins rf dests nes op) -> do         (op', nes', shape) <- determineReduceOp op nes@@ -234,18 +260,16 @@       certifying (stmAuxCerts aux) $         addStms =<< segHist lvl' pat w [] [] ops' bucket_fun' arrs       parallelMin [w]-     Op (Stream w (Sequential accs) lam arrs)       | chunk_size_param : _ <- lambdaParams lam -> do-      types <- asksScope castScope-      ((), stream_bnds) <--        runBinderT (sequentialStreamWholeArray pat w accs lam arrs) types-      let replace (Var v) | v == paramName chunk_size_param = w-          replace se = se-          replaceSets (Acc x y log) =-            Acc (S.map (map replace) x) (S.map (map replace) y) log-      censor replaceSets $ intraGroupStms lvl stream_bnds-+        types <- asksScope castScope+        ((), stream_bnds) <-+          runBinderT (sequentialStreamWholeArray pat w accs lam arrs) types+        let replace (Var v) | v == paramName chunk_size_param = w+            replace se = se+            replaceSets (Acc x y log) =+              Acc (S.map (map replace) x) (S.map (map replace) y) log+        censor replaceSets $ intraGroupStms lvl stream_bnds     Op (Scatter w lam ivs dests) -> do       write_i <- newVName "write_i"       space <- mkSegSpace [(write_i, w)]@@ -253,14 +277,17 @@       let lam' = soacsLambdaToKernels lam           (dests_ws, dests_ns, dests_vs) = unzip3 dests           (i_res, v_res) = splitAt (sum dests_ns) $ bodyResult $ lambdaBody lam'-          krets = do (a_w, a, is_vs) <- zip3 dests_ws dests_vs $ chunks dests_ns $ zip i_res v_res-                     return $ WriteReturns [a_w] a [ ([DimFix i],v) | (i,v) <- is_vs ]-          inputs = do (p, p_a) <- zip (lambdaParams lam') ivs-                      return $ KernelInput (paramName p) (paramType p) p_a [Var write_i]+          krets = do+            (a_w, a, is_vs) <- zip3 dests_ws dests_vs $ chunks dests_ns $ zip i_res v_res+            return $ WriteReturns [a_w] a [([DimFix i], v) | (i, v) <- is_vs]+          inputs = do+            (p, p_a) <- zip (lambdaParams lam') ivs+            return $ KernelInput (paramName p) (paramType p) p_a [Var write_i] -      kstms <- runBinder_ $ localScope (scopeOfSegSpace space) $ do-        mapM_ readKernelInput inputs-        addStms $ bodyStms $ lambdaBody lam'+      kstms <- runBinder_ $+        localScope (scopeOfSegSpace space) $ do+          mapM_ readKernelInput inputs+          addStms $ bodyStms $ lambdaBody lam'        certifying (stmAuxCerts aux) $ do         let ts = map rowType $ patternTypes pat@@ -268,18 +295,23 @@         letBind pat $ Op $ SegOp $ SegMap lvl' space ts body        parallelMin [w]-     _ ->       addStm $ soacsStmToKernels stm  intraGroupStms :: SegLevel -> Stms SOACS -> IntraGroupM () intraGroupStms lvl = mapM_ (intraGroupStm lvl) -intraGroupParalleliseBody :: (MonadFreshNames m, HasScope Out.Kernels m) =>-                             SegLevel -> Body-                          -> m ([[SubExp]], [[SubExp]], Log, Out.KernelBody Out.Kernels)+intraGroupParalleliseBody ::+  (MonadFreshNames m, HasScope Out.Kernels m) =>+  SegLevel ->+  Body ->+  m ([[SubExp]], [[SubExp]], Log, Out.KernelBody Out.Kernels) intraGroupParalleliseBody lvl body = do   (Acc min_ws avail_ws log, kstms) <-     runIntraGroupM $ intraGroupStms lvl $ bodyStms body-  return (S.toList min_ws, S.toList avail_ws, log,-          KernelBody () kstms $ map (Returns ResultMaySimplify) $ bodyResult body)+  return+    ( S.toList min_ws,+      S.toList avail_ws,+      log,+      KernelBody () kstms $ map (Returns ResultMaySimplify) $ bodyResult body+    )
src/Futhark/Pass/ExtractKernels/StreamKernel.hs view
@@ -1,105 +1,147 @@+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}+ module Futhark.Pass.ExtractKernels.StreamKernel-  ( segThreadCapped-  , streamRed-  , streamMap+  ( segThreadCapped,+    streamRed,+    streamMap,   )-  where+where  import Control.Monad import Control.Monad.Writer import Data.List ()--import Prelude hiding (quot)- import Futhark.Analysis.PrimExp import Futhark.IR-import Futhark.IR.Kernels-       hiding (Prog, Body, Stm, Pattern, PatElem,-               BasicOp, Exp, Lambda, FunDef, FParam, LParam, RetType)+import Futhark.IR.Kernels hiding+  ( BasicOp,+    Body,+    Exp,+    FParam,+    FunDef,+    LParam,+    Lambda,+    PatElem,+    Pattern,+    Prog,+    RetType,+    Stm,+  )+import Futhark.MonadFreshNames import Futhark.Pass.ExtractKernels.BlockedKernel import Futhark.Pass.ExtractKernels.ToKernels-import Futhark.MonadFreshNames import Futhark.Tools+import Prelude hiding (quot) -data KernelSize = KernelSize { kernelElementsPerThread :: SubExp-                               -- ^ Int64-                             , kernelNumThreads :: SubExp-                               -- ^ Int32-                             }-                deriving (Eq, Ord, Show)+data KernelSize = KernelSize+  { -- | Int64+    kernelElementsPerThread :: SubExp,+    -- | Int32+    kernelNumThreads :: SubExp+  }+  deriving (Eq, Ord, Show) -numberOfGroups :: (MonadBinder m, Op (Lore m) ~ HostOp (Lore m) inner) =>-                  String -> SubExp -> SubExp -> m (SubExp, SubExp)-numberOfGroups desc w64 group_size = do+numberOfGroups ::+  (MonadBinder m, Op (Lore m) ~ HostOp (Lore m) inner) =>+  String ->+  SubExp ->+  SubExp ->+  m (SubExp, SubExp)+numberOfGroups desc w group_size = do   max_num_groups_key <- nameFromString . pretty <$> newVName (desc ++ "_num_groups")-  num_groups <- letSubExp "num_groups" $-                Op $ SizeOp $ CalcNumGroups w64 max_num_groups_key group_size-  num_threads <- letSubExp "num_threads" $ BasicOp $ BinOp (Mul Int32 OverflowUndef) num_groups group_size+  num_groups <-+    letSubExp "num_groups" $+      Op $ SizeOp $ CalcNumGroups w max_num_groups_key group_size+  num_threads <-+    letSubExp "num_threads" $+      BasicOp $ BinOp (Mul Int64 OverflowUndef) num_groups group_size   return (num_groups, num_threads) -blockedKernelSize :: (MonadBinder m, Lore m ~ Kernels) =>-                     String -> SubExp -> m KernelSize+blockedKernelSize ::+  (MonadBinder m, Lore m ~ Kernels) =>+  String ->+  SubExp ->+  m KernelSize blockedKernelSize desc w = do   group_size <- getSize (desc ++ "_group_size") SizeGroup -  w64 <- letSubExp "w64" $ BasicOp $ ConvOp (SExt Int32 Int64) w-  (_, num_threads) <- numberOfGroups desc w64 group_size+  (_, num_threads) <- numberOfGroups desc w group_size    per_thread_elements <--    letSubExp "per_thread_elements" =<<-    eBinOp (SDivUp Int64 Unsafe) (eSubExp w64) (toExp =<< asIntS Int64 num_threads)+    letSubExp "per_thread_elements"+      =<< eBinOp (SDivUp Int64 Unsafe) (eSubExp w) (eSubExp num_threads)    return $ KernelSize per_thread_elements num_threads -splitArrays :: (MonadBinder m, Lore m ~ Kernels) =>-               VName -> [VName]-            -> SplitOrdering -> SubExp -> SubExp -> SubExp -> [VName]-            -> m ()+splitArrays ::+  (MonadBinder m, Lore m ~ Kernels) =>+  VName ->+  [VName] ->+  SplitOrdering ->+  SubExp ->+  SubExp ->+  SubExp ->+  [VName] ->+  m () splitArrays chunk_size split_bound ordering w i elems_per_i arrs = do   letBindNames [chunk_size] $ Op $ SizeOp $ SplitSpace ordering w i elems_per_i   case ordering of-    SplitContiguous     -> do-      offset <- letSubExp "slice_offset" $ BasicOp $ BinOp (Mul Int32 OverflowUndef) i elems_per_i+    SplitContiguous -> do+      offset <- letSubExp "slice_offset" $ BasicOp $ BinOp (Mul Int64 OverflowUndef) i elems_per_i       zipWithM_ (contiguousSlice offset) split_bound arrs     SplitStrided stride -> zipWithM_ (stridedSlice stride) split_bound arrs-  where contiguousSlice offset slice_name arr = do-          arr_t <- lookupType arr-          let slice = fullSlice arr_t [DimSlice offset (Var chunk_size) (constant (1::Int32))]-          letBindNames [slice_name] $ BasicOp $ Index arr slice+  where+    contiguousSlice offset slice_name arr = do+      arr_t <- lookupType arr+      let slice = fullSlice arr_t [DimSlice offset (Var chunk_size) (constant (1 :: Int64))]+      letBindNames [slice_name] $ BasicOp $ Index arr slice -        stridedSlice stride slice_name arr = do-          arr_t <- lookupType arr-          let slice = fullSlice arr_t [DimSlice i (Var chunk_size) stride]-          letBindNames [slice_name] $ BasicOp $ Index arr slice+    stridedSlice stride slice_name arr = do+      arr_t <- lookupType arr+      let slice = fullSlice arr_t [DimSlice i (Var chunk_size) stride]+      letBindNames [slice_name] $ BasicOp $ Index arr slice -partitionChunkedKernelFoldParameters :: Int -> [Param dec]-                                     -> (VName, Param dec, [Param dec], [Param dec])+partitionChunkedKernelFoldParameters ::+  Int ->+  [Param dec] ->+  (VName, Param dec, [Param dec], [Param dec]) partitionChunkedKernelFoldParameters num_accs (i_param : chunk_param : params) =   let (acc_params, arr_params) = splitAt num_accs params-  in (paramName i_param, chunk_param, acc_params, arr_params)+   in (paramName i_param, chunk_param, acc_params, arr_params) partitionChunkedKernelFoldParameters _ _ =   error "partitionChunkedKernelFoldParameters: lambda takes too few parameters" -blockedPerThread :: (MonadBinder m, Lore m ~ Kernels) =>-                    VName -> SubExp -> KernelSize -> StreamOrd -> Lambda (Lore m)-                 -> Int -> [VName]-                 -> m ([PatElemT Type], [PatElemT Type])+blockedPerThread ::+  (MonadBinder m, Lore m ~ Kernels) =>+  VName ->+  SubExp ->+  KernelSize ->+  StreamOrd ->+  Lambda (Lore m) ->+  Int ->+  [VName] ->+  m ([PatElemT Type], [PatElemT Type]) blockedPerThread thread_gtid w kernel_size ordering lam num_nonconcat arrs = do   let (_, chunk_size, [], arr_params) =         partitionChunkedKernelFoldParameters 0 $ lambdaParams lam        ordering' =-        case ordering of InOrder -> SplitContiguous-                         Disorder -> SplitStrided $ kernelNumThreads kernel_size+        case ordering of+          InOrder -> SplitContiguous+          Disorder -> SplitStrided $ kernelNumThreads kernel_size       red_ts = take num_nonconcat $ lambdaReturnType lam       map_ts = map rowType $ drop num_nonconcat $ lambdaReturnType lam -  per_thread <- asIntS Int32 $ kernelElementsPerThread kernel_size-  splitArrays (paramName chunk_size) (map paramName arr_params) ordering' w-    (Var thread_gtid) per_thread arrs+  per_thread <- asIntS Int64 $ kernelElementsPerThread kernel_size+  splitArrays+    (paramName chunk_size)+    (map paramName arr_params)+    ordering'+    w+    (Var thread_gtid)+    per_thread+    arrs    chunk_red_pes <- forM red_ts $ \red_t -> do     pe_name <- newVName "chunk_fold_red"@@ -112,21 +154,26 @@         splitAt num_nonconcat $ bodyResult $ lambdaBody lam    addStms $-    bodyStms (lambdaBody lam) <>-    stmsFromList-    [ Let (Pattern [] [pe]) (defAux ()) $ BasicOp $ SubExp se-    | (pe,se) <- zip chunk_red_pes chunk_red_ses ] <>-    stmsFromList-    [ Let (Pattern [] [pe]) (defAux ()) $ BasicOp $ SubExp se-    | (pe,se) <- zip chunk_map_pes chunk_map_ses ]+    bodyStms (lambdaBody lam)+      <> stmsFromList+        [ Let (Pattern [] [pe]) (defAux ()) $ BasicOp $ SubExp se+          | (pe, se) <- zip chunk_red_pes chunk_red_ses+        ]+      <> stmsFromList+        [ Let (Pattern [] [pe]) (defAux ()) $ BasicOp $ SubExp se+          | (pe, se) <- zip chunk_map_pes chunk_map_ses+        ]    return (chunk_red_pes, chunk_map_pes)  -- | Given a chunked fold lambda that takes its initial accumulator -- value as parameters, bind those parameters to the neutral element -- instead.-kerneliseLambda :: MonadFreshNames m =>-                   [SubExp] -> Lambda Kernels -> m (Lambda Kernels)+kerneliseLambda ::+  MonadFreshNames m =>+  [SubExp] ->+  Lambda Kernels ->+  m (Lambda Kernels) kerneliseLambda nes lam = do   thread_index <- newVName "thread_index"   let thread_index_param = Param thread_index $ Prim int32@@ -135,59 +182,69 @@        mkAccInit p (Var v)         | not $ primType $ paramType p =-            mkLet [] [paramIdent p] $ BasicOp $ Copy v+          mkLet [] [paramIdent p] $ BasicOp $ Copy v       mkAccInit p x = mkLet [] [paramIdent p] $ BasicOp $ SubExp x       acc_init_bnds = stmsFromList $ zipWith mkAccInit fold_acc_params nes-  return lam { lambdaBody = insertStms acc_init_bnds $-                            lambdaBody lam-             , lambdaParams = thread_index_param :-                              fold_chunk_param :-                              fold_inp_params-             }+  return+    lam+      { lambdaBody =+          insertStms acc_init_bnds $+            lambdaBody lam,+        lambdaParams =+          thread_index_param :+          fold_chunk_param :+          fold_inp_params+      } -prepareStream :: (MonadBinder m, Lore m ~ Kernels) =>-                 KernelSize-              -> [(VName, SubExp)]-              -> SubExp-              -> Commutativity-              -> Lambda Kernels-              -> [SubExp]-              -> [VName]-              -> m (SubExp, SegSpace, [Type], KernelBody Kernels)+prepareStream ::+  (MonadBinder m, Lore m ~ Kernels) =>+  KernelSize ->+  [(VName, SubExp)] ->+  SubExp ->+  Commutativity ->+  Lambda Kernels ->+  [SubExp] ->+  [VName] ->+  m (SubExp, SegSpace, [Type], KernelBody Kernels) prepareStream size ispace w comm fold_lam nes arrs = do   let (KernelSize elems_per_thread num_threads) = size   let (ordering, split_ordering) =-        case comm of Commutative -> (Disorder, SplitStrided num_threads)-                     Noncommutative -> (InOrder, SplitContiguous)+        case comm of+          Commutative -> (Disorder, SplitStrided num_threads)+          Noncommutative -> (InOrder, SplitContiguous)    fold_lam' <- kerneliseLambda nes fold_lam -  elems_per_thread_32 <- asIntS Int32 elems_per_thread-   gtid <- newVName "gtid"   space <- mkSegSpace $ ispace ++ [(gtid, num_threads)]-  kbody <- fmap (uncurry (flip (KernelBody ()))) $ runBinder $-           localScope (scopeOfSegSpace space) $ do-    (chunk_red_pes, chunk_map_pes) <--      blockedPerThread gtid w size ordering fold_lam' (length nes) arrs-    let concatReturns pe =-          ConcatReturns split_ordering w elems_per_thread_32 $ patElemName pe-    return (map (Returns ResultMaySimplify . Var . patElemName) chunk_red_pes ++-            map concatReturns chunk_map_pes)+  kbody <- fmap (uncurry (flip (KernelBody ()))) $+    runBinder $+      localScope (scopeOfSegSpace space) $ do+        (chunk_red_pes, chunk_map_pes) <-+          blockedPerThread gtid w size ordering fold_lam' (length nes) arrs+        let concatReturns pe =+              ConcatReturns split_ordering w elems_per_thread $ patElemName pe+        return+          ( map (Returns ResultMaySimplify . Var . patElemName) chunk_red_pes+              ++ map concatReturns chunk_map_pes+          )    let (redout_ts, mapout_ts) = splitAt (length nes) $ lambdaReturnType fold_lam       ts = redout_ts ++ map rowType mapout_ts    return (num_threads, space, ts, kbody) -streamRed :: (MonadFreshNames m, HasScope Kernels m) =>-             MkSegLevel Kernels m-          -> Pattern Kernels-          -> SubExp-          -> Commutativity-          -> Lambda Kernels -> Lambda Kernels-          -> [SubExp] -> [VName]-          -> m (Stms Kernels)+streamRed ::+  (MonadFreshNames m, HasScope Kernels m) =>+  MkSegLevel Kernels m ->+  Pattern Kernels ->+  SubExp ->+  Commutativity ->+  Lambda Kernels ->+  Lambda Kernels ->+  [SubExp] ->+  [VName] ->+  m (Stms Kernels) streamRed mk_lvl pat w comm red_lam fold_lam nes arrs = runBinderT'_ $ do   -- The strategy here is to rephrase the stream reduction as a   -- non-segmented SegRed that does explicit chunking within its body.@@ -201,17 +258,30 @@   (_, kspace, ts, kbody) <- prepareStream size ispace w comm fold_lam nes arrs    lvl <- mk_lvl [w] "stream_red" $ NoRecommendation SegNoVirt-  letBind pat' $ Op $ SegOp $ SegRed lvl kspace-    [SegBinOp comm red_lam nes mempty] ts kbody+  letBind pat' $+    Op $+      SegOp $+        SegRed+          lvl+          kspace+          [SegBinOp comm red_lam nes mempty]+          ts+          kbody    read_dummy  -- Similar to streamRed, but without the last reduction.-streamMap :: (MonadFreshNames m, HasScope Kernels m) =>-              MkSegLevel Kernels m-          -> [String] -> [PatElem Kernels] -> SubExp-           -> Commutativity -> Lambda Kernels -> [SubExp] -> [VName]-           -> m ((SubExp, [VName]), Stms Kernels)+streamMap ::+  (MonadFreshNames m, HasScope Kernels m) =>+  MkSegLevel Kernels m ->+  [String] ->+  [PatElem Kernels] ->+  SubExp ->+  Commutativity ->+  Lambda Kernels ->+  [SubExp] ->+  [VName] ->+  m ((SubExp, [VName]), Stms Kernels) streamMap mk_lvl out_desc mapout_pes w comm fold_lam nes arrs = runBinderT' $ do   size <- blockedKernelSize "stream_map" w @@ -233,19 +303,20 @@ -- array. segThreadCapped :: MonadFreshNames m => MkSegLevel Kernels m segThreadCapped ws desc r = do-  w64 <- letSubExp "nest_size" =<<-         foldBinOp (Mul Int64 OverflowUndef) (intConst Int64 1) =<<-         mapM (asIntS Int64) ws+  w <-+    letSubExp "nest_size"+      =<< foldBinOp (Mul Int64 OverflowUndef) (intConst Int64 1) ws   group_size <- getSize (desc ++ "_group_size") SizeGroup    case r of     ManyThreads -> do-      usable_groups <- letSubExp "segmap_usable_groups" .-                       BasicOp . ConvOp (SExt Int64 Int32) =<<-                       letSubExp "segmap_usable_groups_64" =<<-                       eBinOp (SDivUp Int64 Unsafe) (eSubExp w64)-                       (eSubExp =<< asIntS Int64 group_size)+      usable_groups <-+        letSubExp "segmap_usable_groups"+          =<< eBinOp+            (SDivUp Int64 Unsafe)+            (eSubExp w)+            (eSubExp =<< asIntS Int64 group_size)       return $ SegThread (Count usable_groups) (Count group_size) SegNoVirt     NoRecommendation v -> do-      (num_groups, _) <- numberOfGroups desc w64 group_size+      (num_groups, _) <- numberOfGroups desc w group_size       return $ SegThread (Count num_groups) (Count group_size) v
src/Futhark/Pass/ExtractKernels/ToKernels.hs view
@@ -1,63 +1,75 @@+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}-module Futhark.Pass.ExtractKernels.ToKernels-       ( getSize-       , segThread -       , soacsLambdaToKernels-       , soacsStmToKernels-       , scopeForKernels-       , scopeForSOACs-       )-       where+module Futhark.Pass.ExtractKernels.ToKernels+  ( getSize,+    segThread,+    soacsLambdaToKernels,+    soacsStmToKernels,+    scopeForKernels,+    scopeForSOACs,+  )+where  import Control.Monad.Identity import Data.List ()- import Futhark.Analysis.Rephrase import Futhark.IR+import Futhark.IR.Kernels import Futhark.IR.SOACS (SOACS) import qualified Futhark.IR.SOACS.SOAC as SOAC-import Futhark.IR.Kernels import Futhark.Tools -getSize :: (MonadBinder m, Op (Lore m) ~ HostOp (Lore m) inner) =>-           String -> SizeClass -> m SubExp+getSize ::+  (MonadBinder m, Op (Lore m) ~ HostOp (Lore m) inner) =>+  String ->+  SizeClass ->+  m SubExp getSize desc size_class = do   size_key <- nameFromString . pretty <$> newVName desc   letSubExp desc $ Op $ SizeOp $ GetSize size_key size_class -segThread :: (MonadBinder m, Op (Lore m) ~ HostOp (Lore m) inner) =>-             String -> m SegLevel+segThread ::+  (MonadBinder m, Op (Lore m) ~ HostOp (Lore m) inner) =>+  String ->+  m SegLevel segThread desc =   SegThread     <$> (Count <$> getSize (desc ++ "_num_groups") SizeNumGroups)     <*> (Count <$> getSize (desc ++ "_group_size") SizeGroup)     <*> pure SegVirt -injectSOACS :: (Monad m,-                SameScope from to,-                ExpDec from ~ ExpDec to,-                BodyDec from ~ BodyDec to,-                RetType from ~ RetType to,-                BranchType from ~ BranchType to,-                Op from ~ SOAC from) =>-               (SOAC to -> Op to) -> Rephraser m from to-injectSOACS f = Rephraser { rephraseExpLore = return-                          , rephraseBodyLore = return-                          , rephraseLetBoundLore = return-                          , rephraseFParamLore = return-                          , rephraseLParamLore = return-                          , rephraseOp = fmap f . onSOAC-                          , rephraseRetType = return-                          , rephraseBranchType = return-                          }-  where onSOAC = SOAC.mapSOACM mapper-        mapper = SOAC.SOACMapper { SOAC.mapOnSOACSubExp = return-                                 , SOAC.mapOnSOACVName = return-                                 , SOAC.mapOnSOACLambda = rephraseLambda $ injectSOACS f-                                 }+injectSOACS ::+  ( Monad m,+    SameScope from to,+    ExpDec from ~ ExpDec to,+    BodyDec from ~ BodyDec to,+    RetType from ~ RetType to,+    BranchType from ~ BranchType to,+    Op from ~ SOAC from+  ) =>+  (SOAC to -> Op to) ->+  Rephraser m from to+injectSOACS f =+  Rephraser+    { rephraseExpLore = return,+      rephraseBodyLore = return,+      rephraseLetBoundLore = return,+      rephraseFParamLore = return,+      rephraseLParamLore = return,+      rephraseOp = fmap f . onSOAC,+      rephraseRetType = return,+      rephraseBranchType = return+    }+  where+    onSOAC = SOAC.mapSOACM mapper+    mapper =+      SOAC.SOACMapper+        { SOAC.mapOnSOACSubExp = return,+          SOAC.mapOnSOACVName = return,+          SOAC.mapOnSOACLambda = rephraseLambda $ injectSOACS f+        }  soacsStmToKernels :: Stm SOACS -> Stm Kernels soacsStmToKernels = runIdentity . rephraseStm (injectSOACS OtherOp)
src/Futhark/Pass/FirstOrderTransform.hs view
@@ -1,5 +1,6 @@-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+ -- | Transform any SOACs to @for@-loops. -- -- Example:@@ -19,19 +20,18 @@ --     let ys'[i] = y --     in ys' -- @-module Futhark.Pass.FirstOrderTransform-  ( firstOrderTransform )-  where+module Futhark.Pass.FirstOrderTransform (firstOrderTransform) where -import Futhark.Transform.FirstOrderTransform (FirstOrderLore, transformFunDef, transformConsts) import Futhark.IR.SOACS (SOACS, scopeOf) import Futhark.Pass+import Futhark.Transform.FirstOrderTransform (FirstOrderLore, transformConsts, transformFunDef)  -- | The first-order transformation pass. firstOrderTransform :: FirstOrderLore lore => Pass SOACS lore firstOrderTransform =   Pass-  "first order transform"-  "Transform all SOACs to for-loops." $-  intraproceduralTransformationWithConsts-  transformConsts (transformFunDef . scopeOf)+    "first order transform"+    "Transform all SOACs to for-loops."+    $ intraproceduralTransformationWithConsts+      transformConsts+      (transformFunDef . scopeOf)
src/Futhark/Pass/KernelBabysitting.hs view
@@ -1,33 +1,46 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}+ -- | Do various kernel optimisations - mostly related to coalescing.-module Futhark.Pass.KernelBabysitting ( babysitKernels )-       where+module Futhark.Pass.KernelBabysitting (babysitKernels) where  import Control.Arrow (first) import Control.Monad.State.Strict-import qualified Data.Map.Strict as M import Data.Foldable import Data.List (elemIndex, isPrefixOf, sort)+import qualified Data.Map.Strict as M import Data.Maybe--import Futhark.MonadFreshNames import Futhark.IR-import Futhark.IR.Kernels-       hiding (Prog, Body, Stm, Pattern, PatElem,-               BasicOp, Exp, Lambda, FunDef, FParam, LParam, RetType)-import Futhark.Tools+import Futhark.IR.Kernels hiding+  ( BasicOp,+    Body,+    Exp,+    FParam,+    FunDef,+    LParam,+    Lambda,+    PatElem,+    Pattern,+    Prog,+    RetType,+    Stm,+  )+import Futhark.MonadFreshNames import Futhark.Pass+import Futhark.Tools import Futhark.Util  -- | The pass definition. babysitKernels :: Pass Kernels Kernels-babysitKernels = Pass "babysit kernels"-                 "Transpose kernel input arrays for better performance." $-                 intraproceduralTransformation onStms-  where onStms scope stms = do-          let m = localScope scope $ transformStms mempty stms-          fmap fst $ modifyNameSource $ runState (runBinderT m M.empty)+babysitKernels =+  Pass+    "babysit kernels"+    "Transpose kernel input arrays for better performance."+    $ intraproceduralTransformation onStms+  where+    onStms scope stms = do+      let m = localScope scope $ transformStms mempty stms+      fmap fst $ modifyNameSource $ runState (runBinderT m M.empty)  type BabysitM = Binder Kernels @@ -54,37 +67,46 @@     Just (Let _ _ (BasicOp (Reshape _ arr))) -> nonlinearInMemory arr m     Just (Let _ _ (BasicOp (Manifest perm _))) -> Just $ Just perm     Just (Let pat _ (Op (SegOp (SegMap _ _ ts _)))) ->-      nonlinear =<< find ((==name) . patElemName . fst)-      (zip (patternElements pat) ts)+      nonlinear+        =<< find+          ((== name) . patElemName . fst)+          (zip (patternElements pat) ts)     _ -> Nothing-  where nonlinear (pe, t)-          | inner_r <- arrayRank t, inner_r > 0 = do-              let outer_r = arrayRank (patElemType pe) - inner_r-              return $ Just $ rearrangeInverse $ [inner_r..inner_r+outer_r-1] ++ [0..inner_r-1]-          | otherwise = Nothing+  where+    nonlinear (pe, t)+      | inner_r <- arrayRank t,+        inner_r > 0 = do+        let outer_r = arrayRank (patElemType pe) - inner_r+        return $ Just $ rearrangeInverse $ [inner_r .. inner_r + outer_r -1] ++ [0 .. inner_r -1]+      | otherwise = Nothing  transformStm :: ExpMap -> Stm Kernels -> BabysitM ExpMap transformStm expmap (Let pat aux (Op (SegOp op))) = do-  let mapper = identitySegOpMapper-               { mapOnSegOpBody =-                   transformKernelBody expmap (segLevel op) (segSpace op)-               }+  let mapper =+        identitySegOpMapper+          { mapOnSegOpBody =+              transformKernelBody expmap (segLevel op) (segSpace op)+          }   op' <- mapSegOpM mapper op   let stm' = Let pat aux $ Op $ SegOp op'   addStm stm'-  return $ M.fromList [ (name, stm') | name <- patternNames pat ] <> expmap+  return $ M.fromList [(name, stm') | name <- patternNames pat] <> expmap transformStm expmap (Let pat aux e) = do   e' <- mapExpM (transform expmap) e   let bnd' = Let pat aux e'   addStm bnd'-  return $ M.fromList [ (name, bnd') | name <- patternNames pat ] <> expmap+  return $ M.fromList [(name, bnd') | name <- patternNames pat] <> expmap  transform :: ExpMap -> Mapper Kernels Kernels BabysitM transform expmap =-  identityMapper { mapOnBody = \scope -> localScope scope . transformBody expmap }+  identityMapper {mapOnBody = \scope -> localScope scope . transformBody expmap} -transformKernelBody :: ExpMap -> SegLevel -> SegSpace -> KernelBody Kernels-                    -> BabysitM (KernelBody Kernels)+transformKernelBody ::+  ExpMap ->+  SegLevel ->+  SegSpace ->+  KernelBody Kernels ->+  BabysitM (KernelBody Kernels) transformKernelBody expmap lvl space kbody = do   -- Go spelunking for accesses to arrays that are defined outside the   -- kernel body and where the indices are kernel thread indices.@@ -92,212 +114,245 @@   let thread_gids = map fst $ unSegSpace space       thread_local = namesFromList $ segFlat space : thread_gids       free_ker_vars = freeIn kbody `namesSubtract` getKerVariantIds space-  num_threads <- letSubExp "num_threads" $ BasicOp $ BinOp (Mul Int32 OverflowUndef)-                 (unCount $ segNumGroups lvl) (unCount $ segGroupSize lvl)-  evalStateT (traverseKernelBodyArrayIndexes-              free_ker_vars-              thread_local-              (scope <> scopeOfSegSpace space)-              (ensureCoalescedAccess expmap (unSegSpace space) num_threads)-              kbody)+  num_threads <-+    letSubExp "num_threads" $+      BasicOp $+        BinOp+          (Mul Int64 OverflowUndef)+          (unCount $ segNumGroups lvl)+          (unCount $ segGroupSize lvl)+  evalStateT+    ( traverseKernelBodyArrayIndexes+        free_ker_vars+        thread_local+        (scope <> scopeOfSegSpace space)+        (ensureCoalescedAccess expmap (unSegSpace space) num_threads)+        kbody+    )     mempty-  where getKerVariantIds = namesFromList . M.keys . scopeOfSegSpace+  where+    getKerVariantIds = namesFromList . M.keys . scopeOfSegSpace  type ArrayIndexTransform m =   Names ->-  (VName -> Bool) ->           -- thread local?-  (VName -> SubExp -> Bool)->  -- variant to a certain gid (given as first param)?-  (SubExp -> Maybe SubExp) ->  -- split substitution?-  Scope Kernels ->            -- type environment-  VName -> Slice SubExp -> m (Maybe (VName, Slice SubExp))+  (VName -> Bool) -> -- thread local?+  (VName -> SubExp -> Bool) -> -- variant to a certain gid (given as first param)?+  (SubExp -> Maybe SubExp) -> -- split substitution?+  Scope Kernels -> -- type environment+  VName ->+  Slice SubExp ->+  m (Maybe (VName, Slice SubExp)) -traverseKernelBodyArrayIndexes :: (Applicative f, Monad f) =>-                                  Names-                               -> Names-                               -> Scope Kernels-                               -> ArrayIndexTransform f-                               -> KernelBody Kernels-                               -> f (KernelBody Kernels)+traverseKernelBodyArrayIndexes ::+  (Applicative f, Monad f) =>+  Names ->+  Names ->+  Scope Kernels ->+  ArrayIndexTransform f ->+  KernelBody Kernels ->+  f (KernelBody Kernels) traverseKernelBodyArrayIndexes free_ker_vars thread_variant outer_scope f (KernelBody () kstms kres) =-  KernelBody () . stmsFromList <$>-  mapM (onStm (varianceInStms mempty kstms,-               mkSizeSubsts kstms,-               outer_scope)) (stmsToList kstms) <*>-  pure kres-  where onLambda (variance, szsubst, scope) lam =-          (\body' -> lam { lambdaBody = body' }) <$>-          onBody (variance, szsubst, scope') (lambdaBody lam)-          where scope' = scope <> scopeOfLParams (lambdaParams lam)--        onBody (variance, szsubst, scope) (Body bdec stms bres) = do-          stms' <- stmsFromList <$> mapM (onStm (variance', szsubst', scope')) (stmsToList stms)-          pure $ Body bdec stms' bres-          where variance' = varianceInStms variance stms-                szsubst' = mkSizeSubsts stms <> szsubst-                scope' = scope <> scopeOf stms+  KernelBody () . stmsFromList+    <$> mapM+      ( onStm+          ( varianceInStms mempty kstms,+            mkSizeSubsts kstms,+            outer_scope+          )+      )+      (stmsToList kstms)+    <*> pure kres+  where+    onLambda (variance, szsubst, scope) lam =+      (\body' -> lam {lambdaBody = body'})+        <$> onBody (variance, szsubst, scope') (lambdaBody lam)+      where+        scope' = scope <> scopeOfLParams (lambdaParams lam) -        onStm (variance, szsubst, _) (Let pat dec (BasicOp (Index arr is))) =-          Let pat dec . oldOrNew <$> f free_ker_vars isThreadLocal isGidVariant sizeSubst outer_scope arr is-          where oldOrNew Nothing =-                  BasicOp $ Index arr is-                oldOrNew (Just (arr', is')) =-                  BasicOp $ Index arr' is'+    onBody (variance, szsubst, scope) (Body bdec stms bres) = do+      stms' <- stmsFromList <$> mapM (onStm (variance', szsubst', scope')) (stmsToList stms)+      pure $ Body bdec stms' bres+      where+        variance' = varianceInStms variance stms+        szsubst' = mkSizeSubsts stms <> szsubst+        scope' = scope <> scopeOf stms -                isGidVariant gid (Var v) =-                  gid == v || nameIn gid (M.findWithDefault (oneName v) v variance)-                isGidVariant _ _ = False+    onStm (variance, szsubst, _) (Let pat dec (BasicOp (Index arr is))) =+      Let pat dec . oldOrNew <$> f free_ker_vars isThreadLocal isGidVariant sizeSubst outer_scope arr is+      where+        oldOrNew Nothing =+          BasicOp $ Index arr is+        oldOrNew (Just (arr', is')) =+          BasicOp $ Index arr' is' -                isThreadLocal v =-                  thread_variant `namesIntersect`-                  M.findWithDefault (oneName v) v variance+        isGidVariant gid (Var v) =+          gid == v || nameIn gid (M.findWithDefault (oneName v) v variance)+        isGidVariant _ _ = False -                sizeSubst (Constant v) = Just $ Constant v-                sizeSubst (Var v)-                  | v `M.member` outer_scope      = Just $ Var v-                  | Just v' <- M.lookup v szsubst = sizeSubst v'-                  | otherwise                      = Nothing+        isThreadLocal v =+          thread_variant+            `namesIntersect` M.findWithDefault (oneName v) v variance -        onStm (variance, szsubst, scope) (Let pat dec e) =-          Let pat dec <$> mapExpM (mapper (variance, szsubst, scope)) e+        sizeSubst (Constant v) = Just $ Constant v+        sizeSubst (Var v)+          | v `M.member` outer_scope = Just $ Var v+          | Just v' <- M.lookup v szsubst = sizeSubst v'+          | otherwise = Nothing+    onStm (variance, szsubst, scope) (Let pat dec e) =+      Let pat dec <$> mapExpM (mapper (variance, szsubst, scope)) e -        onOp ctx (OtherOp soac) =-          OtherOp <$> mapSOACM identitySOACMapper{ mapOnSOACLambda = onLambda ctx } soac-        onOp _ op = return op+    onOp ctx (OtherOp soac) =+      OtherOp <$> mapSOACM identitySOACMapper {mapOnSOACLambda = onLambda ctx} soac+    onOp _ op = return op -        mapper ctx = identityMapper { mapOnBody = const (onBody ctx)-                                    , mapOnOp = onOp ctx }+    mapper ctx =+      identityMapper+        { mapOnBody = const (onBody ctx),+          mapOnOp = onOp ctx+        } -        mkSizeSubsts = foldMap mkStmSizeSubst-          where mkStmSizeSubst (Let (Pattern [] [pe]) _ (Op (SizeOp (SplitSpace _ _ _ elems_per_i)))) =-                  M.singleton (patElemName pe) elems_per_i-                mkStmSizeSubst _ = mempty+    mkSizeSubsts = foldMap mkStmSizeSubst+      where+        mkStmSizeSubst (Let (Pattern [] [pe]) _ (Op (SizeOp (SplitSpace _ _ _ elems_per_i)))) =+          M.singleton (patElemName pe) elems_per_i+        mkStmSizeSubst _ = mempty  type Replacements = M.Map (VName, Slice SubExp) VName -ensureCoalescedAccess :: MonadBinder m =>-                         ExpMap-                      -> [(VName,SubExp)]-                      -> SubExp-                      -> ArrayIndexTransform (StateT Replacements m)-ensureCoalescedAccess expmap thread_space num_threads free_ker_vars isThreadLocal-                      isGidVariant sizeSubst outer_scope arr slice = do-  seen <- gets $ M.lookup (arr, slice)--  case (seen, isThreadLocal arr, typeOf <$> M.lookup arr outer_scope) of-    -- Already took care of this case elsewhere.-    (Just arr', _, _) ->-      pure $ Just (arr', slice)+ensureCoalescedAccess ::+  MonadBinder m =>+  ExpMap ->+  [(VName, SubExp)] ->+  SubExp ->+  ArrayIndexTransform (StateT Replacements m)+ensureCoalescedAccess+  expmap+  thread_space+  num_threads+  free_ker_vars+  isThreadLocal+  isGidVariant+  sizeSubst+  outer_scope+  arr+  slice = do+    seen <- gets $ M.lookup (arr, slice) -    (Nothing, False, Just t)-      -- We are fully indexing the array with thread IDs, but the-      -- indices are in a permuted order.-      | Just is <- sliceIndices slice,-        length is == arrayRank t,-        Just is' <- coalescedIndexes free_ker_vars isGidVariant (map Var thread_gids) is,-        Just perm <- is' `isPermutationOf` is ->+    case (seen, isThreadLocal arr, typeOf <$> M.lookup arr outer_scope) of+      -- Already took care of this case elsewhere.+      (Just arr', _, _) ->+        pure $ Just (arr', slice)+      (Nothing, False, Just t)+        -- We are fully indexing the array with thread IDs, but the+        -- indices are in a permuted order.+        | Just is <- sliceIndices slice,+          length is == arrayRank t,+          Just is' <- coalescedIndexes free_ker_vars isGidVariant (map Var thread_gids) is,+          Just perm <- is' `isPermutationOf` is ->           replace =<< lift (rearrangeInput (nonlinearInMemory arr expmap) perm arr)--      -- Check whether the access is already coalesced because of a-      -- previous rearrange being applied to the current array:-      -- 1. get the permutation of the source-array rearrange-      -- 2. apply it to the slice-      -- 3. check that the innermost index is actually the gid-      --    of the innermost kernel dimension.-      -- If so, the access is already coalesced, nothing to do!-      -- (Cosmin's Heuristic.)-      | Just (Let _ _ (BasicOp (Rearrange perm _))) <- M.lookup arr expmap,-        not $ null perm,-        not $ null thread_gids,-        inner_gid <- last thread_gids,-        length slice >= length perm,-        slice' <- map (slice !!) perm,-        DimFix inner_ind <- last slice',-        not $ null thread_gids,-        isGidVariant inner_gid inner_ind ->+        -- Check whether the access is already coalesced because of a+        -- previous rearrange being applied to the current array:+        -- 1. get the permutation of the source-array rearrange+        -- 2. apply it to the slice+        -- 3. check that the innermost index is actually the gid+        --    of the innermost kernel dimension.+        -- If so, the access is already coalesced, nothing to do!+        -- (Cosmin's Heuristic.)+        | Just (Let _ _ (BasicOp (Rearrange perm _))) <- M.lookup arr expmap,+          not $ null perm,+          not $ null thread_gids,+          inner_gid <- last thread_gids,+          length slice >= length perm,+          slice' <- map (slice !!) perm,+          DimFix inner_ind <- last slice',+          not $ null thread_gids,+          isGidVariant inner_gid inner_ind ->           return Nothing--      -- We are not fully indexing an array, but the remaining slice-      -- is invariant to the innermost-kernel dimension. We assume-      -- the remaining slice will be sequentially streamed, hence-      -- tiling will be applied later and will solve coalescing.-      -- Hence nothing to do at this point. (Cosmin's Heuristic.)-      | (is, rem_slice) <- splitSlice slice,-        not $ null rem_slice,-        allDimAreSlice rem_slice,-        Nothing <- M.lookup arr expmap,-        not $ tooSmallSlice (primByteSize (elemType t)) rem_slice,-        is /= map Var (take (length is) thread_gids) || length is == length thread_gids,-        not (null thread_gids || null is),-        not (last thread_gids `nameIn` (freeIn is <> freeIn rem_slice)) ->+        -- We are not fully indexing an array, but the remaining slice+        -- is invariant to the innermost-kernel dimension. We assume+        -- the remaining slice will be sequentially streamed, hence+        -- tiling will be applied later and will solve coalescing.+        -- Hence nothing to do at this point. (Cosmin's Heuristic.)+        | (is, rem_slice) <- splitSlice slice,+          not $ null rem_slice,+          allDimAreSlice rem_slice,+          Nothing <- M.lookup arr expmap,+          not $ tooSmallSlice (primByteSize (elemType t)) rem_slice,+          is /= map Var (take (length is) thread_gids) || length is == length thread_gids,+          not (null thread_gids || null is),+          not (last thread_gids `nameIn` (freeIn is <> freeIn rem_slice)) ->           return Nothing--      -- We are not fully indexing the array, and the indices are not-      -- a proper prefix of the thread indices, and some indices are-      -- thread local, so we assume (HEURISTIC!)  that the remaining-      -- dimensions will be traversed sequentially.-      | (is, rem_slice) <- splitSlice slice,-        not $ null rem_slice,-        not $ tooSmallSlice (primByteSize (elemType t)) rem_slice,-        is /= map Var (take (length is) thread_gids) || length is == length thread_gids,-        any isThreadLocal (namesToList $ freeIn is) -> do+        -- We are not fully indexing the array, and the indices are not+        -- a proper prefix of the thread indices, and some indices are+        -- thread local, so we assume (HEURISTIC!)  that the remaining+        -- dimensions will be traversed sequentially.+        | (is, rem_slice) <- splitSlice slice,+          not $ null rem_slice,+          not $ tooSmallSlice (primByteSize (elemType t)) rem_slice,+          is /= map Var (take (length is) thread_gids) || length is == length thread_gids,+          any isThreadLocal (namesToList $ freeIn is) -> do           let perm = coalescingPermutation (length is) $ arrayRank t           replace =<< lift (rearrangeInput (nonlinearInMemory arr expmap) perm arr) -      -- We are taking a slice of the array with a unit stride.  We-      -- assume that the slice will be traversed sequentially.-      ---      -- We will really want to treat the sliced dimension like two-      -- dimensions so we can transpose them.  This may require-      -- padding.-      | (is, rem_slice) <- splitSlice slice,-        and $ zipWith (==) is $ map Var thread_gids,-        DimSlice offset len (Constant stride):_ <- rem_slice,-        isThreadLocalSubExp offset,-        Just {} <- sizeSubst len,-        oneIsh stride -> do-          let num_chunks = if null is-                           then primExpFromSubExp int32 num_threads-                           else coerceIntPrimExp Int32 $-                                product $ map (primExpFromSubExp int32) $-                                drop (length is) thread_gdims+        -- We are taking a slice of the array with a unit stride.  We+        -- assume that the slice will be traversed sequentially.+        --+        -- We will really want to treat the sliced dimension like two+        -- dimensions so we can transpose them.  This may require+        -- padding.+        | (is, rem_slice) <- splitSlice slice,+          and $ zipWith (==) is $ map Var thread_gids,+          DimSlice offset len (Constant stride) : _ <- rem_slice,+          isThreadLocalSubExp offset,+          Just {} <- sizeSubst len,+          oneIsh stride -> do+          let num_chunks =+                if null is+                  then untyped $ pe32 num_threads+                  else+                    untyped $+                      product $+                        map pe64 $+                          drop (length is) thread_gdims           replace =<< lift (rearrangeSlice (length is) (arraySize (length is) t) num_chunks arr) -      -- Everything is fine... assuming that the array is in row-major-      -- order!  Make sure that is the case.-      | Just{} <- nonlinearInMemory arr expmap ->+        -- Everything is fine... assuming that the array is in row-major+        -- order!  Make sure that is the case.+        | Just {} <- nonlinearInMemory arr expmap ->           case sliceIndices slice of-            Just is | Just _ <- coalescedIndexes free_ker_vars isGidVariant (map Var thread_gids) is ->-                        replace =<< lift (rowMajorArray arr)-                    | otherwise ->-                        return Nothing+            Just is+              | Just _ <- coalescedIndexes free_ker_vars isGidVariant (map Var thread_gids) is ->+                replace =<< lift (rowMajorArray arr)+              | otherwise ->+                return Nothing             _ -> replace =<< lift (rowMajorArray arr)--    _ -> return Nothing--  where (thread_gids, thread_gdims) = unzip thread_space+      _ -> return Nothing+    where+      (thread_gids, thread_gdims) = unzip thread_space -        replace arr' = do-          modify $ M.insert (arr, slice) arr'-          return $ Just (arr', slice)+      replace arr' = do+        modify $ M.insert (arr, slice) arr'+        return $ Just (arr', slice) -        isThreadLocalSubExp (Var v) = isThreadLocal v-        isThreadLocalSubExp Constant{} = False+      isThreadLocalSubExp (Var v) = isThreadLocal v+      isThreadLocalSubExp Constant {} = False  -- Heuristic for avoiding rearranging too small arrays. tooSmallSlice :: Int32 -> Slice SubExp -> Bool-tooSmallSlice bs = fst . foldl comb (True,bs) . sliceDims-  where comb (True, x) (Constant (IntValue (Int32Value d))) = (d*x < 4, d*x)-        comb (_, x)     _                                   = (False, x)+tooSmallSlice bs = fst . foldl comb (True, bs) . sliceDims+  where+    comb (True, x) (Constant (IntValue (Int32Value d))) = (d * x < 4, d * x)+    comb (_, x) _ = (False, x)  splitSlice :: Slice SubExp -> ([SubExp], Slice SubExp) splitSlice [] = ([], [])-splitSlice (DimFix i:is) = first (i:) $ splitSlice is+splitSlice (DimFix i : is) = first (i :) $ splitSlice is splitSlice is = ([], is)  allDimAreSlice :: Slice SubExp -> Bool allDimAreSlice [] = True-allDimAreSlice (DimFix _:_) = False-allDimAreSlice (_:is) = allDimAreSlice is+allDimAreSlice (DimFix _ : _) = False+allDimAreSlice (_ : is) = allDimAreSlice is  -- Try to move thread indexes into their proper position. coalescedIndexes :: Names -> (VName -> SubExp -> Bool) -> [SubExp] -> [SubExp] -> Maybe [SubExp]@@ -310,59 +365,65 @@   -- 3. the indexes are a prefix of the thread indexes, because that   -- means multiple threads will be accessing the same element.   | any isCt is =-      Nothing+    Nothing   | any (`nameIn` free_ker_vars) (subExpVars is) =-      Nothing+    Nothing   | is `isPrefixOf` tgids =-      Nothing+    Nothing   | not (null tgids),     not (null is),     Var innergid <- last tgids,     num_is > 0 && isGidVariant innergid (last is) =-      Just is+    Just is   -- 3. Otherwise try fix coalescing   | otherwise =-      Just $ reverse $ foldl move (reverse is) $ zip [0..] (reverse tgids)-  where num_is = length is+    Just $ reverse $ foldl move (reverse is) $ zip [0 ..] (reverse tgids)+  where+    num_is = length is -        move is_rev (i, tgid)-          -- If tgid is in is_rev anywhere but at position i, and-          -- position i exists, we move it to position i instead.-          | Just j <- elemIndex tgid is_rev, i /= j, i < num_is =-              swap i j is_rev-          | otherwise =-              is_rev+    move is_rev (i, tgid)+      -- If tgid is in is_rev anywhere but at position i, and+      -- position i exists, we move it to position i instead.+      | Just j <- elemIndex tgid is_rev,+        i /= j,+        i < num_is =+        swap i j is_rev+      | otherwise =+        is_rev -        swap i j l-          | Just ix <- maybeNth i l,-            Just jx <- maybeNth j l =-              update i jx $ update j ix l-          | otherwise =-              error $ "coalescedIndexes swap: invalid indices" ++ show (i, j, l)+    swap i j l+      | Just ix <- maybeNth i l,+        Just jx <- maybeNth j l =+        update i jx $ update j ix l+      | otherwise =+        error $ "coalescedIndexes swap: invalid indices" ++ show (i, j, l) -        update 0 x (_:ys) = x : ys-        update i x (y:ys) = y : update (i-1) x ys-        update _ _ []     = error "coalescedIndexes: update"+    update 0 x (_ : ys) = x : ys+    update i x (y : ys) = y : update (i -1) x ys+    update _ _ [] = error "coalescedIndexes: update" -        isCt :: SubExp -> Bool-        isCt (Constant _) = True-        isCt (Var      _) = False+    isCt :: SubExp -> Bool+    isCt (Constant _) = True+    isCt (Var _) = False  coalescingPermutation :: Int -> Int -> [Int] coalescingPermutation num_is rank =-  [num_is..rank-1] ++ [0..num_is-1]+  [num_is .. rank -1] ++ [0 .. num_is -1] -rearrangeInput :: MonadBinder m =>-                  Maybe (Maybe [Int]) -> [Int] -> VName -> m VName+rearrangeInput ::+  MonadBinder m =>+  Maybe (Maybe [Int]) ->+  [Int] ->+  VName ->+  m VName rearrangeInput (Just (Just current_perm)) perm arr   | current_perm == perm = return arr -- Already has desired representation.- rearrangeInput Nothing perm arr   | sort perm == perm = return arr -- We don't know the current-                                   -- representation, but the indexing-                                   -- is linear, so let's hope the-                                   -- array is too.-rearrangeInput (Just Just{}) perm arr+  -- representation, but the indexing+  -- is linear, so let's hope the+  -- array is too.+rearrangeInput (Just Just {}) perm arr   | sort perm == perm = rowMajorArray arr -- We just want a row-major array, no tricks. rearrangeInput manifest perm arr = do   -- We may first manifest the array to ensure that it is flat in@@ -372,60 +433,73 @@   letExp (baseString arr ++ "_coalesced") $     BasicOp $ Manifest perm manifested -rowMajorArray :: MonadBinder m =>-                 VName -> m VName+rowMajorArray ::+  MonadBinder m =>+  VName ->+  m VName rowMajorArray arr = do   rank <- arrayRank <$> lookupType arr-  letExp (baseString arr ++ "_rowmajor") $ BasicOp $ Manifest [0..rank-1] arr+  letExp (baseString arr ++ "_rowmajor") $ BasicOp $ Manifest [0 .. rank -1] arr -rearrangeSlice :: MonadBinder m =>-                  Int -> SubExp -> PrimExp VName -> VName-               -> m VName+rearrangeSlice ::+  MonadBinder m =>+  Int ->+  SubExp ->+  PrimExp VName ->+  VName ->+  m VName rearrangeSlice d w num_chunks arr = do   num_chunks' <- toSubExp "num_chunks" num_chunks    (w_padded, padding) <- paddedScanReduceInput w num_chunks' -  per_chunk <- letSubExp "per_chunk" $-               BasicOp $ BinOp (SQuot Int32 Unsafe) w_padded num_chunks'+  per_chunk <-+    letSubExp "per_chunk" $+      BasicOp $ BinOp (SQuot Int64 Unsafe) w_padded num_chunks'   arr_t <- lookupType arr   arr_padded <- padArray w_padded padding arr_t   rearrange num_chunks' w_padded per_chunk (baseString arr) arr_padded arr_t--  where padArray w_padded padding arr_t = do-          let arr_shape = arrayShape arr_t-              padding_shape = setDim d arr_shape padding-          arr_padding <--            letExp (baseString arr <> "_padding") $-            BasicOp $ Scratch (elemType arr_t) (shapeDims padding_shape)-          letExp (baseString arr <> "_padded") $-            BasicOp $ Concat d arr [arr_padding] w_padded+  where+    padArray w_padded padding arr_t = do+      let arr_shape = arrayShape arr_t+          padding_shape = setDim d arr_shape padding+      arr_padding <-+        letExp (baseString arr <> "_padding") $+          BasicOp $ Scratch (elemType arr_t) (shapeDims padding_shape)+      letExp (baseString arr <> "_padded") $+        BasicOp $ Concat d arr [arr_padding] w_padded -        rearrange num_chunks' w_padded per_chunk arr_name arr_padded arr_t = do-          let arr_dims = arrayDims arr_t-              pre_dims = take d arr_dims-              post_dims = drop (d+1) arr_dims-              extradim_shape = Shape $ pre_dims ++ [num_chunks', per_chunk] ++ post_dims-              tr_perm = [0..d-1] ++ map (+d) ([1] ++ [2..shapeRank extradim_shape-1-d] ++ [0])-          arr_extradim <--            letExp (arr_name <> "_extradim") $-            BasicOp $ Reshape (map DimNew $ shapeDims extradim_shape) arr_padded-          arr_extradim_tr <--            letExp (arr_name <> "_extradim_tr") $-            BasicOp $ Manifest tr_perm arr_extradim-          arr_inv_tr <- letExp (arr_name <> "_inv_tr") $-            BasicOp $ Reshape (map DimCoercion pre_dims ++ map DimNew (w_padded : post_dims))-            arr_extradim_tr-          letExp (arr_name <> "_inv_tr_init") =<<-            eSliceArray d  arr_inv_tr (eSubExp $ constant (0::Int32)) (eSubExp w)+    rearrange num_chunks' w_padded per_chunk arr_name arr_padded arr_t = do+      let arr_dims = arrayDims arr_t+          pre_dims = take d arr_dims+          post_dims = drop (d + 1) arr_dims+          extradim_shape = Shape $ pre_dims ++ [num_chunks', per_chunk] ++ post_dims+          tr_perm = [0 .. d -1] ++ map (+ d) ([1] ++ [2 .. shapeRank extradim_shape -1 - d] ++ [0])+      arr_extradim <-+        letExp (arr_name <> "_extradim") $+          BasicOp $ Reshape (map DimNew $ shapeDims extradim_shape) arr_padded+      arr_extradim_tr <-+        letExp (arr_name <> "_extradim_tr") $+          BasicOp $ Manifest tr_perm arr_extradim+      arr_inv_tr <-+        letExp (arr_name <> "_inv_tr") $+          BasicOp $+            Reshape+              (map DimCoercion pre_dims ++ map DimNew (w_padded : post_dims))+              arr_extradim_tr+      letExp (arr_name <> "_inv_tr_init")+        =<< eSliceArray d arr_inv_tr (eSubExp $ constant (0 :: Int64)) (eSubExp w) -paddedScanReduceInput :: MonadBinder m =>-                         SubExp -> SubExp-                      -> m (SubExp, SubExp)+paddedScanReduceInput ::+  MonadBinder m =>+  SubExp ->+  SubExp ->+  m (SubExp, SubExp) paddedScanReduceInput w stride = do-  w_padded <- letSubExp "padded_size" =<<-              eRoundToMultipleOf Int32 (eSubExp w) (eSubExp stride)-  padding <- letSubExp "padding" $ BasicOp $ BinOp (Sub Int32 OverflowUndef) w_padded w+  w_padded <-+    letSubExp "padded_size"+      =<< eRoundToMultipleOf Int64 (eSubExp w) (eSubExp stride)+  padding <- letSubExp "padding" $ BasicOp $ BinOp (Sub Int64 OverflowUndef) w_padded w   return (w_padded, padding)  --- Computing variance.@@ -438,6 +512,7 @@ varianceInStm :: VarianceTable -> Stm Kernels -> VarianceTable varianceInStm variance bnd =   foldl' add variance $ patternNames $ stmPattern bnd-  where add variance' v = M.insert v binding_variance variance'-        look variance' v = oneName v <> M.findWithDefault mempty v variance'-        binding_variance = mconcat $ map (look variance) $ namesToList (freeIn bnd)+  where+    add variance' v = M.insert v binding_variance variance'+    look variance' v = oneName v <> M.findWithDefault mempty v variance'+    binding_variance = mconcat $ map (look variance) $ namesToList (freeIn bnd)
src/Futhark/Pass/Simplify.hs view
@@ -1,25 +1,26 @@ {-# LANGUAGE FlexibleContexts #-}+ module Futhark.Pass.Simplify-  ( simplify-  , simplifySOACS-  , simplifySeq-  , simplifyKernels-  , simplifyKernelsMem-  , simplifySeqMem+  ( simplify,+    simplifySOACS,+    simplifySeq,+    simplifyKernels,+    simplifyKernelsMem,+    simplifySeqMem,   )-  where+where -import qualified Futhark.IR.SOACS.Simplify as SOACS import qualified Futhark.IR.Kernels.Simplify as Kernels-import qualified Futhark.IR.Seq as Seq import qualified Futhark.IR.KernelsMem as KernelsMem+import qualified Futhark.IR.SOACS.Simplify as SOACS+import qualified Futhark.IR.Seq as Seq import qualified Futhark.IR.SeqMem as SeqMem--import Futhark.Pass import Futhark.IR.Syntax+import Futhark.Pass -simplify :: (Prog lore -> PassM (Prog lore))-         -> Pass lore lore+simplify ::+  (Prog lore -> PassM (Prog lore)) ->+  Pass lore lore simplify = Pass "simplify" "Perform simple enabling optimisations."  simplifySOACS :: Pass SOACS.SOACS SOACS.SOACS
src/Futhark/Passes.hs view
@@ -1,23 +1,28 @@ {-# LANGUAGE FlexibleContexts #-}+ -- | Optimisation pipelines. module Futhark.Passes-  ( standardPipeline-  , sequentialPipeline-  , kernelsPipeline-  , sequentialCpuPipeline-  , gpuPipeline+  ( standardPipeline,+    sequentialPipeline,+    kernelsPipeline,+    sequentialCpuPipeline,+    gpuPipeline,   ) where  import Control.Category ((>>>))-+import Futhark.IR.Kernels (Kernels)+import Futhark.IR.KernelsMem (KernelsMem)+import Futhark.IR.SOACS (SOACS)+import Futhark.IR.Seq (Seq)+import Futhark.IR.SeqMem (SeqMem) import Futhark.Optimise.CSE+import Futhark.Optimise.DoubleBuffer import Futhark.Optimise.Fusion import Futhark.Optimise.InPlaceLowering import Futhark.Optimise.InliningDeadFun import Futhark.Optimise.Sink import Futhark.Optimise.TileLoops-import Futhark.Optimise.DoubleBuffer import Futhark.Optimise.Unstream import Futhark.Pass.ExpandAllocations import qualified Futhark.Pass.ExplicitAllocations.Kernels as Kernels@@ -27,69 +32,69 @@ import Futhark.Pass.KernelBabysitting import Futhark.Pass.Simplify import Futhark.Pipeline-import Futhark.IR.KernelsMem (KernelsMem)-import Futhark.IR.SeqMem (SeqMem)-import Futhark.IR.Kernels (Kernels)-import Futhark.IR.Seq (Seq)-import Futhark.IR.SOACS (SOACS)  standardPipeline :: Pipeline SOACS SOACS standardPipeline =-  passes [ simplifySOACS-         , inlineFunctions-         , simplifySOACS-         , performCSE True-         , simplifySOACS-           -- We run fusion twice-         , fuseSOACs-         , performCSE True-         , simplifySOACS-         , fuseSOACs-         , performCSE True-         , simplifySOACS-         , removeDeadFunctions-         ]+  passes+    [ simplifySOACS,+      inlineFunctions,+      simplifySOACS,+      performCSE True,+      simplifySOACS,+      -- We run fusion twice+      fuseSOACs,+      performCSE True,+      simplifySOACS,+      fuseSOACs,+      performCSE True,+      simplifySOACS,+      removeDeadFunctions+    ]  kernelsPipeline :: Pipeline SOACS Kernels kernelsPipeline =-  standardPipeline >>>-  onePass extractKernels >>>-  passes [ simplifyKernels-         , babysitKernels-         , tileLoops-         , unstream-         , performCSE True-         , simplifyKernels-         , sink-         , inPlaceLoweringKernels-         ]+  standardPipeline+    >>> onePass extractKernels+    >>> passes+      [ simplifyKernels,+        babysitKernels,+        tileLoops,+        unstream,+        performCSE True,+        simplifyKernels,+        sink,+        inPlaceLoweringKernels+      ]  sequentialPipeline :: Pipeline SOACS Seq sequentialPipeline =-  standardPipeline >>>-  onePass firstOrderTransform >>>-  passes [ simplifySeq-         , inPlaceLoweringSeq-         ]+  standardPipeline+    >>> onePass firstOrderTransform+    >>> passes+      [ simplifySeq,+        inPlaceLoweringSeq+      ]  sequentialCpuPipeline :: Pipeline SOACS SeqMem sequentialCpuPipeline =-  sequentialPipeline >>>-  onePass Seq.explicitAllocations >>>-  passes [ performCSE False-         , simplifySeqMem-         , simplifySeqMem-         ]+  sequentialPipeline+    >>> onePass Seq.explicitAllocations+    >>> passes+      [ performCSE False,+        simplifySeqMem,+        simplifySeqMem+      ]  gpuPipeline :: Pipeline SOACS KernelsMem gpuPipeline =-  kernelsPipeline >>>-  onePass Kernels.explicitAllocations >>>-  passes [ simplifyKernelsMem-         , performCSE False-         , simplifyKernelsMem-         , doubleBuffer-         , simplifyKernelsMem-         , expandAllocations-         , simplifyKernelsMem-         ]+  kernelsPipeline+    >>> onePass Kernels.explicitAllocations+    >>> passes+      [ simplifyKernelsMem,+        performCSE False,+        simplifyKernelsMem,+        doubleBuffer,+        simplifyKernelsMem,+        expandAllocations,+        simplifyKernelsMem+      ]
src/Futhark/Pipeline.hs view
@@ -1,5 +1,8 @@-{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleContexts, OverloadedStrings #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Trustworthy #-}+ -- | Definition of the core compiler driver building blocks.  The -- spine of the compiler is the 'FutharkM' monad, although note that -- individual passes are pure functions, and do not use the 'FutharkM'@@ -10,152 +13,171 @@ -- final program (still in IR), then running an 'Action', which is -- usually a code generator. module Futhark.Pipeline-       ( Pipeline-       , PipelineConfig (..)-       , Action (..)--       , FutharkM-       , runFutharkM-       , Verbosity(..)--       , module Futhark.Error--       , onePass-       , passes-       , runPipeline-       )-       where+  ( Pipeline,+    PipelineConfig (..),+    Action (..),+    FutharkM,+    runFutharkM,+    Verbosity (..),+    module Futhark.Error,+    onePass,+    passes,+    runPipeline,+  )+where  import Control.Category import Control.Monad-import Control.Monad.Writer.Strict hiding (pass) import Control.Monad.Except-import Control.Monad.State import Control.Monad.Reader+import Control.Monad.State+import Control.Monad.Writer.Strict hiding (pass) import qualified Data.Text as T import qualified Data.Text.IO as T import Data.Time.Clock-import System.IO-import Text.Printf--import Prelude hiding (id, (.))- import qualified Futhark.Analysis.Alias as Alias import Futhark.Error-import Futhark.IR (Prog, PrettyLore)-import Futhark.TypeCheck+import Futhark.IR (PrettyLore, Prog)+import Futhark.MonadFreshNames import Futhark.Pass+import Futhark.TypeCheck import Futhark.Util.Log import Futhark.Util.Pretty (prettyText)-import Futhark.MonadFreshNames+import System.IO+import Text.Printf+import Prelude hiding (id, (.))  -- | How much information to print to stderr while the compiler is running. data Verbosity-  = NotVerbose -- ^ Silence is golden.-  | Verbose -- ^ Print messages about which pass is running.-  | VeryVerbose -- ^ Also print logs from individual passes.+  = -- | Silence is golden.+    NotVerbose+  | -- | Print messages about which pass is running.+    Verbose+  | -- | Also print logs from individual passes.+    VeryVerbose   deriving (Eq, Ord) -newtype FutharkEnv = FutharkEnv { futharkVerbose :: Verbosity }+newtype FutharkEnv = FutharkEnv {futharkVerbose :: Verbosity} -data FutharkState = FutharkState { futharkPrevLog :: UTCTime-                                 , futharkNameSource :: VNameSource }+data FutharkState = FutharkState+  { futharkPrevLog :: UTCTime,+    futharkNameSource :: VNameSource+  }  -- | The main Futhark compiler driver monad - basically some state -- tracking on top if 'IO'. newtype FutharkM a = FutharkM (ExceptT CompilerError (StateT FutharkState (ReaderT FutharkEnv IO)) a)-                     deriving (Applicative, Functor, Monad,-                               MonadError CompilerError,-                               MonadState FutharkState,-                               MonadReader FutharkEnv,-                               MonadIO)+  deriving+    ( Applicative,+      Functor,+      Monad,+      MonadError CompilerError,+      MonadState FutharkState,+      MonadReader FutharkEnv,+      MonadIO+    )  instance MonadFreshNames FutharkM where   getNameSource = gets futharkNameSource-  putNameSource src = modify $ \s -> s { futharkNameSource = src }+  putNameSource src = modify $ \s -> s {futharkNameSource = src}  instance MonadLogger FutharkM where   addLog = mapM_ perLine . T.lines . toText-    where perLine msg = do-            verb <- asks $ (>=Verbose) . futharkVerbose-            prev <- gets futharkPrevLog-            now <- liftIO getCurrentTime-            let delta :: Double-                delta = fromRational $ toRational (now `diffUTCTime` prev)-                prefix = printf "[  +%.6f] " delta-            modify $ \s -> s { futharkPrevLog = now }-            when verb $ liftIO $ T.hPutStrLn stderr $ T.pack prefix <> msg+    where+      perLine msg = do+        verb <- asks $ (>= Verbose) . futharkVerbose+        prev <- gets futharkPrevLog+        now <- liftIO getCurrentTime+        let delta :: Double+            delta = fromRational $ toRational (now `diffUTCTime` prev)+            prefix = printf "[  +%.6f] " delta+        modify $ \s -> s {futharkPrevLog = now}+        when verb $ liftIO $ T.hPutStrLn stderr $ T.pack prefix <> msg  -- | Run a 'FutharkM' action. runFutharkM :: FutharkM a -> Verbosity -> IO (Either CompilerError a) runFutharkM (FutharkM m) verbose = do   s <- FutharkState <$> getCurrentTime <*> pure blankNameSource   runReaderT (evalStateT (runExceptT m) s) newEnv-  where newEnv = FutharkEnv verbose+  where+    newEnv = FutharkEnv verbose  -- | A compilation always ends with some kind of action.-data Action lore =-  Action { actionName :: String-         , actionDescription :: String-         , actionProcedure :: Prog lore -> FutharkM ()-         }+data Action lore = Action+  { actionName :: String,+    actionDescription :: String,+    actionProcedure :: Prog lore -> FutharkM ()+  }  -- | Configuration object for running a compiler pipeline.-data PipelineConfig =-  PipelineConfig { pipelineVerbose :: Bool-                 , pipelineValidate :: Bool-                 }+data PipelineConfig = PipelineConfig+  { pipelineVerbose :: Bool,+    pipelineValidate :: Bool+  }  -- | A compiler pipeline is conceptually a function from programs to -- programs, where the actual representation may change.  Pipelines -- can be composed using their 'Category' instance.-newtype Pipeline fromlore tolore =-  Pipeline { unPipeline :: PipelineConfig -> Prog fromlore -> FutharkM (Prog tolore) }+newtype Pipeline fromlore tolore = Pipeline {unPipeline :: PipelineConfig -> Prog fromlore -> FutharkM (Prog tolore)}  instance Category Pipeline where   id = Pipeline $ const return   p2 . p1 = Pipeline perform-    where perform cfg prog =-            runPipeline p2 cfg =<< runPipeline p1 cfg prog+    where+      perform cfg prog =+        runPipeline p2 cfg =<< runPipeline p1 cfg prog  -- | Run the pipeline on the given program.-runPipeline :: Pipeline fromlore tolore-            -> PipelineConfig-            -> Prog fromlore-            -> FutharkM (Prog tolore)+runPipeline ::+  Pipeline fromlore tolore ->+  PipelineConfig ->+  Prog fromlore ->+  FutharkM (Prog tolore) runPipeline = unPipeline  -- | Construct a pipeline from a single compiler pass.-onePass :: Checkable tolore =>-           Pass fromlore tolore -> Pipeline fromlore tolore+onePass ::+  Checkable tolore =>+  Pass fromlore tolore ->+  Pipeline fromlore tolore onePass pass = Pipeline perform-  where perform cfg prog = do-          when (pipelineVerbose cfg) $ logMsg $-            "Running pass " <> T.pack (passName pass)-          prog' <- runPass pass prog-          let prog'' = Alias.aliasAnalysis prog'-          when (pipelineValidate cfg) $-            case checkProg prog'' of-              Left err -> validationError pass prog'' $ show err-              Right () -> return ()-          return prog'+  where+    perform cfg prog = do+      when (pipelineVerbose cfg) $+        logMsg $+          "Running pass " <> T.pack (passName pass)+      prog' <- runPass pass prog+      let prog'' = Alias.aliasAnalysis prog'+      when (pipelineValidate cfg) $+        case checkProg prog'' of+          Left err -> validationError pass prog'' $ show err+          Right () -> return ()+      return prog'  -- | Create a pipeline from a list of passes.-passes :: Checkable lore =>-          [Pass lore lore] -> Pipeline lore lore+passes ::+  Checkable lore =>+  [Pass lore lore] ->+  Pipeline lore lore passes = foldl (>>>) id . map onePass -validationError :: PrettyLore lore =>-                   Pass fromlore tolore -> Prog lore -> String -> FutharkM a+validationError ::+  PrettyLore lore =>+  Pass fromlore tolore ->+  Prog lore ->+  String ->+  FutharkM a validationError pass prog err =   throwError $ InternalError msg (prettyText prog) CompilerBug-  where msg = "Type error after pass '" <> T.pack (passName pass) <> "':\n" <> T.pack err+  where+    msg = "Type error after pass '" <> T.pack (passName pass) <> "':\n" <> T.pack err -runPass :: Pass fromlore tolore-        -> Prog fromlore-        -> FutharkM (Prog tolore)+runPass ::+  Pass fromlore tolore ->+  Prog fromlore ->+  FutharkM (Prog tolore) runPass pass prog = do   (prog', logged) <- runPassM (passFunction pass prog)-  verb <- asks $ (>=VeryVerbose) . futharkVerbose+  verb <- asks $ (>= VeryVerbose) . futharkVerbose   when verb $ addLog logged   return prog'
src/Futhark/Pkg/Info.hs view
@@ -1,44 +1,43 @@ {-# LANGUAGE OverloadedStrings #-}+ -- | Obtaining information about packages over THE INTERNET! module Futhark.Pkg.Info   ( -- * Package info-    PkgInfo(..)-  , lookupPkgRev-  , pkgInfo-  , PkgRevInfo (..)-  , GetManifest (getManifest)-  , downloadZipball+    PkgInfo (..),+    lookupPkgRev,+    pkgInfo,+    PkgRevInfo (..),+    GetManifest (getManifest),+    downloadZipball,      -- * Package registry-  , PkgRegistry-  , MonadPkgRegistry(..)-  , lookupPackage-  , lookupPackageRev-  , lookupNewestRev+    PkgRegistry,+    MonadPkgRegistry (..),+    lookupPackage,+    lookupPackageRev,+    lookupNewestRev,   )-  where+where +import qualified Codec.Archive.Zip as Zip import Control.Monad.IO.Class-import Data.Maybe+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as LBS import Data.IORef+import Data.List (foldl', intersperse) import qualified Data.Map as M+import Data.Maybe import qualified Data.Text as T-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS import qualified Data.Text.Encoding as T-import Data.List (foldl', intersperse)-import qualified System.FilePath.Posix as Posix+import Data.Time (UTCTime, defaultTimeLocale, formatTime, getCurrentTime)+import Futhark.Pkg.Types+import Futhark.Util (maybeHead)+import Futhark.Util.Log import System.Exit+import qualified System.FilePath.Posix as Posix import System.IO--import qualified Codec.Archive.Zip as Zip-import Data.Time (UTCTime, UTCTime, defaultTimeLocale, formatTime, getCurrentTime) import System.Process.ByteString (readProcessWithExitCode) -import Futhark.Pkg.Types-import Futhark.Util.Log-import Futhark.Util (maybeHead)- -- | Download URL via shelling out to @curl@. curl :: String -> IO (Either String BS.ByteString) curl url = do@@ -47,8 +46,9 @@     liftIO $ readProcessWithExitCode "curl" ["-L", url] mempty   case code of     ExitFailure 127 ->-      return $ Left $-      "'" <> unwords ["curl", "-L", url] <> "' failed (program not found?)."+      return $+        Left $+          "'" <> unwords ["curl", "-L", url] <> "' failed (program not found?)."     ExitFailure _ -> do       liftIO $ BS.hPutStr stderr err       return $ Left $ "'" <> unwords ["curl", "-L", url] <> "' failed."@@ -59,7 +59,7 @@ -- fetch them on-demand.  It would be a waste to fetch it information -- for every version of every package if we only actually need a small -- subset of them.-newtype GetManifest m = GetManifest { getManifest :: m PkgManifest }+newtype GetManifest m = GetManifest {getManifest :: m PkgManifest}  instance Show (GetManifest m) where   show _ = "#<revdeps>"@@ -69,24 +69,25 @@  -- | Information about a version of a single package.  The version -- number is stored separately.-data PkgRevInfo m = PkgRevInfo { pkgRevZipballUrl :: T.Text-                               , pkgRevZipballDir :: FilePath-                                 -- ^ The directory inside the zipball-                                 -- containing the @lib@ directory, in-                                 -- which the package files themselves-                                 -- are stored (Based on the package-                                 -- path).-                               , pkgRevCommit :: T.Text-                                 -- ^ The commit ID can be used for-                                 -- verification ("freezing"), by-                                 -- storing what it was at the time this-                                 -- version was last selected.-                               , pkgRevGetManifest :: GetManifest m-                               , pkgRevTime :: UTCTime-                                 -- ^ Timestamp for when the revision-                                 -- was made (rarely used).-                               }-                  deriving (Eq, Show)+data PkgRevInfo m = PkgRevInfo+  { pkgRevZipballUrl :: T.Text,+    -- | The directory inside the zipball+    -- containing the @lib@ directory, in+    -- which the package files themselves+    -- are stored (Based on the package+    -- path).+    pkgRevZipballDir :: FilePath,+    -- | The commit ID can be used for+    -- verification ("freezing"), by+    -- storing what it was at the time this+    -- version was last selected.+    pkgRevCommit :: T.Text,+    pkgRevGetManifest :: GetManifest m,+    -- | Timestamp for when the revision+    -- was made (rarely used).+    pkgRevTime :: UTCTime+  }+  deriving (Eq, Show)  -- | Create memoisation around a 'GetManifest' action to ensure that -- multiple inspections of the same revisions will not result in@@ -94,23 +95,27 @@ memoiseGetManifest :: MonadIO m => GetManifest m -> m (GetManifest m) memoiseGetManifest (GetManifest m) = do   ref <- liftIO $ newIORef Nothing-  return $ GetManifest $ do-    v <- liftIO $ readIORef ref-    case v of Just v' -> return v'-              Nothing -> do-                v' <- m-                liftIO $ writeIORef ref $ Just v'-                return v'+  return $+    GetManifest $ do+      v <- liftIO $ readIORef ref+      case v of+        Just v' -> return v'+        Nothing -> do+          v' <- m+          liftIO $ writeIORef ref $ Just v'+          return v'  -- | Download the zip archive corresponding to a specific package -- version.-downloadZipball :: (MonadLogger m, MonadIO m, MonadFail m) =>-                   PkgRevInfo m -> m Zip.Archive+downloadZipball ::+  (MonadLogger m, MonadIO m, MonadFail m) =>+  PkgRevInfo m ->+  m Zip.Archive downloadZipball info = do   let url = pkgRevZipballUrl info   logMsg $ "Downloading " <> T.unpack url -  let bad = fail . (("When downloading " <> T.unpack url <> ": ")<>)+  let bad = fail . (("When downloading " <> T.unpack url <> ": ") <>)   http <- liftIO $ curl $ T.unpack url   case http of     Left e -> bad e@@ -121,11 +126,12 @@  -- | Information about a package.  The name of the package is stored -- separately.-data PkgInfo m = PkgInfo { pkgVersions :: M.Map SemVer (PkgRevInfo m)-                         , pkgLookupCommit :: Maybe T.Text -> m (PkgRevInfo m)-                           -- ^ Look up information about a specific-                           -- commit, or HEAD in case of Nothing.-                         }+data PkgInfo m = PkgInfo+  { pkgVersions :: M.Map SemVer (PkgRevInfo m),+    -- | Look up information about a specific+    -- commit, or HEAD in case of Nothing.+    pkgLookupCommit :: Maybe T.Text -> m (PkgRevInfo m)+  }  -- | Lookup information about a given version of a package. lookupPkgRev :: SemVer -> PkgInfo m -> Maybe (PkgRevInfo m)@@ -135,31 +141,40 @@ majorRevOfPkg p =   case T.splitOn "@" p of     [p', v] | [(v', "")] <- reads $ T.unpack v -> (p', [v'])-    _                                          -> (p, [0, 1])+    _ -> (p, [0, 1])  -- | Retrieve information about a package based on its package path. -- This uses Semantic Import Versioning when interacting with -- repositories.  For example, a package @github.com/user/repo@ will -- match version 0.* or 1.* tags only, a package -- @github.com/user/repo/v2@ will match 2.* tags, and so forth..-pkgInfo :: (MonadIO m, MonadLogger m, MonadFail m) =>-           PkgPath -> m (Either T.Text (PkgInfo m))+pkgInfo ::+  (MonadIO m, MonadLogger m, MonadFail m) =>+  PkgPath ->+  m (Either T.Text (PkgInfo m)) pkgInfo path   | ["github.com", owner, repo] <- T.splitOn "/" path =-      let (repo', vs) = majorRevOfPkg repo-      in ghPkgInfo owner repo' vs-  | "github.com": owner : repo : _ <- T.splitOn "/" path =-      return $ Left $ T.intercalate "\n"-      [nope, "Do you perhaps mean 'github.com/" <> owner <> "/" <> repo <> "'?"]+    let (repo', vs) = majorRevOfPkg repo+     in ghPkgInfo owner repo' vs+  | "github.com" : owner : repo : _ <- T.splitOn "/" path =+    return $+      Left $+        T.intercalate+          "\n"+          [nope, "Do you perhaps mean 'github.com/" <> owner <> "/" <> repo <> "'?"]   | ["gitlab.com", owner, repo] <- T.splitOn "/" path =-      let (repo', vs) = majorRevOfPkg repo-      in glPkgInfo owner repo' vs-  | "gitlab.com": owner : repo : _ <- T.splitOn "/" path =-      return $ Left $ T.intercalate "\n"-      [nope, "Do you perhaps mean 'gitlab.com/" <> owner <> "/" <> repo <> "'?"]+    let (repo', vs) = majorRevOfPkg repo+     in glPkgInfo owner repo' vs+  | "gitlab.com" : owner : repo : _ <- T.splitOn "/" path =+    return $+      Left $+        T.intercalate+          "\n"+          [nope, "Do you perhaps mean 'gitlab.com/" <> owner <> "/" <> repo <> "'?"]   | otherwise =-      return $ Left nope-  where nope = "Unable to handle package paths of the form '" <> path <> "'"+    return $ Left nope+  where+    nope = "Unable to handle package paths of the form '" <> path <> "'"  -- For GitHub, we unfortunately cannot use the (otherwise very nice) -- GitHub web API, because it is rate-limited to 60 requests per hour@@ -182,14 +197,23 @@ -- couple of generic functions that are used to implement support for -- both. -ghglRevGetManifest :: (MonadIO m, MonadLogger m, MonadFail m) =>-                      T.Text -> T.Text -> T.Text -> T.Text -> GetManifest m+ghglRevGetManifest ::+  (MonadIO m, MonadLogger m, MonadFail m) =>+  T.Text ->+  T.Text ->+  T.Text ->+  T.Text ->+  GetManifest m ghglRevGetManifest url owner repo tag = GetManifest $ do   logMsg $ "Downloading package manifest from " <> url -  let path = T.unpack $ owner <> "/" <> repo <> "@" <>-             tag <> "/" <> T.pack futharkPkg-      msg = (("When reading " <> path <> ": ")<>)+  let path =+        T.unpack $+          owner <> "/" <> repo <> "@"+            <> tag+            <> "/"+            <> T.pack futharkPkg+      msg = (("When reading " <> path <> ": ") <>)   http <- liftIO $ curl $ T.unpack url   case http of     Left e -> fail e@@ -201,75 +225,142 @@             Left e -> fail $ msg $ errorBundlePretty e             Right pm -> return pm -ghglLookupCommit :: (MonadIO m, MonadLogger m, MonadFail m) =>-                    T.Text -> T.Text -> (T.Text -> T.Text)-                 -> T.Text -> T.Text -> T.Text -> T.Text -> T.Text -> m (PkgRevInfo m)+ghglLookupCommit ::+  (MonadIO m, MonadLogger m, MonadFail m) =>+  T.Text ->+  T.Text ->+  (T.Text -> T.Text) ->+  T.Text ->+  T.Text ->+  T.Text ->+  T.Text ->+  T.Text ->+  m (PkgRevInfo m) ghglLookupCommit archive_url manifest_url mk_zip_dir owner repo d ref hash = do   gd <- memoiseGetManifest $ ghglRevGetManifest manifest_url owner repo ref   let dir = Posix.addTrailingPathSeparator $ T.unpack $ mk_zip_dir d   time <- liftIO getCurrentTime -- FIXME   return $ PkgRevInfo archive_url dir hash gd time -ghglPkgInfo :: (MonadIO m, MonadLogger m, MonadFail m) =>-               T.Text-            -> (T.Text -> T.Text) -> (T.Text -> T.Text)  -> (T.Text -> T.Text)-            -> T.Text -> T.Text -> [Word] -> m (Either T.Text (PkgInfo m))+ghglPkgInfo ::+  (MonadIO m, MonadLogger m, MonadFail m) =>+  T.Text ->+  (T.Text -> T.Text) ->+  (T.Text -> T.Text) ->+  (T.Text -> T.Text) ->+  T.Text ->+  T.Text ->+  [Word] ->+  m (Either T.Text (PkgInfo m)) ghglPkgInfo repo_url mk_archive_url mk_manifest_url mk_zip_dir owner repo versions = do   logMsg $ "Retrieving list of tags from " <> repo_url   remote_lines <- T.lines . T.decodeUtf8 <$> gitCmd ["ls-remote", T.unpack repo_url] -  head_ref <- maybe (fail $ "Cannot find HEAD ref for " <> T.unpack repo_url) return $-              maybeHead $ mapMaybe isHeadRef remote_lines+  head_ref <-+    maybe (fail $ "Cannot find HEAD ref for " <> T.unpack repo_url) return $+      maybeHead $ mapMaybe isHeadRef remote_lines   let def = fromMaybe head_ref    rev_info <- M.fromList . catMaybes <$> mapM revInfo remote_lines -  return $ Right $ PkgInfo rev_info $ \r ->-    ghglLookupCommit-    (mk_archive_url (def r)) (mk_manifest_url (def r)) mk_zip_dir-    owner repo (def r) (def r) (def r)-  where isHeadRef l-          | [hash, "HEAD"] <- T.words l = Just hash-          | otherwise                   = Nothing+  return $+    Right $+      PkgInfo rev_info $ \r ->+        ghglLookupCommit+          (mk_archive_url (def r))+          (mk_manifest_url (def r))+          mk_zip_dir+          owner+          repo+          (def r)+          (def r)+          (def r)+  where+    isHeadRef l+      | [hash, "HEAD"] <- T.words l = Just hash+      | otherwise = Nothing -        revInfo l-          | [hash, ref] <- T.words l,-            ["refs", "tags", t] <- T.splitOn "/" ref,-            "v" `T.isPrefixOf` t,-            Right v <- semver $ T.drop 1 t,-            _svMajor v `elem` versions = do-              pinfo <- ghglLookupCommit-                       (mk_archive_url t) (mk_manifest_url t) mk_zip_dir-                       owner repo (prettySemVer v) t hash-              return $ Just (v, pinfo)-          | otherwise = return Nothing+    revInfo l+      | [hash, ref] <- T.words l,+        ["refs", "tags", t] <- T.splitOn "/" ref,+        "v" `T.isPrefixOf` t,+        Right v <- semver $ T.drop 1 t,+        _svMajor v `elem` versions = do+        pinfo <-+          ghglLookupCommit+            (mk_archive_url t)+            (mk_manifest_url t)+            mk_zip_dir+            owner+            repo+            (prettySemVer v)+            t+            hash+        return $ Just (v, pinfo)+      | otherwise = return Nothing -ghPkgInfo :: (MonadIO m, MonadLogger m, MonadFail m) =>-             T.Text -> T.Text -> [Word] -> m (Either T.Text (PkgInfo m))+ghPkgInfo ::+  (MonadIO m, MonadLogger m, MonadFail m) =>+  T.Text ->+  T.Text ->+  [Word] ->+  m (Either T.Text (PkgInfo m)) ghPkgInfo owner repo versions =-  ghglPkgInfo repo_url mk_archive_url mk_manifest_url mk_zip_dir-  owner repo versions-  where repo_url = "https://github.com/" <> owner <> "/" <> repo-        mk_archive_url r = repo_url <> "/archive/" <> r <> ".zip"-        mk_manifest_url r = "https://raw.githubusercontent.com/" <>-                            owner <> "/" <> repo <> "/" <>-                            r <> "/" <> T.pack futharkPkg-        mk_zip_dir r = repo <> "-" <> r+  ghglPkgInfo+    repo_url+    mk_archive_url+    mk_manifest_url+    mk_zip_dir+    owner+    repo+    versions+  where+    repo_url = "https://github.com/" <> owner <> "/" <> repo+    mk_archive_url r = repo_url <> "/archive/" <> r <> ".zip"+    mk_manifest_url r =+      "https://raw.githubusercontent.com/"+        <> owner+        <> "/"+        <> repo+        <> "/"+        <> r+        <> "/"+        <> T.pack futharkPkg+    mk_zip_dir r = repo <> "-" <> r -glPkgInfo :: (MonadIO m, MonadLogger m, MonadFail m) =>-             T.Text -> T.Text -> [Word] -> m (Either T.Text (PkgInfo m))+glPkgInfo ::+  (MonadIO m, MonadLogger m, MonadFail m) =>+  T.Text ->+  T.Text ->+  [Word] ->+  m (Either T.Text (PkgInfo m)) glPkgInfo owner repo versions =-  ghglPkgInfo repo_url mk_archive_url mk_manifest_url mk_zip_dir-  owner repo versions-  where base_url = "https://gitlab.com/" <> owner <> "/" <> repo-        repo_url = base_url <> ".git"-        mk_archive_url r = base_url <> "/-/archive/" <> r <>-                           "/" <> repo <> "-" <> r <> ".zip"-        mk_manifest_url r = base_url <> "/raw/" <>-                            r <> "/" <> T.pack futharkPkg-        mk_zip_dir r-          | Right _ <- semver r = repo <> "-v" <> r-          | otherwise = repo <> "-" <> r+  ghglPkgInfo+    repo_url+    mk_archive_url+    mk_manifest_url+    mk_zip_dir+    owner+    repo+    versions+  where+    base_url = "https://gitlab.com/" <> owner <> "/" <> repo+    repo_url = base_url <> ".git"+    mk_archive_url r =+      base_url <> "/-/archive/" <> r+        <> "/"+        <> repo+        <> "-"+        <> r+        <> ".zip"+    mk_manifest_url r =+      base_url <> "/raw/"+        <> r+        <> "/"+        <> T.pack futharkPkg+    mk_zip_dir r+      | Right _ <- semver r = repo <> "-v" <> r+      | otherwise = repo <> "-" <> r  -- | A package registry is a mapping from package paths to information -- about the package.  It is unlikely that any given registry is@@ -297,8 +388,10 @@   modifyPkgRegistry f = putPkgRegistry . f =<< getPkgRegistry  -- | Given a package path, look up information about that package.-lookupPackage :: MonadPkgRegistry m =>-                 PkgPath -> m (PkgInfo m)+lookupPackage ::+  MonadPkgRegistry m =>+  PkgPath ->+  m (PkgInfo m) lookupPackage p = do   r@(PkgRegistry m) <- getPkgRegistry   case lookupKnownPackage p r of@@ -312,50 +405,67 @@           putPkgRegistry $ PkgRegistry $ M.insert p pinfo m           return pinfo -lookupPackageCommit :: MonadPkgRegistry m =>-                       PkgPath -> Maybe T.Text -> m (SemVer, PkgRevInfo m)+lookupPackageCommit ::+  MonadPkgRegistry m =>+  PkgPath ->+  Maybe T.Text ->+  m (SemVer, PkgRevInfo m) lookupPackageCommit p ref = do   pinfo <- lookupPackage p   rev_info <- pkgLookupCommit pinfo ref-  let timestamp = T.pack $ formatTime defaultTimeLocale "%Y%m%d%H%M%S" $-                  pkgRevTime rev_info+  let timestamp =+        T.pack $+          formatTime defaultTimeLocale "%Y%m%d%H%M%S" $+            pkgRevTime rev_info       v = commitVersion timestamp $ pkgRevCommit rev_info-      pinfo' = pinfo { pkgVersions = M.insert v rev_info $ pkgVersions pinfo }+      pinfo' = pinfo {pkgVersions = M.insert v rev_info $ pkgVersions pinfo}   modifyPkgRegistry $ \(PkgRegistry m) ->     PkgRegistry $ M.insert p pinfo' m   return (v, rev_info)  -- | Look up information about a specific version of a package.-lookupPackageRev :: MonadPkgRegistry m =>-                    PkgPath -> SemVer -> m (PkgRevInfo m)+lookupPackageRev ::+  MonadPkgRegistry m =>+  PkgPath ->+  SemVer ->+  m (PkgRevInfo m) lookupPackageRev p v   | Just commit <- isCommitVersion v =-      snd <$> lookupPackageCommit p (Just commit)+    snd <$> lookupPackageCommit p (Just commit)   | otherwise = do-  pinfo <- lookupPackage p-  case lookupPkgRev v pinfo of-    Nothing ->-      let versions = case M.keys $ pkgVersions pinfo of-                       [] -> "Package " <> p <> " has no versions.  Invalid package path?"-                       ks -> "Known versions: " <>-                             T.concat (intersperse ", " $ map prettySemVer ks)-          major | (_, vs) <- majorRevOfPkg p,-                  _svMajor v `notElem` vs =-                    "\nFor major version " <> T.pack (show (_svMajor v)) <>-                    ", use package path " <> p <> "@" <> T.pack (show (_svMajor v))-                | otherwise = mempty-      in fail $ T.unpack $-         "package " <> p <> " does not have a version " <> prettySemVer v <> ".\n" <>-         versions <> major-    Just v' -> return v'+    pinfo <- lookupPackage p+    case lookupPkgRev v pinfo of+      Nothing ->+        let versions = case M.keys $ pkgVersions pinfo of+              [] -> "Package " <> p <> " has no versions.  Invalid package path?"+              ks ->+                "Known versions: "+                  <> T.concat (intersperse ", " $ map prettySemVer ks)+            major+              | (_, vs) <- majorRevOfPkg p,+                _svMajor v `notElem` vs =+                "\nFor major version " <> T.pack (show (_svMajor v))+                  <> ", use package path "+                  <> p+                  <> "@"+                  <> T.pack (show (_svMajor v))+              | otherwise = mempty+         in fail $+              T.unpack $+                "package " <> p <> " does not have a version " <> prettySemVer v <> ".\n"+                  <> versions+                  <> major+      Just v' -> return v'  -- | Find the newest version of a package.-lookupNewestRev :: MonadPkgRegistry m =>-                   PkgPath -> m SemVer+lookupNewestRev ::+  MonadPkgRegistry m =>+  PkgPath ->+  m SemVer lookupNewestRev p = do   pinfo <- lookupPackage p   case M.keys $ pkgVersions pinfo of     [] -> do       logMsg $ "Package " <> p <> " has no released versions.  Using HEAD."       fst <$> lookupPackageCommit p Nothing-    v:vs -> return $ foldl' max v vs+    v : vs -> return $ foldl' max v vs
src/Futhark/Pkg/Solve.hs view
@@ -1,26 +1,25 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+ -- | Dependency solver -- -- This is a relatively simple problem due to the choice of the -- Minimum Package Version algorithm.  In fact, the only failure mode -- is referencing an unknown package or revision. module Futhark.Pkg.Solve-  ( solveDeps-  , solveDepsPure-  , PkgRevDepInfo-  ) where+  ( solveDeps,+    solveDepsPure,+    PkgRevDepInfo,+  )+where +import Control.Monad.Free.Church import Control.Monad.State-import qualified Data.Set as S import qualified Data.Map as M+import qualified Data.Set as S import qualified Data.Text as T--import Control.Monad.Free.Church- import Futhark.Pkg.Info import Futhark.Pkg.Types- import Prelude  data PkgOp a = OpGetDeps PkgPath SemVer (Maybe T.Text) (PkgRevDeps -> a)@@ -32,7 +31,7 @@ -- that are not reachable from the root.  Also contains the -- dependencies of each package. newtype RoughBuildList = RoughBuildList (M.Map PkgPath (SemVer, [PkgPath]))-                       deriving (Show)+  deriving (Show)  emptyRoughBuildList :: RoughBuildList emptyRoughBuildList = RoughBuildList mempty@@ -45,12 +44,13 @@ buildList :: S.Set PkgPath -> RoughBuildList -> BuildList buildList roots (RoughBuildList pkgs) =   BuildList $ execState (mapM_ addPkg roots) mempty-  where addPkg p = case M.lookup p pkgs of-                     Nothing -> return ()-                     Just (v, deps) -> do-                       listed <- gets $ M.member p-                       modify $ M.insert p v-                       unless listed $ mapM_ addPkg deps+  where+    addPkg p = case M.lookup p pkgs of+      Nothing -> return ()+      Just (v, deps) -> do+        listed <- gets $ M.member p+        modify $ M.insert p v+        unless listed $ mapM_ addPkg deps  type SolveM = StateT RoughBuildList (F PkgOp) @@ -62,38 +62,51 @@ -- dependencies. doSolveDeps :: PkgRevDeps -> SolveM () doSolveDeps (PkgRevDeps deps) = mapM_ add $ M.toList deps-  where add (p, (v, maybe_h)) = do-          RoughBuildList l <- get-          case M.lookup p l of-            -- Already satisfied?-            Just (cur_v, _) | v <= cur_v -> return ()-            -- No; add 'p' and its dependencies.-            _ -> do-              PkgRevDeps p_deps <- getDeps p v maybe_h-              put $ RoughBuildList $ M.insert p (v, M.keys p_deps) l-              mapM_ add $ M.toList p_deps+  where+    add (p, (v, maybe_h)) = do+      RoughBuildList l <- get+      case M.lookup p l of+        -- Already satisfied?+        Just (cur_v, _) | v <= cur_v -> return ()+        -- No; add 'p' and its dependencies.+        _ -> do+          PkgRevDeps p_deps <- getDeps p v maybe_h+          put $ RoughBuildList $ M.insert p (v, M.keys p_deps) l+          mapM_ add $ M.toList p_deps  -- | Run the solver, producing both a package registry containing -- a cache of the lookups performed, as well as a build list.-solveDeps :: MonadPkgRegistry m =>-             PkgRevDeps -> m BuildList-solveDeps deps = buildList (depRoots deps) <$> runF-                 (execStateT (doSolveDeps deps) emptyRoughBuildList)-                 return step-  where step (OpGetDeps p v h c) = do-          pinfo <- lookupPackageRev p v+solveDeps ::+  MonadPkgRegistry m =>+  PkgRevDeps ->+  m BuildList+solveDeps deps =+  buildList (depRoots deps)+    <$> runF+      (execStateT (doSolveDeps deps) emptyRoughBuildList)+      return+      step+  where+    step (OpGetDeps p v h c) = do+      pinfo <- lookupPackageRev p v -          checkHash p v pinfo h+      checkHash p v pinfo h -          d <- fmap pkgRevDeps . getManifest $ pkgRevGetManifest pinfo-          c d+      d <- fmap pkgRevDeps . getManifest $ pkgRevGetManifest pinfo+      c d -        checkHash _ _ _ Nothing = return ()-        checkHash p v pinfo (Just h)-          | h == pkgRevCommit pinfo = return ()-          | otherwise = fail $ T.unpack $ "Package " <> p <> " " <> prettySemVer v <>-                        " has commit hash " <> pkgRevCommit pinfo <>-                        ", but expected " <> h <> " from package manifest."+    checkHash _ _ _ Nothing = return ()+    checkHash p v pinfo (Just h)+      | h == pkgRevCommit pinfo = return ()+      | otherwise =+        fail $+          T.unpack $+            "Package " <> p <> " " <> prettySemVer v+              <> " has commit hash "+              <> pkgRevCommit pinfo+              <> ", but expected "+              <> h+              <> " from package manifest."  -- | A mapping of package revisions to the dependencies of that -- package.  Can be considered a 'PkgRegistry' without the option of@@ -104,10 +117,14 @@ -- | Perform package resolution with only pre-known information.  This -- is useful for testing. solveDepsPure :: PkgRevDepInfo -> PkgRevDeps -> Either T.Text BuildList-solveDepsPure r deps = buildList (depRoots deps) <$> runF-                       (execStateT (doSolveDeps deps) emptyRoughBuildList)-                       Right step-  where step (OpGetDeps p v _ c) = do-          let errmsg = "Unknown package/version: " <> p <> "-" <> prettySemVer v-          d <- maybe (Left errmsg) Right $ M.lookup (p,v) r-          c d+solveDepsPure r deps =+  buildList (depRoots deps)+    <$> runF+      (execStateT (doSolveDeps deps) emptyRoughBuildList)+      Right+      step+  where+    step (OpGetDeps p v _ c) = do+      let errmsg = "Unknown package/version: " <> p <> "-" <> prettySemVer v+      d <- maybe (Left errmsg) Right $ M.lookup (p, v) r+      c d
src/Futhark/Pkg/Types.hs view
@@ -1,57 +1,57 @@ {-# LANGUAGE OverloadedStrings #-}+ -- | Types (and a few other simple definitions) for futhark-pkg. module Futhark.Pkg.Types-  ( PkgPath-  , pkgPathFilePath-  , PkgRevDeps(..)-  , module Data.Versions+  ( PkgPath,+    pkgPathFilePath,+    PkgRevDeps (..),+    module Data.Versions, -  -- * Versions-  , commitVersion-  , isCommitVersion-  , parseVersion+    -- * Versions+    commitVersion,+    isCommitVersion,+    parseVersion, -  -- * Package manifests-  , PkgManifest(..)-  , newPkgManifest-  , pkgRevDeps-  , pkgDir-  , addRequiredToManifest-  , removeRequiredFromManifest-  , prettyPkgManifest-  , Comment-  , Commented(..)-  , Required(..)-  , futharkPkg+    -- * Package manifests+    PkgManifest (..),+    newPkgManifest,+    pkgRevDeps,+    pkgDir,+    addRequiredToManifest,+    removeRequiredFromManifest,+    prettyPkgManifest,+    Comment,+    Commented (..),+    Required (..),+    futharkPkg, -  -- * Parsing package manifests-  , parsePkgManifest-  , parsePkgManifestFromFile-  , errorBundlePretty+    -- * Parsing package manifests+    parsePkgManifest,+    parsePkgManifestFromFile,+    errorBundlePretty, -  -- * Build list-  , BuildList(..)-  , prettyBuildList-  ) where+    -- * Build list+    BuildList (..),+    prettyBuildList,+  )+where  import Control.Applicative import Control.Monad import Data.Either import Data.Foldable import Data.List (sortOn)+import qualified Data.Map as M import Data.Maybe-import Data.Traversable-import Data.Void import qualified Data.Text as T import qualified Data.Text.IO as T-import qualified Data.Map as M+import Data.Traversable+import Data.Versions (SemVer (..), VUnit (..), prettySemVer, semver)+import Data.Void import System.FilePath import qualified System.FilePath.Posix as Posix--import Data.Versions (semver, SemVer(..), VUnit(..), prettySemVer) import Text.Megaparsec hiding (many, some) import Text.Megaparsec.Char- import Prelude  -- | A package path is a unique identifier for a package, for example@@ -86,14 +86,15 @@  semver' :: Parsec Void T.Text SemVer semver' = SemVer <$> majorP <*> minorP <*> patchP <*> preRel <*> metaData-  where majorP = digitsP <* char '.'-        minorP = majorP-        patchP = digitsP-        digitsP = read <$> ((T.unpack <$> string "0") <|> some digitChar)-        preRel = maybe [] pure <$> optional preRel'-        preRel' = char '-' *> (pure . Str . T.pack <$> some digitChar)-        metaData = maybe [] pure <$> optional metaData'-        metaData' = char '+' *> (pure . Str . T.pack <$> some alphaNumChar)+  where+    majorP = digitsP <* char '.'+    minorP = majorP+    patchP = digitsP+    digitsP = read <$> ((T.unpack <$> string "0") <|> some digitChar)+    preRel = maybe [] pure <$> optional preRel'+    preRel' = char '-' *> (pure . Str . T.pack <$> some digitChar)+    metaData = maybe [] pure <$> optional metaData'+    metaData' = char '+' *> (pure . Str . T.pack <$> some alphaNumChar)  -- | The dependencies of a (revision of a) package is a mapping from -- package paths to minimum versions (and an optional hash pinning).@@ -114,10 +115,11 @@ -- | Wraps a value with an annotation of preceding line comments. -- This is important to our goal of being able to programmatically -- modify the @futhark.pkg@ file while keeping comments intact.-data Commented a = Commented { comments :: [Comment]-                             , commented :: a-                             }-                   deriving (Show, Eq)+data Commented a = Commented+  { comments :: [Comment],+    commented :: a+  }+  deriving (Show, Eq)  instance Functor Commented where   fmap = fmapDefault@@ -130,17 +132,17 @@  -- | An entry in the @required@ section of a @futhark.pkg@ file. data Required = Required-                { requiredPkg :: PkgPath-                  -- ^ Name of the required package.-                , requiredPkgRev :: SemVer-                  -- ^ The minimum revision.-                , requiredHash :: Maybe T.Text-                  -- ^ An optional hash indicating what-                  -- this revision looked like the last-                  -- time we saw it.  Used for integrity-                  -- checking.-                }-                deriving (Show, Eq)+  { -- | Name of the required package.+    requiredPkg :: PkgPath,+    -- | The minimum revision.+    requiredPkgRev :: SemVer,+    -- | An optional hash indicating what+    -- this revision looked like the last+    -- time we saw it.  Used for integrity+    -- checking.+    requiredHash :: Maybe T.Text+  }+  deriving (Show, Eq)  -- | The name of the file containing the futhark-pkg manifest. futharkPkg :: FilePath@@ -149,12 +151,13 @@ -- | A structure corresponding to a @futhark.pkg@ file, including -- comments.  It is an invariant that duplicate required packages do -- not occcur (the parser will verify this).-data PkgManifest = PkgManifest { manifestPkgPath :: Commented (Maybe PkgPath)-                               -- ^ The name of the package.-                               , manifestRequire :: Commented [Either Comment Required]-                               , manifestEndComments :: [Comment]-                               }-                   deriving (Show, Eq)+data PkgManifest = PkgManifest+  { -- | The name of the package.+    manifestPkgPath :: Commented (Maybe PkgPath),+    manifestRequire :: Commented [Either Comment Required],+    manifestEndComments :: [Comment]+  }+  deriving (Show, Eq)  -- | Possibly given a package path, construct an otherwise-empty manifest file. newPkgManifest :: Maybe PkgPath -> PkgManifest@@ -165,54 +168,70 @@ -- @futhark.pkg@ file. prettyPkgManifest :: PkgManifest -> T.Text prettyPkgManifest (PkgManifest name required endcs) =-  T.unlines $ concat [ prettyComments name-                     , maybe [] (pure . ("package "<>) . (<>"\n")) $ commented name-                     , prettyComments required-                     , ["require {"]-                     , map (("  "<>) . prettyRequired) $ commented required-                     , ["}"]-                     , map prettyComment endcs-                     ]-  where prettyComments = map prettyComment . comments-        prettyComment = ("--"<>)-        prettyRequired (Left c) = prettyComment c-        prettyRequired (Right (Required p r h)) =-          T.unwords $ catMaybes [Just p,-                                 Just $ prettySemVer r,-                                 ("#"<>) <$> h]+  T.unlines $+    concat+      [ prettyComments name,+        maybe [] (pure . ("package " <>) . (<> "\n")) $ commented name,+        prettyComments required,+        ["require {"],+        map (("  " <>) . prettyRequired) $ commented required,+        ["}"],+        map prettyComment endcs+      ]+  where+    prettyComments = map prettyComment . comments+    prettyComment = ("--" <>)+    prettyRequired (Left c) = prettyComment c+    prettyRequired (Right (Required p r h)) =+      T.unwords $+        catMaybes+          [ Just p,+            Just $ prettySemVer r,+            ("#" <>) <$> h+          ]  -- | The required packages listed in a package manifest. pkgRevDeps :: PkgManifest -> PkgRevDeps-pkgRevDeps = PkgRevDeps . M.fromList . mapMaybe onR .-             commented .  manifestRequire-  where onR (Right r) = Just (requiredPkg r, (requiredPkgRev r, requiredHash r))-        onR (Left _) = Nothing+pkgRevDeps =+  PkgRevDeps . M.fromList . mapMaybe onR+    . commented+    . manifestRequire+  where+    onR (Right r) = Just (requiredPkg r, (requiredPkgRev r, requiredHash r))+    onR (Left _) = Nothing  -- | Where in the corresponding repository archive we can expect to -- find the package files. pkgDir :: PkgManifest -> Maybe Posix.FilePath-pkgDir = fmap (Posix.addTrailingPathSeparator . ("lib" Posix.</>) .-               T.unpack) . commented . manifestPkgPath+pkgDir =+  fmap+    ( Posix.addTrailingPathSeparator . ("lib" Posix.</>)+        . T.unpack+    )+    . commented+    . manifestPkgPath  -- | Add new required package to the package manifest.  If the package -- was already present, return the old version. addRequiredToManifest :: Required -> PkgManifest -> (PkgManifest, Maybe Required) addRequiredToManifest new_r pm =   let (old, requires') = mapAccumL add Nothing $ commented $ manifestRequire pm-  in (if isJust old-      then pm { manifestRequire = requires' <$ manifestRequire pm }-      else pm { manifestRequire = (++[Right new_r]) <$> manifestRequire pm },-      old)-  where add acc (Left c) = (acc, Left c)-        add acc (Right r)-          | requiredPkg r == requiredPkg new_r = (Just r, Right new_r)-          | otherwise                          = (acc, Right r)+   in ( if isJust old+          then pm {manifestRequire = requires' <$ manifestRequire pm}+          else pm {manifestRequire = (++ [Right new_r]) <$> manifestRequire pm},+        old+      )+  where+    add acc (Left c) = (acc, Left c)+    add acc (Right r)+      | requiredPkg r == requiredPkg new_r = (Just r, Right new_r)+      | otherwise = (acc, Right r)  -- | Check if the manifest specifies a required package with the given -- package path. requiredInManifest :: PkgPath -> PkgManifest -> Maybe Required requiredInManifest p =-  find ((==p) . requiredPkg) . rights . commented . manifestRequire+  find ((== p) . requiredPkg) . rights . commented . manifestRequire  -- | Remove a required package from the manifest.  Returns 'Nothing' -- if the package was not found in the manifest, and otherwise the new@@ -220,9 +239,12 @@ removeRequiredFromManifest :: PkgPath -> PkgManifest -> Maybe (PkgManifest, Required) removeRequiredFromManifest p pm = do   r <- requiredInManifest p pm-  return (pm { manifestRequire = filter (not . matches) <$> manifestRequire pm },-          r)-  where matches = either (const False) ((==p) . requiredPkg)+  return+    ( pm {manifestRequire = filter (not . matches) <$> manifestRequire pm},+      r+    )+  where+    matches = either (const False) ((== p) . requiredPkg)  --- Parsing futhark.pkg. @@ -234,42 +256,50 @@   p <- optional $ lexstr "package" *> pPkgPath   space   c2 <- pComments-  required <- (lexstr "require" *>-               braces (many $ (Left <$> pComment) <|> (Right <$> pRequired)))-              <|> pure []+  required <-+    ( lexstr "require"+        *> braces (many $ (Left <$> pComment) <|> (Right <$> pRequired))+      )+      <|> pure []   c3 <- pComments   eof   return $ PkgManifest (Commented c1 p) (Commented c2 required) c3-  where lexeme :: Parser a -> Parser a-        lexeme p = p <* space--        lexeme' p = p <* spaceNoEol+  where+    lexeme :: Parser a -> Parser a+    lexeme p = p <* space -        lexstr :: T.Text -> Parser ()-        lexstr = void . try . lexeme . string+    lexeme' p = p <* spaceNoEol -        braces :: Parser a -> Parser a-        braces p = lexstr "{" *> p <* lexstr "}"+    lexstr :: T.Text -> Parser ()+    lexstr = void . try . lexeme . string -        spaceNoEol = many $ oneOf (" \t" :: String)+    braces :: Parser a -> Parser a+    braces p = lexstr "{" *> p <* lexstr "}" -        pPkgPath = T.pack <$> some (alphaNumChar <|> oneOf ("@-/.:" :: String))-                   <?> "package path"+    spaceNoEol = many $ oneOf (" \t" :: String) -        pRequired = space *> (Required <$> lexeme' pPkgPath-                                       <*> lexeme' semver'-                                       <*> optional (lexeme' pHash)) <* space-                    <?> "package requirement"+    pPkgPath =+      T.pack <$> some (alphaNumChar <|> oneOf ("@-/.:" :: String))+        <?> "package path" -        pHash = char '#' *> (T.pack <$> some alphaNumChar)+    pRequired =+      space+        *> ( Required <$> lexeme' pPkgPath+               <*> lexeme' semver'+               <*> optional (lexeme' pHash)+           )+        <* space+        <?> "package requirement" -        pComment = lexeme $ T.pack <$> (string "--" >> anySingle `manyTill` (void eol <|> eof))+    pHash = char '#' *> (T.pack <$> some alphaNumChar) -        pComments :: Parser [Comment]-        pComments = catMaybes <$> many (comment <|> blankLine)-          where comment = Just <$> pComment-                blankLine = some spaceChar >> pure Nothing+    pComment = lexeme $ T.pack <$> (string "--" >> anySingle `manyTill` (void eol <|> eof)) +    pComments :: Parser [Comment]+    pComments = catMaybes <$> many (comment <|> blankLine)+      where+        comment = Just <$> pComment+        blankLine = some spaceChar >> pure Nothing  -- | Parse a text as a 'PkgManifest'.  The 'FilePath' is used for any error messages. parsePkgManifest :: FilePath -> T.Text -> Either (ParseErrorBundle T.Text Void) PkgManifest@@ -285,11 +315,12 @@  -- | A mapping from package paths to their chosen revisions.  This is -- the result of the version solver.-newtype BuildList = BuildList { unBuildList :: M.Map PkgPath SemVer }-                  deriving (Eq, Show)+newtype BuildList = BuildList {unBuildList :: M.Map PkgPath SemVer}+  deriving (Eq, Show)  -- | Prettyprint a build list; one package per line and -- newline-terminated. prettyBuildList :: BuildList -> T.Text prettyBuildList (BuildList m) = T.unlines $ map f $ sortOn fst $ M.toList m-  where f (p, v) = T.unwords [p, "=>", prettySemVer v]+  where+    f (p, v) = T.unwords [p, "=>", prettySemVer v]
src/Futhark/Test.hs view
@@ -1,95 +1,97 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-}+ -- | Facilities for reading Futhark test programs.  A Futhark test -- program is an ordinary Futhark program where an initial comment -- block specifies input- and output-sets. module Futhark.Test-       ( testSpecFromFile-       , testSpecFromFileOrDie-       , testSpecsFromPaths-       , testSpecsFromPathsOrDie-       , valuesFromByteString-       , getValues-       , getValuesBS-       , compareValues-       , compareValues1-       , testRunReferenceOutput-       , getExpectedResult-       , compileProgram-       , runProgram-       , ensureReferenceOutput-       , determineTuning-       , binaryName-       , Mismatch--       , ProgramTest (..)-       , StructureTest (..)-       , StructurePipeline (..)-       , WarningTest (..)-       , TestAction (..)-       , ExpectedError (..)-       , InputOutputs (..)-       , TestRun (..)-       , ExpectedResult (..)-       , Success(..)-       , Values (..)-       , Value-       )-       where+  ( testSpecFromFile,+    testSpecFromFileOrDie,+    testSpecsFromPaths,+    testSpecsFromPathsOrDie,+    valuesFromByteString,+    getValues,+    getValuesBS,+    compareValues,+    compareValues1,+    testRunReferenceOutput,+    getExpectedResult,+    compileProgram,+    runProgram,+    ensureReferenceOutput,+    determineTuning,+    binaryName,+    Mismatch,+    ProgramTest (..),+    StructureTest (..),+    StructurePipeline (..),+    WarningTest (..),+    TestAction (..),+    ExpectedError (..),+    InputOutputs (..),+    TestRun (..),+    ExpectedResult (..),+    Success (..),+    Values (..),+    Value,+  )+where +import Codec.Compression.GZip+import Codec.Compression.Zlib.Internal (DecompressError) import Control.Applicative-import qualified Data.ByteString.Lazy as BS-import qualified Data.ByteString as SBS import Control.Exception (catch)+import qualified Control.Exception.Base as E import Control.Monad import Control.Monad.Except-import qualified Data.Map.Strict as M+import qualified Data.ByteString as SBS+import qualified Data.ByteString.Lazy as BS import Data.Char import Data.Functor-import Data.Maybe import Data.List (foldl')+import qualified Data.Map.Strict as M+import Data.Maybe import qualified Data.Text as T-import qualified Data.Text.IO as T import qualified Data.Text.Encoding as T+import qualified Data.Text.IO as T import Data.Void-import System.FilePath-import Codec.Compression.GZip-import Codec.Compression.Zlib.Internal (DecompressError)-import qualified Control.Exception.Base as E--import Text.Megaparsec hiding (many, some)-import Text.Megaparsec.Char-import Text.Regex.TDFA-import System.Directory-import System.Exit-import System.Process.ByteString (readProcessWithExitCode)-import System.IO (withFile, IOMode(..), hFileSize, hClose)-import System.IO.Error-import System.IO.Temp--import Prelude- import Futhark.Analysis.Metrics import Futhark.IR.Primitive-       (IntType(..), intValue, intByteSize,-        FloatType(..), floatValue, floatByteSize)+  ( FloatType (..),+    IntType (..),+    floatByteSize,+    floatValue,+    intByteSize,+    intValue,+  ) import Futhark.Test.Values import Futhark.Util (directoryContents, pmapIO) import Futhark.Util.Pretty (pretty, prettyText)-import Language.Futhark.Syntax (PrimType(..), PrimValue(..))-import Language.Futhark.Prop (primValueType, primByteSize)+import Language.Futhark.Prop (primByteSize, primValueType)+import Language.Futhark.Syntax (PrimType (..), PrimValue (..))+import System.Directory+import System.Exit+import System.FilePath+import System.IO (IOMode (..), hClose, hFileSize, withFile)+import System.IO.Error+import System.IO.Temp+import System.Process.ByteString (readProcessWithExitCode)+import Text.Megaparsec hiding (many, some)+import Text.Megaparsec.Char+import Text.Regex.TDFA+import Prelude  -- | Description of a test to be carried out on a Futhark program. -- The Futhark program is stored separately.-data ProgramTest =-  ProgramTest { testDescription ::-                   T.Text-              , testTags ::-                   [T.Text]-              , testAction ::-                   TestAction-              }+data ProgramTest = ProgramTest+  { testDescription ::+      T.Text,+    testTags ::+      [T.Text],+    testAction ::+      TestAction+  }   deriving (Show)  -- | How to test a program.@@ -99,29 +101,33 @@   deriving (Show)  -- | Input and output pairs for some entry point(s).-data InputOutputs = InputOutputs { iosEntryPoint :: T.Text-                                 , iosTestRuns :: [TestRun] }+data InputOutputs = InputOutputs+  { iosEntryPoint :: T.Text,+    iosTestRuns :: [TestRun]+  }   deriving (Show)  -- | The error expected for a negative test.-data ExpectedError = AnyError-                   | ThisError T.Text Regex+data ExpectedError+  = AnyError+  | ThisError T.Text Regex  instance Show ExpectedError where   show AnyError = "AnyError"   show (ThisError r _) = "ThisError " ++ show r  -- | How a program can be transformed.-data StructurePipeline = KernelsPipeline-                       | SOACSPipeline-                       | SequentialCpuPipeline-                       | GpuPipeline-                       deriving (Show)+data StructurePipeline+  = KernelsPipeline+  | SOACSPipeline+  | SequentialCpuPipeline+  | GpuPipeline+  deriving (Show)  -- | A structure test specifies a compilation pipeline, as well as -- metrics for the program coming out the other end. data StructureTest = StructureTest StructurePipeline AstMetrics-                     deriving (Show)+  deriving (Show)  -- | A warning test requires that a warning matching the regular -- expression is produced.  The program must also compile succesfully.@@ -132,27 +138,29 @@  -- | A condition for execution, input, and expected result. data TestRun = TestRun-               { runTags :: [String]-               , runInput :: Values-               , runExpectedResult :: ExpectedResult Success-               , runIndex :: Int-               , runDescription :: String-               }-             deriving (Show)+  { runTags :: [String],+    runInput :: Values,+    runExpectedResult :: ExpectedResult Success,+    runIndex :: Int,+    runDescription :: String+  }+  deriving (Show)  -- | Several Values - either literally, or by reference to a file, or -- to be generated on demand.-data Values = Values [Value]-            | InFile FilePath-            | GenValues [GenValue]-            deriving (Show)+data Values+  = Values [Value]+  | InFile FilePath+  | GenValues [GenValue]+  deriving (Show) -data GenValue = GenValue [Int] PrimType-                -- ^ Generate a value of the given rank and primitive-                -- type.  Scalars are considered 0-ary arrays.-              | GenPrim PrimValue-                -- ^ A fixed non-randomised primitive value.-              deriving (Show)+data GenValue+  = -- | Generate a value of the given rank and primitive+    -- type.  Scalars are considered 0-ary arrays.+    GenValue [Int] PrimType+  | -- | A fixed non-randomised primitive value.+    GenPrim PrimValue+  deriving (Show)  -- | A prettyprinted representation of type of value produced by a -- 'GenValue'.@@ -164,18 +172,21 @@  -- | How a test case is expected to terminate. data ExpectedResult values-  = Succeeds (Maybe values) -- ^ Execution suceeds, with or without-                            -- expected result values.-  | RunTimeFailure ExpectedError -- ^ Execution fails with this error.+  = -- | Execution suceeds, with or without+    -- expected result values.+    Succeeds (Maybe values)+  | -- | Execution fails with this error.+    RunTimeFailure ExpectedError   deriving (Show)  -- | The result expected from a succesful execution.-data Success = SuccessValues Values-             -- ^ These values are expected.-             | SuccessGenerateValues-             -- ^ Compute expected values from executing a known-good-             -- reference implementation.-             deriving (Show)+data Success+  = -- | These values are expected.+    SuccessValues Values+  | -- | Compute expected values from executing a known-good+    -- reference implementation.+    SuccessGenerateValues+  deriving (Show)  type Parser = Parsec Void T.Text @@ -197,31 +208,43 @@ braces p = lexstr "{" *> p <* lexstr "}"  parseNatural :: Parser Int-parseNatural = lexeme $ foldl' (\acc x -> acc * 10 + x) 0 .-               map num <$> some digitChar-  where num c = ord c - ord '0'+parseNatural =+  lexeme $+    foldl' (\acc x -> acc * 10 + x) 0+      . map num+      <$> some digitChar+  where+    num c = ord c - ord '0'  parseDescription :: Parser T.Text parseDescription = lexeme $ T.pack <$> (anySingle `manyTill` parseDescriptionSeparator)  parseDescriptionSeparator :: Parser ()-parseDescriptionSeparator = try (string descriptionSeparator >>-                                 void (satisfy isSpace `manyTill` newline)) <|> eof+parseDescriptionSeparator =+  try+    ( string descriptionSeparator+        >> void (satisfy isSpace `manyTill` newline)+    )+    <|> eof  descriptionSeparator :: T.Text descriptionSeparator = "=="  parseTags :: Parser [T.Text] parseTags = lexstr "tags" *> braces (many parseTag) <|> pure []-  where parseTag = T.pack <$> lexeme (some $ satisfy tagConstituent)+  where+    parseTag = T.pack <$> lexeme (some $ satisfy tagConstituent)  tagConstituent :: Char -> Bool tagConstituent c = isAlphaNum c || c == '_' || c == '-'  parseAction :: Parser TestAction-parseAction = CompileTimeFailure <$> (lexstr' "error:" *> parseExpectedError) <|>-              (RunCases <$> parseInputOutputs <*>-               many parseExpectedStructure <*> many parseWarning)+parseAction =+  CompileTimeFailure <$> (lexstr' "error:" *> parseExpectedError)+    <|> ( RunCases <$> parseInputOutputs+            <*> many parseExpectedStructure+            <*> many parseWarning+        )  parseInputOutputs :: Parser [InputOutputs] parseInputOutputs = do@@ -231,125 +254,156 @@  parseEntryPoints :: Parser [T.Text] parseEntryPoints = (lexstr "entry:" *> many entry <* space) <|> pure ["main"]-  where constituent c = not (isSpace c) && c /= '}'-        entry = lexeme' $ T.pack <$> some (satisfy constituent)+  where+    constituent c = not (isSpace c) && c /= '}'+    entry = lexeme' $ T.pack <$> some (satisfy constituent)  parseRunTags :: Parser [String] parseRunTags = many parseTag-  where parseTag = try $ lexeme $ do s <- some $ satisfy tagConstituent-                                     guard $ s `notElem` ["input", "structure", "warning"]-                                     return s+  where+    parseTag = try $+      lexeme $ do+        s <- some $ satisfy tagConstituent+        guard $ s `notElem` ["input", "structure", "warning"]+        return s  parseRunCases :: Parser [TestRun]-parseRunCases = parseRunCases' (0::Int)-  where parseRunCases' i = (:) <$> parseRunCase i <*> parseRunCases' (i+1)-                           <|> pure []-        parseRunCase i = do-          tags <- parseRunTags-          lexstr "input"-          input <- if "random" `elem` tags-                   then parseRandomValues-                   else parseValues-          expr <- parseExpectedResult-          return $ TestRun tags input expr i $ desc i input+parseRunCases = parseRunCases' (0 :: Int)+  where+    parseRunCases' i =+      (:) <$> parseRunCase i <*> parseRunCases' (i + 1)+        <|> pure []+    parseRunCase i = do+      tags <- parseRunTags+      lexstr "input"+      input <-+        if "random" `elem` tags+          then parseRandomValues+          else parseValues+      expr <- parseExpectedResult+      return $ TestRun tags input expr i $ desc i input -        -- If the file is gzipped, we strip the 'gz' extension from-        -- the dataset name.  This makes it more convenient to rename-        -- from 'foo.in' to 'foo.in.gz', as the reported dataset name-        -- does not change (which would make comparisons to historical-        -- data harder).-        desc _ (InFile path)-          | takeExtension path == ".gz" = dropExtension path-          | otherwise                   = path-        desc i (Values vs) =-          -- Turn linebreaks into space.-          "#" ++ show i ++ " (\"" ++ unwords (lines vs') ++ "\")"-          where vs' = case unwords (map pretty vs) of-                        s | length s > 50 -> take 50 s ++ "..."-                          | otherwise     -> s-        desc _ (GenValues gens) =-          unwords $ map genValueType gens+    -- If the file is gzipped, we strip the 'gz' extension from+    -- the dataset name.  This makes it more convenient to rename+    -- from 'foo.in' to 'foo.in.gz', as the reported dataset name+    -- does not change (which would make comparisons to historical+    -- data harder).+    desc _ (InFile path)+      | takeExtension path == ".gz" = dropExtension path+      | otherwise = path+    desc i (Values vs) =+      -- Turn linebreaks into space.+      "#" ++ show i ++ " (\"" ++ unwords (lines vs') ++ "\")"+      where+        vs' = case unwords (map pretty vs) of+          s+            | length s > 50 -> take 50 s ++ "..."+            | otherwise -> s+    desc _ (GenValues gens) =+      unwords $ map genValueType gens  parseExpectedResult :: Parser (ExpectedResult Success) parseExpectedResult =-  (lexstr "auto" *> lexstr "output" $> Succeeds (Just SuccessGenerateValues)) <|>-  (Succeeds . Just . SuccessValues <$> (lexstr "output" *> parseValues)) <|>-  (RunTimeFailure <$> (lexstr "error:" *> parseExpectedError)) <|>-  pure (Succeeds Nothing)+  (lexstr "auto" *> lexstr "output" $> Succeeds (Just SuccessGenerateValues))+    <|> (Succeeds . Just . SuccessValues <$> (lexstr "output" *> parseValues))+    <|> (RunTimeFailure <$> (lexstr "error:" *> parseExpectedError))+    <|> pure (Succeeds Nothing)  parseExpectedError :: Parser ExpectedError parseExpectedError = lexeme $ do   s <- T.strip <$> restOfLine   if T.null s     then return AnyError-         -- blankCompOpt creates a regular expression that treats-         -- newlines like ordinary characters, which is what we want.-    else ThisError s <$> makeRegexOptsM blankCompOpt defaultExecOpt (T.unpack s)+    else -- blankCompOpt creates a regular expression that treats+    -- newlines like ordinary characters, which is what we want.+      ThisError s <$> makeRegexOptsM blankCompOpt defaultExecOpt (T.unpack s)  parseRandomValues :: Parser Values parseRandomValues = GenValues <$> between (lexstr "{") (lexstr "}") (many parseGenValue)  parseGenValue :: Parser GenValue-parseGenValue = choice [ GenValue <$> many dim <*> parsePrimType-                       , lexeme $ GenPrim <$> choice [i8, i16, i32, i64,-                                                      u8, u16, u32, u64,-                                                      f32, f64,-                                                      int SignedValue Int32 ""]-                       ]-  where digits = some (satisfy isDigit)-        dim = between (lexstr "[") (lexstr "]") $-              lexeme $ read <$> digits--        readint :: String -> Integer-        readint = read -- To avoid warnings.--        readfloat :: String -> Double-        readfloat = read -- To avoid warnings.+parseGenValue =+  choice+    [ GenValue <$> many dim <*> parsePrimType,+      lexeme $+        GenPrim+          <$> choice+            [ i8,+              i16,+              i32,+              i64,+              u8,+              u16,+              u32,+              u64,+              f32,+              f64,+              int SignedValue Int32 ""+            ]+    ]+  where+    digits = some (satisfy isDigit)+    dim =+      between (lexstr "[") (lexstr "]") $+        lexeme $ read <$> digits -        int f t s = try $ lexeme $ f . intValue t  . readint <$> digits <*-                    string s <*-                    notFollowedBy (satisfy isAlphaNum)-        i8  = int SignedValue Int8 "i8"-        i16 = int SignedValue Int16 "i16"-        i32 = int SignedValue Int32 "i32"-        i64 = int SignedValue Int64 "i64"-        u8  = int UnsignedValue Int8 "u8"-        u16 = int UnsignedValue Int16 "u16"-        u32 = int UnsignedValue Int32 "u32"-        u64 = int UnsignedValue Int64 "u64"+    readint :: String -> Integer+    readint = read -- To avoid warnings.+    readfloat :: String -> Double+    readfloat = read -- To avoid warnings.+    int f t s =+      try $+        lexeme $+          f . intValue t . readint <$> digits+            <* string s+            <* notFollowedBy (satisfy isAlphaNum)+    i8 = int SignedValue Int8 "i8"+    i16 = int SignedValue Int16 "i16"+    i32 = int SignedValue Int32 "i32"+    i64 = int SignedValue Int64 "i64"+    u8 = int UnsignedValue Int8 "u8"+    u16 = int UnsignedValue Int16 "u16"+    u32 = int UnsignedValue Int32 "u32"+    u64 = int UnsignedValue Int64 "u64" -        optSuffix s suff = do-          s' <- s-          ((s'<>) <$> suff) <|> pure s'+    optSuffix s suff = do+      s' <- s+      ((s' <>) <$> suff) <|> pure s' -        float f t s = try $ lexeme $ f . floatValue t  . readfloat <$>-                      (digits `optSuffix` (char '.' *> (("."<>) <$> digits))) <*-                      string s <*-                      notFollowedBy (satisfy isAlphaNum)-        f32 = float FloatValue Float32 "f32"-        f64 = float FloatValue Float64 "f64"+    float f t s =+      try $+        lexeme $+          f . floatValue t . readfloat+            <$> (digits `optSuffix` (char '.' *> (("." <>) <$> digits)))+            <* string s+            <* notFollowedBy (satisfy isAlphaNum)+    f32 = float FloatValue Float32 "f32"+    f64 = float FloatValue Float64 "f64"  parsePrimType :: Parser PrimType parsePrimType =-  choice [ lexstr "i8" $> Signed Int8-         , lexstr "i16" $> Signed Int16-         , lexstr "i32" $> Signed Int32-         , lexstr "i64" $> Signed Int64-         , lexstr "u8" $> Unsigned Int8-         , lexstr "u16" $> Unsigned Int16-         , lexstr "u32" $> Unsigned Int32-         , lexstr "u64" $> Unsigned Int64-         , lexstr "f32" $> FloatType Float32-         , lexstr "f64" $> FloatType Float64-         , lexstr "bool" $> Bool-         ]+  choice+    [ lexstr "i8" $> Signed Int8,+      lexstr "i16" $> Signed Int16,+      lexstr "i32" $> Signed Int32,+      lexstr "i64" $> Signed Int64,+      lexstr "u8" $> Unsigned Int8,+      lexstr "u16" $> Unsigned Int16,+      lexstr "u32" $> Unsigned Int32,+      lexstr "u64" $> Unsigned Int64,+      lexstr "f32" $> FloatType Float32,+      lexstr "f64" $> FloatType Float64,+      lexstr "bool" $> Bool+    ]  parseValues :: Parser Values-parseValues = do s <- parseBlock-                 case valuesFromByteString "input block contents" $ BS.fromStrict $ T.encodeUtf8 s of-                   Left err -> fail err-                   Right vs -> return $ Values vs-              <|> lexstr "@" *> lexeme (InFile . T.unpack <$> nextWord)+parseValues =+  do+    s <- parseBlock+    case valuesFromByteString "input block contents" $ BS.fromStrict $ T.encodeUtf8 s of+      Left err -> fail err+      Right vs -> return $ Values vs+    <|> lexstr "@" *> lexeme (InFile . T.unpack <$> nextWord)  parseBlock :: Parser T.Text parseBlock = lexeme $ braces (T.pack <$> parseBlockBody 0)@@ -357,11 +411,11 @@ parseBlockBody :: Int -> Parser String parseBlockBody n = do   c <- lookAhead anySingle-  case (c,n) of+  case (c, n) of     ('}', 0) -> return mempty-    ('}', _) -> (:) <$> anySingle <*> parseBlockBody (n-1)-    ('{', _) -> (:) <$> anySingle <*> parseBlockBody (n+1)-    _        -> (:) <$> anySingle <*> parseBlockBody n+    ('}', _) -> (:) <$> anySingle <*> parseBlockBody (n -1)+    ('{', _) -> (:) <$> anySingle <*> parseBlockBody (n + 1)+    _ -> (:) <$> anySingle <*> parseBlockBody n  restOfLine :: Parser T.Text restOfLine = T.pack <$> (anySingle `manyTill` (void newline <|> eof))@@ -371,25 +425,31 @@  parseWarning :: Parser WarningTest parseWarning = lexstr "warning:" >> parseExpectedWarning-  where parseExpectedWarning = lexeme $ do-          s <- T.strip <$> restOfLine-          ExpectedWarning s <$> makeRegexOptsM blankCompOpt defaultExecOpt (T.unpack s)+  where+    parseExpectedWarning = lexeme $ do+      s <- T.strip <$> restOfLine+      ExpectedWarning s <$> makeRegexOptsM blankCompOpt defaultExecOpt (T.unpack s)  parseExpectedStructure :: Parser StructureTest parseExpectedStructure =-  lexstr "structure" *>-  (StructureTest <$> optimisePipeline <*> parseMetrics)+  lexstr "structure"+    *> (StructureTest <$> optimisePipeline <*> parseMetrics)  optimisePipeline :: Parser StructurePipeline-optimisePipeline = lexstr "distributed" $> KernelsPipeline <|>-                   lexstr "gpu" $> GpuPipeline <|>-                   lexstr "cpu" $> SequentialCpuPipeline <|>-                   pure SOACSPipeline+optimisePipeline =+  lexstr "distributed" $> KernelsPipeline+    <|> lexstr "gpu" $> GpuPipeline+    <|> lexstr "cpu" $> SequentialCpuPipeline+    <|> pure SOACSPipeline  parseMetrics :: Parser AstMetrics-parseMetrics = braces $ fmap (AstMetrics . M.fromList) $ many $-               (,) <$> (T.pack <$> lexeme (some (satisfy constituent))) <*> parseNatural-  where constituent c = isAlpha c || c == '/'+parseMetrics =+  braces $+    fmap (AstMetrics . M.fromList) $+      many $+        (,) <$> (T.pack <$> lexeme (some (satisfy constituent))) <*> parseNatural+  where+    constituent c = isAlpha c || c == '/'  testSpec :: Parser ProgramTest testSpec =@@ -397,20 +457,24 @@  parserState :: Int -> FilePath -> s -> State s e parserState line name t =-  State { stateInput = t-        , stateOffset = 0-        , statePosState = PosState-          { pstateInput = t-          , pstateOffset = 0-          , pstateSourcePos = SourcePos-                              { sourceName = name-                              , sourceLine = mkPos line-                              , sourceColumn = mkPos 3 }-          , pstateTabWidth = defaultTabWidth-          , pstateLinePrefix = "-- "}-        , stateParseErrors = []-        }-+  State+    { stateInput = t,+      stateOffset = 0,+      statePosState =+        PosState+          { pstateInput = t,+            pstateOffset = 0,+            pstateSourcePos =+              SourcePos+                { sourceName = name,+                  sourceLine = mkPos line,+                  sourceColumn = mkPos 3+                },+            pstateTabWidth = defaultTabWidth,+            pstateLinePrefix = "-- "+          },+      stateParseErrors = []+    }  readTestSpec :: Int -> String -> T.Text -> Either (ParseErrorBundle T.Text Void) ProgramTest readTestSpec line name t =@@ -418,8 +482,9 @@  readInputOutputs :: Int -> String -> T.Text -> Either (ParseErrorBundle T.Text Void) [InputOutputs] readInputOutputs line name t =-  snd $ runParser' (parseDescription *> space *> parseInputOutputs <* eof) $-  parserState line name t+  snd $+    runParser' (parseDescription *> space *> parseInputOutputs <* eof) $+      parserState line name t  commentPrefix :: T.Text commentPrefix = T.pack "--"@@ -432,54 +497,58 @@ testSpecFromFile path = do   blocks_or_err <-     (Right . testBlocks <$> T.readFile path)-    `catch` couldNotRead+      `catch` couldNotRead   case blocks_or_err of     Left err -> return $ Left err     Right blocks -> do       let (first_spec_line, first_spec, rest_specs) =-            case blocks of []       -> (0, mempty, [])-                           (n,s):ss -> (n, s, ss)-      case readTestSpec (1+first_spec_line) path first_spec of+            case blocks of+              [] -> (0, mempty, [])+              (n, s) : ss -> (n, s, ss)+      case readTestSpec (1 + first_spec_line) path first_spec of         Left err -> return $ Left $ errorBundlePretty err-        Right v  -> return $ foldM moreCases v rest_specs--  where moreCases test (lineno, cases) =-          case readInputOutputs lineno path cases of-            Left err     -> Left $ errorBundlePretty err-            Right cases' ->-              case testAction test of-                RunCases old_cases structures warnings ->-                  Right test { testAction = RunCases (old_cases ++ cases') structures warnings }-                _ -> Left "Secondary test block provided, but primary test block specifies compilation error."+        Right v -> return $ foldM moreCases v rest_specs+  where+    moreCases test (lineno, cases) =+      case readInputOutputs lineno path cases of+        Left err -> Left $ errorBundlePretty err+        Right cases' ->+          case testAction test of+            RunCases old_cases structures warnings ->+              Right test {testAction = RunCases (old_cases ++ cases') structures warnings}+            _ -> Left "Secondary test block provided, but primary test block specifies compilation error."  -- | Like 'testSpecFromFile', but kills the process on error. testSpecFromFileOrDie :: FilePath -> IO ProgramTest testSpecFromFileOrDie prog = do   spec_or_err <- testSpecFromFile prog   case spec_or_err of-    Left err -> do putStrLn err-                   exitFailure+    Left err -> do+      putStrLn err+      exitFailure     Right spec -> return spec  testBlocks :: T.Text -> [(Int, T.Text)] testBlocks = mapMaybe isTestBlock . commentBlocks-  where isTestBlock (n,block)-          | any ((" " <> descriptionSeparator) `T.isPrefixOf`) block =-              Just (n, T.unlines block)-          | otherwise =-              Nothing+  where+    isTestBlock (n, block)+      | any ((" " <> descriptionSeparator) `T.isPrefixOf`) block =+        Just (n, T.unlines block)+      | otherwise =+        Nothing  commentBlocks :: T.Text -> [(Int, [T.Text])]-commentBlocks = commentBlocks' . zip [0..] . T.lines-  where isComment = (commentPrefix `T.isPrefixOf`)-        commentBlocks' ls =-          let ls' = dropWhile (not . isComment . snd) ls-          in case ls' of+commentBlocks = commentBlocks' . zip [0 ..] . T.lines+  where+    isComment = (commentPrefix `T.isPrefixOf`)+    commentBlocks' ls =+      let ls' = dropWhile (not . isComment . snd) ls+       in case ls' of             [] -> []-            (n,_) : _ ->+            (n, _) : _ ->               let (block, ls'') = span (isComment . snd) ls'                   block' = map (T.drop 2 . snd) block-              in (n, block') : commentBlocks' ls''+               in (n, block') : commentBlocks' ls''  -- | Read test specifications from the given path, which can be a file -- or directory containing @.fut@ files and further directories.@@ -495,23 +564,27 @@ -- | Read test specifications from the given paths, which can be a -- files or directories containing @.fut@ files and further -- directories.-testSpecsFromPaths :: [FilePath]-                   -> IO (Either String [(FilePath, ProgramTest)])+testSpecsFromPaths ::+  [FilePath] ->+  IO (Either String [(FilePath, ProgramTest)]) testSpecsFromPaths = fmap (fmap concat . sequence) . mapM testSpecsFromPath  -- | Like 'testSpecsFromPaths', but kills the process on errors.-testSpecsFromPathsOrDie :: [FilePath]-                        -> IO [(FilePath, ProgramTest)]+testSpecsFromPathsOrDie ::+  [FilePath] ->+  IO [(FilePath, ProgramTest)] testSpecsFromPathsOrDie dirs = do   specs_or_err <- testSpecsFromPaths dirs   case specs_or_err of-    Left err -> do putStrLn err-                   exitFailure+    Left err -> do+      putStrLn err+      exitFailure     Right specs -> return specs  testPrograms :: FilePath -> IO [FilePath] testPrograms dir = filter isFut <$> directoryContents dir-  where isFut = (==".fut") . takeExtension+  where+    isFut = (== ".fut") . takeExtension  -- | Try to parse a several values from a byte string.  The 'String' -- parameter is used for error messages.@@ -528,11 +601,13 @@ getValues dir v = do   s <- getValuesBS dir v   case valuesFromByteString file s of-    Left e   -> fail e+    Left e -> fail e     Right vs -> return vs-  where file = case v of Values{} -> "<values>"-                         InFile f -> f-                         GenValues{} -> "<randomly generated>"+  where+    file = case v of+      Values {} -> "<values>"+      InFile f -> f+      GenValues {} -> "<randomly generated>"  -- | Extract a pretty representation of some 'Values'.  In the IO -- monad because this might involve reading from a file.  There is no@@ -542,16 +617,17 @@   return $ BS.fromStrict $ T.encodeUtf8 $ T.unlines $ map prettyText vs getValuesBS dir (InFile file) =   case takeExtension file of-   ".gz" -> liftIO $ do-     s <- E.try readAndDecompress-     case s of-       Left e   -> fail $ show file ++ ": " ++ show (e :: DecompressError)-       Right s' -> return s'--   _  -> liftIO $ BS.readFile file'-  where file' = dir </> file-        readAndDecompress = do s <- BS.readFile file'-                               E.evaluate $ decompress s+    ".gz" -> liftIO $ do+      s <- E.try readAndDecompress+      case s of+        Left e -> fail $ show file ++ ": " ++ show (e :: DecompressError)+        Right s' -> return s'+    _ -> liftIO $ BS.readFile file'+  where+    file' = dir </> file+    readAndDecompress = do+      s <- BS.readFile file'+      E.evaluate $ decompress s getValuesBS dir (GenValues gens) =   mconcat <$> mapM (getGenBS dir) gens @@ -568,30 +644,37 @@ getGenBS :: MonadIO m => FilePath -> GenValue -> m BS.ByteString getGenBS dir gen = do   liftIO $ createDirectoryIfMissing True $ dir </> "data"-  exists_and_proper_size <- liftIO $-    withFile (dir </> file) ReadMode (fmap (== genFileSize gen) . hFileSize)-    `catch` \ex -> if isDoesNotExistError ex then return False-                   else E.throw ex-  unless exists_and_proper_size $ liftIO $ do-    s <- genValues [gen]-    withTempFile (dir </> "data") (genFileName gen) $ \tmpfile h -> do-      hClose h -- We will be writing and reading this ourselves.-      SBS.writeFile tmpfile s-      renameFile tmpfile $ dir </> file+  exists_and_proper_size <-+    liftIO $+      withFile (dir </> file) ReadMode (fmap (== genFileSize gen) . hFileSize)+        `catch` \ex ->+          if isDoesNotExistError ex+            then return False+            else E.throw ex+  unless exists_and_proper_size $+    liftIO $ do+      s <- genValues [gen]+      withTempFile (dir </> "data") (genFileName gen) $ \tmpfile h -> do+        hClose h -- We will be writing and reading this ourselves.+        SBS.writeFile tmpfile s+        renameFile tmpfile $ dir </> file   getValuesBS dir $ InFile file-  where file = "data" </> genFileName gen+  where+    file = "data" </> genFileName gen  genValues :: [GenValue] -> IO SBS.ByteString genValues gens = do-  (code, stdout, stderr) <- readProcessWithExitCode "futhark" ("dataset":args) mempty+  (code, stdout, stderr) <- readProcessWithExitCode "futhark" ("dataset" : args) mempty   case code of     ExitSuccess ->       return stdout     ExitFailure e ->-      fail $ "'futhark dataset' failed with exit code " ++ show e ++ " and stderr:\n" ++-      map (chr . fromIntegral) (SBS.unpack stderr)-  where args = "-b" : concatMap argForGen gens-        argForGen g = ["-g", genValueType g]+      fail $+        "'futhark dataset' failed with exit code " ++ show e ++ " and stderr:\n"+          ++ map (chr . fromIntegral) (SBS.unpack stderr)+  where+    args = "-b" : concatMap argForGen gens+    argForGen g = ["-g", genValueType g]  genFileName :: GenValue -> FilePath genFileName gen = genValueType gen ++ ".in"@@ -600,49 +683,63 @@ -- whether an existing file is broken/truncated. genFileSize :: GenValue -> Integer genFileSize = genSize-  where header_size = 1 + 1 + 1 + 4 -- 'b' <version> <num_dims> <type>--        genSize (GenValue ds t) = header_size + toInteger (length ds) * 8 +-                                  product (map toInteger ds) * primSize t-        genSize (GenPrim v) =-          header_size + primByteSize (primValueType v)+  where+    header_size = 1 + 1 + 1 + 4 -- 'b' <version> <num_dims> <type>+    genSize (GenValue ds t) =+      header_size + toInteger (length ds) * 8+        + product (map toInteger ds) * primSize t+    genSize (GenPrim v) =+      header_size + primByteSize (primValueType v) -        primSize (Signed it) = intByteSize it-        primSize (Unsigned it) = intByteSize it-        primSize (FloatType ft) = floatByteSize ft-        primSize Bool = 1+    primSize (Signed it) = intByteSize it+    primSize (Unsigned it) = intByteSize it+    primSize (FloatType ft) = floatByteSize ft+    primSize Bool = 1  -- | When/if generating a reference output file for this run, what -- should it be called?  Includes the "data/" folder. testRunReferenceOutput :: FilePath -> T.Text -> TestRun -> FilePath testRunReferenceOutput prog entry tr =   "data"-  </> takeBaseName prog-  <> ":" <> T.unpack entry-  <> "-" <> map clean (runDescription tr)-  <.> "out"-  where clean '/' = '_' -- Would this ever happen?-        clean ' ' = '_'-        clean c = c+    </> takeBaseName prog+    <> ":"+    <> T.unpack entry+    <> "-"+    <> map clean (runDescription tr)+    <.> "out"+  where+    clean '/' = '_' -- Would this ever happen?+    clean ' ' = '_'+    clean c = c  -- | Get the values corresponding to an expected result, if any.-getExpectedResult :: (MonadFail m, MonadIO m) =>-                     FilePath -> T.Text -> TestRun-                  -> m (ExpectedResult [Value])+getExpectedResult ::+  (MonadFail m, MonadIO m) =>+  FilePath ->+  T.Text ->+  TestRun ->+  m (ExpectedResult [Value]) getExpectedResult prog entry tr =   case runExpectedResult tr of     (Succeeds (Just (SuccessValues vals))) ->       Succeeds . Just <$> getValues (takeDirectory prog) vals     Succeeds (Just SuccessGenerateValues) ->-      getExpectedResult prog entry-      tr { runExpectedResult = Succeeds $ Just $ SuccessValues $ InFile $-                               testRunReferenceOutput prog entry tr }+      getExpectedResult+        prog+        entry+        tr+          { runExpectedResult =+              Succeeds $+                Just $+                  SuccessValues $+                    InFile $+                      testRunReferenceOutput prog entry tr+          }     Succeeds Nothing ->       return $ Succeeds Nothing     RunTimeFailure err ->       return $ RunTimeFailure err - -- | The name we use for compiled programs. binaryName :: FilePath -> FilePath binaryName = dropExtension@@ -651,19 +748,24 @@ -- @program@ with the command @futhark backend extra-options...@, and -- returns stdout and stderr of the compiler.  Throws an IO exception -- containing stderr if compilation fails.-compileProgram :: (MonadIO m, MonadError [T.Text] m) =>-                  [String] -> FilePath -> String -> FilePath-               -> m (SBS.ByteString, SBS.ByteString)+compileProgram ::+  (MonadIO m, MonadError [T.Text] m) =>+  [String] ->+  FilePath ->+  String ->+  FilePath ->+  m (SBS.ByteString, SBS.ByteString) compileProgram extra_options futhark backend program = do-  (futcode, stdout, stderr) <- liftIO $ readProcessWithExitCode futhark (backend:options) ""+  (futcode, stdout, stderr) <- liftIO $ readProcessWithExitCode futhark (backend : options) ""   case futcode of     ExitFailure 127 -> throwError [progNotFound $ T.pack futhark]-    ExitFailure _   -> throwError [T.decodeUtf8 stderr]-    ExitSuccess     -> return ()+    ExitFailure _ -> throwError [T.decodeUtf8 stderr]+    ExitSuccess -> return ()   return (stdout, stderr)-  where binOutputf = binaryName program-        options = [program, "-o", binOutputf] ++ extra_options-        progNotFound s = s <> ": command not found"+  where+    binOutputf = binaryName program+    options = [program, "-o", binOutputf] ++ extra_options+    progNotFound s = s <> ": command not found"  -- | @runProgram runner extra_options prog entry input@ runs the -- Futhark program @prog@ (which must have the @.fut@ suffix),@@ -673,10 +775,14 @@ -- "interpreter" for the compiled program (e.g. @python@ when using -- the Python backends).  The @extra_options@ are passed to the -- program.-runProgram :: MonadIO m =>-              String -> [String]-           -> String -> T.Text -> Values-           -> m (ExitCode, SBS.ByteString, SBS.ByteString)+runProgram ::+  MonadIO m =>+  String ->+  [String] ->+  String ->+  T.Text ->+  Values ->+  m (ExitCode, SBS.ByteString, SBS.ByteString) runProgram runner extra_options prog entry input = do   let progbin = binaryName prog       dir = takeDirectory prog@@ -693,43 +799,53 @@ -- | Ensure that any reference output files exist, or create them (by -- compiling the program with the reference compiler and running it on -- the input) if necessary.-ensureReferenceOutput :: (MonadIO m, MonadError [T.Text] m) =>-                         Maybe Int -> FilePath -> String -> FilePath -> [InputOutputs]-                      -> m ()+ensureReferenceOutput ::+  (MonadIO m, MonadError [T.Text] m) =>+  Maybe Int ->+  FilePath ->+  String ->+  FilePath ->+  [InputOutputs] ->+  m () ensureReferenceOutput concurrency futhark compiler prog ios = do   missing <- filterM isReferenceMissing $ concatMap entryAndRuns ios    unless (null missing) $ do     void $ compileProgram [] futhark compiler prog -    res <- liftIO $ flip (pmapIO concurrency) missing $ \(entry, tr) -> do-      (code, stdout, stderr) <- runProgram "" ["-b"] prog entry $ runInput tr-      case code of-        ExitFailure e ->-          return $ Left-          [T.pack $ "Reference dataset generation failed with exit code " ++-           show e ++ " and stderr:\n" ++-           map (chr . fromIntegral) (SBS.unpack stderr)]-        ExitSuccess -> do-          let f = file (entry, tr)-          liftIO $ createDirectoryIfMissing True $ takeDirectory f-          SBS.writeFile f stdout-          return $ Right ()+    res <- liftIO $+      flip (pmapIO concurrency) missing $ \(entry, tr) -> do+        (code, stdout, stderr) <- runProgram "" ["-b"] prog entry $ runInput tr+        case code of+          ExitFailure e ->+            return $+              Left+                [ T.pack $+                    "Reference dataset generation failed with exit code "+                      ++ show e+                      ++ " and stderr:\n"+                      ++ map (chr . fromIntegral) (SBS.unpack stderr)+                ]+          ExitSuccess -> do+            let f = file (entry, tr)+            liftIO $ createDirectoryIfMissing True $ takeDirectory f+            SBS.writeFile f stdout+            return $ Right ()      case sequence_ res of       Left err -> throwError err       Right () -> return ()--  where file (entry, tr) =-          takeDirectory prog </> testRunReferenceOutput prog entry tr+  where+    file (entry, tr) =+      takeDirectory prog </> testRunReferenceOutput prog entry tr -        entryAndRuns (InputOutputs entry rts) = map (entry,) rts+    entryAndRuns (InputOutputs entry rts) = map (entry,) rts -        isReferenceMissing (entry, tr)-          | Succeeds (Just SuccessGenerateValues) <- runExpectedResult tr =-              liftIO . fmap not . doesFileExist . file $ (entry, tr)-          | otherwise =-              return False+    isReferenceMissing (entry, tr)+      | Succeeds (Just SuccessGenerateValues) <- runExpectedResult tr =+        liftIO . fmap not . doesFileExist . file $ (entry, tr)+      | otherwise =+        return False  -- | Determine the --tuning options to pass to the program.  The first -- argument is the extension of the tuning file, or 'Nothing' if none@@ -739,6 +855,9 @@ determineTuning (Just ext) program = do   exists <- liftIO $ doesFileExist (program <.> ext)   if exists-    then return (["--tuning", program <.> ext],-                 " (using " <> takeFileName (program <.> ext) <> ")")+    then+      return+        ( ["--tuning", program <.> ext],+          " (using " <> takeFileName (program <.> ext) <> ")"+        )     else return ([], mempty)
src/Futhark/Test/Values.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Strict #-} {-# LANGUAGE Trustworthy #-}+ -- | This module defines an efficient value representation as well as -- parsing and comparison functions.  This is because the standard -- Futhark parser is not able to cope with large values (like arrays@@ -8,46 +9,45 @@ -- here does not support tuples, so don't use those as input/output -- for your test programs. module Futhark.Test.Values-       ( Value(..)-       , Vector+  ( Value (..),+    Vector, -       -- * Reading Values-       , readValues+    -- * Reading Values+    readValues, -       -- * Types of values-       , ValueType(..)-       , valueType+    -- * Types of values+    ValueType (..),+    valueType, -       -- * Comparing Values-       , compareValues-       , compareValues1-       , Mismatch-       )-       where+    -- * Comparing Values+    compareValues,+    compareValues1,+    Mismatch,+  )+where  import Control.Monad import Control.Monad.ST import Data.Binary-import Data.Binary.Put import Data.Binary.Get+import Data.Binary.Put import qualified Data.ByteString.Lazy.Char8 as BS-import Data.Int (Int8, Int16, Int32, Int64)-import Data.Char (isSpace, ord, chr)+import Data.Char (chr, isSpace, ord)+import Data.Int (Int16, Int32, Int64, Int8) import Data.Vector.Binary-import qualified Data.Vector.Unboxed.Mutable as UMVec-import qualified Data.Vector.Unboxed as UVec import Data.Vector.Generic (freeze)--import qualified Language.Futhark.Syntax as F-import Language.Futhark.Pretty()-import Futhark.IR.Primitive (PrimValue)-import Language.Futhark.Parser.Lexer-import qualified Futhark.Util.Pretty as PP-import Futhark.IR.Prop.Constants (IsValue(..))+import qualified Data.Vector.Unboxed as UVec+import qualified Data.Vector.Unboxed.Mutable as UMVec import Futhark.IR.Pretty ()-import Futhark.Util.Pretty+import Futhark.IR.Primitive (PrimValue)+import Futhark.IR.Prop.Constants (IsValue (..)) import Futhark.Util (maybeHead)-import Futhark.Util.Loc (Pos(..))+import Futhark.Util.Loc (Pos (..))+import Futhark.Util.Pretty+import qualified Futhark.Util.Pretty as PP+import Language.Futhark.Parser.Lexer+import Language.Futhark.Pretty ()+import qualified Language.Futhark.Syntax as F  type STVector s = UMVec.STVector s @@ -57,21 +57,19 @@ -- | An efficiently represented Futhark value.  Use 'pretty' to get a -- human-readable representation, and v'put' to obtain binary a -- representation.-data Value = Int8Value (Vector Int) (Vector Int8)-           | Int16Value (Vector Int) (Vector Int16)-           | Int32Value (Vector Int) (Vector Int32)-           | Int64Value (Vector Int) (Vector Int64)--           | Word8Value (Vector Int) (Vector Word8)-           | Word16Value (Vector Int) (Vector Word16)-           | Word32Value (Vector Int) (Vector Word32)-           | Word64Value (Vector Int) (Vector Word64)--           | Float32Value (Vector Int) (Vector Float)-           | Float64Value (Vector Int) (Vector Double)--           | BoolValue (Vector Int) (Vector Bool)-           deriving Show+data Value+  = Int8Value (Vector Int) (Vector Int8)+  | Int16Value (Vector Int) (Vector Int16)+  | Int32Value (Vector Int) (Vector Int32)+  | Int64Value (Vector Int) (Vector Int64)+  | Word8Value (Vector Int) (Vector Word8)+  | Word16Value (Vector Int) (Vector Word16)+  | Word32Value (Vector Int) (Vector Word32)+  | Word64Value (Vector Int) (Vector Word64)+  | Float32Value (Vector Int) (Vector Float)+  | Float64Value (Vector Int) (Vector Double)+  | BoolValue (Vector Int) (Vector Bool)+  deriving (Show)  binaryFormatVersion :: Word8 binaryFormatVersion = 2@@ -88,8 +86,9 @@   put (Float32Value shape vs) = putBinaryValue " f32" shape vs putFloatle   put (Float64Value shape vs) = putBinaryValue " f64" shape vs putDoublele   put (BoolValue shape vs) = putBinaryValue "bool" shape vs $ putInt8 . boolToInt-    where boolToInt True = 1-          boolToInt False = 0+    where+      boolToInt True = 1+      boolToInt False = 0    get = do     first <- getInt8@@ -121,14 +120,20 @@       " f32" -> get' (Float32Value shape') getFloatle num_elems       " f64" -> get' (Float64Value shape') getDoublele num_elems       "bool" -> get' (BoolValue shape') getBool num_elems-      s      -> fail $ "Cannot parse binary values of type " ++ show s-    where getBool = (/=0) <$> getWord8+      s -> fail $ "Cannot parse binary values of type " ++ show s+    where+      getBool = (/= 0) <$> getWord8 -          get' mk get_elem num_elems =-            mk <$> genericGetVectorWith (pure num_elems) get_elem+      get' mk get_elem num_elems =+        mk <$> genericGetVectorWith (pure num_elems) get_elem -putBinaryValue :: UVec.Unbox a =>-                  String -> Vector Int -> Vector a -> (a -> Put) -> Put+putBinaryValue ::+  UVec.Unbox a =>+  String ->+  Vector Int ->+  Vector a ->+  (a -> Put) ->+  Put putBinaryValue tstr shape vs putv = do   putInt8 $ fromIntegral $ ord 'b'   putWord8 binaryFormatVersion@@ -138,10 +143,12 @@   mapM_ putv $ UVec.toList vs  instance PP.Pretty Value where-  ppr v | product (valueShape v) == 0 =-            text "empty" <>-            parens (dims <> ppr (valueElemType v))-    where dims = mconcat $ map (brackets . ppr) $ valueShape v+  ppr v+    | product (valueShape v) == 0 =+      text "empty"+        <> parens (dims <> ppr (valueElemType v))+    where+      dims = mconcat $ map (brackets . ppr) $ valueShape v   ppr (Int8Value shape vs) = pprArray (UVec.toList shape) vs   ppr (Int16Value shape vs) = pprArray (UVec.toList shape) vs   ppr (Int32Value shape vs) = pprArray (UVec.toList shape) vs@@ -157,20 +164,23 @@ pprArray :: (UVec.Unbox a, F.IsPrimValue a) => [Int] -> UVec.Vector a -> Doc pprArray [] vs =   ppr $ F.primValue $ UVec.head vs-pprArray (d:ds) vs =-  brackets $ cat $ punctuate separator $ map (pprArray ds . slice) [0..d-1]-  where slice_size = product ds-        slice i = UVec.slice (i*slice_size) slice_size vs-        separator | null ds   = comma <> space-                  | otherwise = comma <> line+pprArray (d : ds) vs =+  brackets $ cat $ punctuate separator $ map (pprArray ds . slice) [0 .. d -1]+  where+    slice_size = product ds+    slice i = UVec.slice (i * slice_size) slice_size vs+    separator+      | null ds = comma <> space+      | otherwise = comma <> line  -- | A representation of the simple values we represent in this module. data ValueType = ValueType [Int] F.PrimType-               deriving (Show)+  deriving (Show)  instance PP.Pretty ValueType where   ppr (ValueType ds t) = mconcat (map pprDim ds) <> ppr t-    where pprDim d = brackets $ ppr d+    where+      pprDim d = brackets $ ppr d  -- | A textual description of the type of a value.  Follows Futhark -- type notation, and contains the exact dimension sizes if an array.@@ -178,17 +188,17 @@ valueType v = ValueType (valueShape v) $ valueElemType v  valueElemType :: Value -> F.PrimType-valueElemType Int8Value{} = F.Signed F.Int8-valueElemType Int16Value{} = F.Signed F.Int16-valueElemType Int32Value{} = F.Signed F.Int32-valueElemType Int64Value{} = F.Signed F.Int64-valueElemType Word8Value{} = F.Unsigned F.Int8-valueElemType Word16Value{} = F.Unsigned F.Int16-valueElemType Word32Value{} = F.Unsigned F.Int32-valueElemType Word64Value{} = F.Unsigned F.Int64-valueElemType Float32Value{} = F.FloatType F.Float32-valueElemType Float64Value{} = F.FloatType F.Float64-valueElemType BoolValue{} = F.Bool+valueElemType Int8Value {} = F.Signed F.Int8+valueElemType Int16Value {} = F.Signed F.Int16+valueElemType Int32Value {} = F.Signed F.Int32+valueElemType Int64Value {} = F.Signed F.Int64+valueElemType Word8Value {} = F.Unsigned F.Int8+valueElemType Word16Value {} = F.Unsigned F.Int16+valueElemType Word32Value {} = F.Unsigned F.Int32+valueElemType Word64Value {} = F.Unsigned F.Int64+valueElemType Float32Value {} = F.FloatType F.Float32+valueElemType Float64Value {} = F.FloatType F.Float64+valueElemType BoolValue {} = F.Bool  valueShape :: Value -> [Int] valueShape (Int8Value shape _) = UVec.toList shape@@ -206,10 +216,11 @@ -- The parser  dropRestOfLine, dropSpaces :: BS.ByteString -> BS.ByteString-dropRestOfLine = BS.drop 1 . BS.dropWhile (/='\n')+dropRestOfLine = BS.drop 1 . BS.dropWhile (/= '\n') dropSpaces t = case BS.dropWhile isSpace t of-  t' | "--" `BS.isPrefixOf` t' -> dropSpaces $ dropRestOfLine t'-     | otherwise -> t'+  t'+    | "--" `BS.isPrefixOf` t' -> dropSpaces $ dropRestOfLine t'+    | otherwise -> t'  type ReadValue v = BS.ByteString -> Maybe (v, BS.ByteString) @@ -226,48 +237,57 @@ -- (Used elements, shape, elements, remaining input) type State s v = (Int, Vector Int, STVector s v, BS.ByteString) -readArrayElemsST :: UMVec.Unbox v =>-                    Int -> Int -> ReadValue v -> State s v-                 -> ST s (Maybe (Int, State s v))+readArrayElemsST ::+  UMVec.Unbox v =>+  Int ->+  Int ->+  ReadValue v ->+  State s v ->+  ST s (Maybe (Int, State s v)) readArrayElemsST j r rv s = do   ms <- readRankedArrayOfST r rv s   case ms of     Just (i, shape, arr, t)       | Just t' <- symbol ',' t ->-          readArrayElemsST (j+1) r rv (i, shape, arr, t')+        readArrayElemsST (j + 1) r rv (i, shape, arr, t')       | otherwise -> return $ Just (j, (i, shape, arr, t))     _ ->       return $ Just (0, s)  updateShape :: Int -> Int -> Vector Int -> Maybe (Vector Int) updateShape d n shape-  | old_n < 0  = Just $ shape UVec.// [(r-d, n)]+  | old_n < 0 = Just $ shape UVec.// [(r - d, n)]   | old_n == n = Just shape-  | otherwise  = Nothing-  where r = UVec.length shape-        old_n = shape UVec.! (r-d)+  | otherwise = Nothing+  where+    r = UVec.length shape+    old_n = shape UVec.! (r - d)  growIfFilled :: UVec.Unbox v => Int -> STVector s v -> ST s (STVector s v) growIfFilled i arr =   if i >= capacity-  then UMVec.grow arr capacity-  else return arr-  where capacity = UMVec.length arr+    then UMVec.grow arr capacity+    else return arr+  where+    capacity = UMVec.length arr -readRankedArrayOfST :: UMVec.Unbox v =>-                 Int -> ReadValue v -> State s v-              -> ST s (Maybe (State s v))+readRankedArrayOfST ::+  UMVec.Unbox v =>+  Int ->+  ReadValue v ->+  State s v ->+  ST s (Maybe (State s v)) readRankedArrayOfST 0 rv (i, shape, arr, t)   | Just (v, t') <- rv t = do-      arr' <- growIfFilled i arr-      UMVec.write arr' i v-      return $ Just (i+1, shape, arr', t')+    arr' <- growIfFilled i arr+    UMVec.write arr' i v+    return $ Just (i + 1, shape, arr', t') readRankedArrayOfST r rv (i, shape, arr, t)   | Just t' <- symbol '[' t = do-      ms <- readArrayElemsST 1 (r-1) rv (i, shape, arr, t')-      return $ do-        (j, s) <- ms-        closeArray r j s+    ms <- readArrayElemsST 1 (r -1) rv (i, shape, arr, t')+    return $ do+      (j, s) <- ms+      closeArray r j s readRankedArrayOfST _ _ _ =   return Nothing @@ -277,8 +297,12 @@   shape' <- updateShape r j shape   return (i, shape', arr, t') -readRankedArrayOf :: UMVec.Unbox v =>-                     Int -> ReadValue v -> BS.ByteString -> Maybe (Vector Int, Vector v, BS.ByteString)+readRankedArrayOf ::+  UMVec.Unbox v =>+  Int ->+  ReadValue v ->+  BS.ByteString ->+  Maybe (Vector Int, Vector v, BS.ByteString) readRankedArrayOf r rv t = runST $ do   arr <- UMVec.new 1024   ms <- readRankedArrayOfST r rv (0, UVec.replicate r (-1), arr, t)@@ -300,120 +324,135 @@ readIntegral :: Integral int => (Token -> Maybe int) -> ReadValue int readIntegral f t = do   v <- case fst <$> scanTokens (Pos "" 1 1 0) a of-         Right [L _ NEGATE, L _ (INTLIT x)] -> Just $ negate $ fromIntegral x-         Right [L _ (INTLIT x)] -> Just $ fromIntegral x-         Right [L _ tok] -> f tok-         Right [L _ NEGATE, L _ tok] -> negate <$> f tok-         _ -> Nothing+    Right [L _ NEGATE, L _ (INTLIT x)] -> Just $ negate $ fromIntegral x+    Right [L _ (INTLIT x)] -> Just $ fromIntegral x+    Right [L _ tok] -> f tok+    Right [L _ NEGATE, L _ tok] -> negate <$> f tok+    _ -> Nothing   return (v, dropSpaces b)-  where (a,b) = BS.span constituent t+  where+    (a, b) = BS.span constituent t  readInt8 :: ReadValue Int8 readInt8 = readIntegral f-  where f (I8LIT x) = Just x-        f _          = Nothing+  where+    f (I8LIT x) = Just x+    f _ = Nothing  readInt16 :: ReadValue Int16 readInt16 = readIntegral f-  where f (I16LIT x) = Just x-        f _          = Nothing+  where+    f (I16LIT x) = Just x+    f _ = Nothing  readInt32 :: ReadValue Int32 readInt32 = readIntegral f-  where f (I32LIT x) = Just x-        f _          = Nothing+  where+    f (I32LIT x) = Just x+    f _ = Nothing  readInt64 :: ReadValue Int64 readInt64 = readIntegral f-  where f (I64LIT x) = Just x-        f _          = Nothing+  where+    f (I64LIT x) = Just x+    f _ = Nothing  readWord8 :: ReadValue Word8 readWord8 = readIntegral f-  where f (U8LIT x) = Just x-        f _          = Nothing+  where+    f (U8LIT x) = Just x+    f _ = Nothing  readWord16 :: ReadValue Word16 readWord16 = readIntegral f-  where f (U16LIT x) = Just x-        f _          = Nothing+  where+    f (U16LIT x) = Just x+    f _ = Nothing  readWord32 :: ReadValue Word32 readWord32 = readIntegral f-  where f (U32LIT x) = Just x-        f _          = Nothing+  where+    f (U32LIT x) = Just x+    f _ = Nothing  readWord64 :: ReadValue Word64 readWord64 = readIntegral f-  where f (U64LIT x) = Just x-        f _          = Nothing+  where+    f (U64LIT x) = Just x+    f _ = Nothing  readFloat :: RealFloat float => ([Token] -> Maybe float) -> ReadValue float readFloat f t = do   v <- case map unLoc . fst <$> scanTokens (Pos "" 1 1 0) a of-         Right [NEGATE, FLOATLIT x] -> Just $ negate $ fromDouble x-         Right [FLOATLIT x] -> Just $ fromDouble x-         Right (NEGATE : toks) -> negate <$> f toks-         Right toks -> f toks-         _ -> Nothing+    Right [NEGATE, FLOATLIT x] -> Just $ negate $ fromDouble x+    Right [FLOATLIT x] -> Just $ fromDouble x+    Right (NEGATE : toks) -> negate <$> f toks+    Right toks -> f toks+    _ -> Nothing   return (v, dropSpaces b)-  where (a,b) = BS.span constituent t-        fromDouble = uncurry encodeFloat . decodeFloat-        unLoc (L _ x) = x+  where+    (a, b) = BS.span constituent t+    fromDouble = uncurry encodeFloat . decodeFloat+    unLoc (L _ x) = x  readFloat32 :: ReadValue Float readFloat32 = readFloat lexFloat32-  where lexFloat32 [F32LIT x] = Just x-        lexFloat32 [ID "f32", PROJ_FIELD "inf"] = Just $ 1/0-        lexFloat32 [ID "f32", PROJ_FIELD "nan"] = Just $ 0/0-        lexFloat32 _ = Nothing+  where+    lexFloat32 [F32LIT x] = Just x+    lexFloat32 [ID "f32", PROJ_FIELD "inf"] = Just $ 1 / 0+    lexFloat32 [ID "f32", PROJ_FIELD "nan"] = Just $ 0 / 0+    lexFloat32 _ = Nothing  readFloat64 :: ReadValue Double readFloat64 = readFloat lexFloat64-  where lexFloat64 [F64LIT x] = Just x-        lexFloat64 [ID "f64", PROJ_FIELD "inf"] = Just $ 1/0-        lexFloat64 [ID "f64", PROJ_FIELD "nan"] = Just $ 0/0-        lexFloat64 _          = Nothing+  where+    lexFloat64 [F64LIT x] = Just x+    lexFloat64 [ID "f64", PROJ_FIELD "inf"] = Just $ 1 / 0+    lexFloat64 [ID "f64", PROJ_FIELD "nan"] = Just $ 0 / 0+    lexFloat64 _ = Nothing  readBool :: ReadValue Bool-readBool t = do v <- case fst <$> scanTokens (Pos "" 1 1 0) a of-                       Right [L _ TRUE]  -> Just True-                       Right [L _ FALSE] -> Just False-                       _                 -> Nothing-                return (v, dropSpaces b)-  where (a,b) = BS.span constituent t+readBool t = do+  v <- case fst <$> scanTokens (Pos "" 1 1 0) a of+    Right [L _ TRUE] -> Just True+    Right [L _ FALSE] -> Just False+    _ -> Nothing+  return (v, dropSpaces b)+  where+    (a, b) = BS.span constituent t  readPrimType :: ReadValue String readPrimType t = do   pt <- case fst <$> scanTokens (Pos "" 1 1 0) a of-          Right [L _ (ID s)] -> Just $ F.nameToString s-          _                  -> Nothing+    Right [L _ (ID s)] -> Just $ F.nameToString s+    _ -> Nothing   return (pt, dropSpaces b)-  where (a,b) = BS.span constituent t+  where+    (a, b) = BS.span constituent t  readEmptyArrayOfShape :: [Int] -> BS.ByteString -> Maybe (Value, BS.ByteString) readEmptyArrayOfShape shape t   | Just t' <- symbol '[' t,     Just (d, t'') <- readIntegral (const Nothing) t',-    Just t''' <- symbol ']' t'' = readEmptyArrayOfShape (shape++[d]) t'''-+    Just t''' <- symbol ']' t'' =+    readEmptyArrayOfShape (shape ++ [d]) t'''   | otherwise = do-      (pt, t') <- readPrimType t-      guard $ elem 0 shape-      v <- case pt of-             "i8" -> Just $ Int8Value (UVec.fromList shape) UVec.empty-             "i16" -> Just $ Int16Value (UVec.fromList shape) UVec.empty-             "i32" -> Just $ Int32Value (UVec.fromList shape) UVec.empty-             "i64" -> Just $ Int64Value (UVec.fromList shape) UVec.empty-             "u8" -> Just $ Word8Value (UVec.fromList shape) UVec.empty-             "u16" -> Just $ Word16Value (UVec.fromList shape) UVec.empty-             "u32" -> Just $ Word32Value (UVec.fromList shape) UVec.empty-             "u64" -> Just $ Word64Value (UVec.fromList shape) UVec.empty-             "f32" -> Just $ Float32Value (UVec.fromList shape) UVec.empty-             "f64" -> Just $ Float64Value (UVec.fromList shape) UVec.empty-             "bool" -> Just $ BoolValue (UVec.fromList shape) UVec.empty-             _  -> Nothing-      return (v, t')+    (pt, t') <- readPrimType t+    guard $ elem 0 shape+    v <- case pt of+      "i8" -> Just $ Int8Value (UVec.fromList shape) UVec.empty+      "i16" -> Just $ Int16Value (UVec.fromList shape) UVec.empty+      "i32" -> Just $ Int32Value (UVec.fromList shape) UVec.empty+      "i64" -> Just $ Int64Value (UVec.fromList shape) UVec.empty+      "u8" -> Just $ Word8Value (UVec.fromList shape) UVec.empty+      "u16" -> Just $ Word16Value (UVec.fromList shape) UVec.empty+      "u32" -> Just $ Word32Value (UVec.fromList shape) UVec.empty+      "u64" -> Just $ Word64Value (UVec.fromList shape) UVec.empty+      "f32" -> Just $ Float32Value (UVec.fromList shape) UVec.empty+      "f64" -> Just $ Float64Value (UVec.fromList shape) UVec.empty+      "bool" -> Just $ BoolValue (UVec.fromList shape) UVec.empty+      _ -> Nothing+    return (v, t')  readEmptyArray :: BS.ByteString -> Maybe (Value, BS.ByteString) readEmptyArray t = do@@ -425,54 +464,55 @@ readValue :: BS.ByteString -> Maybe (Value, BS.ByteString) readValue full_t   | Right (t', _, v) <- decodeOrFail full_t =-      Just (v, dropSpaces t')+    Just (v, dropSpaces t')   | otherwise = readEmptyArray full_t `mplus` insideBrackets 0 full_t-  where insideBrackets r t = maybe (tryValueAndReadValue r t) (insideBrackets (r+1)) $ symbol '[' t-        tryWith f mk r t-          | Just _ <- f t = do-              (shape, arr, rest_t) <- readRankedArrayOf r f full_t-              return (mk shape arr, rest_t)-          | otherwise = Nothing-        tryValueAndReadValue r t =-          -- 32-bit signed integers come first such that we parse-          -- unsuffixed integer constants as of that type.-          tryWith readInt32 Int32Value r t `mplus`-          tryWith readInt8 Int8Value r t `mplus`-          tryWith readInt16 Int16Value r t `mplus`-          tryWith readInt64 Int64Value r t `mplus`--          tryWith readWord8 Word8Value r t `mplus`-          tryWith readWord16 Word16Value r t `mplus`-          tryWith readWord32 Word32Value r t `mplus`-          tryWith readWord64 Word64Value r t `mplus`--          tryWith readFloat64 Float64Value r t `mplus`-          tryWith readFloat32 Float32Value r t `mplus`--          tryWith readBool BoolValue r t+  where+    insideBrackets r t = maybe (tryValueAndReadValue r t) (insideBrackets (r + 1)) $ symbol '[' t+    tryWith f mk r t+      | Just _ <- f t = do+        (shape, arr, rest_t) <- readRankedArrayOf r f full_t+        return (mk shape arr, rest_t)+      | otherwise = Nothing+    tryValueAndReadValue r t =+      -- 32-bit signed integers come first such that we parse+      -- unsuffixed integer constants as of that type.+      tryWith readInt32 Int32Value r t+        `mplus` tryWith readInt8 Int8Value r t+        `mplus` tryWith readInt16 Int16Value r t+        `mplus` tryWith readInt64 Int64Value r t+        `mplus` tryWith readWord8 Word8Value r t+        `mplus` tryWith readWord16 Word16Value r t+        `mplus` tryWith readWord32 Word32Value r t+        `mplus` tryWith readWord64 Word64Value r t+        `mplus` tryWith readFloat64 Float64Value r t+        `mplus` tryWith readFloat32 Float32Value r t+        `mplus` tryWith readBool BoolValue r t  -- | Parse Futhark values from the given bytestring. readValues :: BS.ByteString -> Maybe [Value] readValues = readValues' . dropSpaces-  where readValues' t-          | BS.null t = Just []-          | otherwise = do (a, t') <- readValue t-                           (a:) <$> readValues' t'+  where+    readValues' t+      | BS.null t = Just []+      | otherwise = do+        (a, t') <- readValue t+        (a :) <$> readValues' t'  -- Comparisons  -- | Two values differ in some way.  The 'Show' instance produces a -- human-readable explanation.-data Mismatch = PrimValueMismatch (Int,Int) PrimValue PrimValue-              -- ^ The position the value number and a flat index-              -- into the array.-              | ArrayShapeMismatch Int [Int] [Int]-              | TypeMismatch Int String String-              | ValueCountMismatch Int Int+data Mismatch+  = -- | The position the value number and a flat index+    -- into the array.+    PrimValueMismatch (Int, Int) PrimValue PrimValue+  | ArrayShapeMismatch Int [Int] [Int]+  | TypeMismatch Int String String+  | ValueCountMismatch Int Int  instance Show Mismatch where-  show (PrimValueMismatch (i,j) got expected) =-    explainMismatch (i,j) "" got expected+  show (PrimValueMismatch (i, j) got expected) =+    explainMismatch (i, j) "" got expected   show (ArrayShapeMismatch i got expected) =     explainMismatch i "array of shape " got expected   show (TypeMismatch i got expected) =@@ -490,9 +530,10 @@ compareValues :: [Value] -> [Value] -> [Mismatch] compareValues got expected   | n /= m = [ValueCountMismatch n m]-  | otherwise = concat $ zipWith3 compareValue [0..] got expected-  where n = length got-        m = length expected+  | otherwise = concat $ zipWith3 compareValue [0 ..] got expected+  where+    n = length got+    m = length expected  -- | As 'compareValues', but only reports one mismatch. compareValues1 :: [Value] -> [Value] -> Maybe Mismatch@@ -527,38 +568,47 @@       _ ->         [TypeMismatch i (pretty $ valueElemType got_v) (pretty $ valueElemType expected_v)]   | otherwise =-      [ArrayShapeMismatch i (valueShape got_v) (valueShape expected_v)]-  where compareNum tol = compareGen $ compareElement tol-        compareFloat tol = compareGen $ compareFloatElement tol+    [ArrayShapeMismatch i (valueShape got_v) (valueShape expected_v)]+  where+    compareNum tol = compareGen $ compareElement tol+    compareFloat tol = compareGen $ compareFloatElement tol -        compareGen cmp got expected =-          concat $-          zipWith cmp (UVec.toList $ UVec.indexed got) (UVec.toList expected)+    compareGen cmp got expected =+      concat $+        zipWith cmp (UVec.toList $ UVec.indexed got) (UVec.toList expected) -        compareElement tol (j, got) expected-          | comparePrimValue tol got expected = []-          | otherwise = [PrimValueMismatch (i,j) (value got) (value expected)]+    compareElement tol (j, got) expected+      | comparePrimValue tol got expected = []+      | otherwise = [PrimValueMismatch (i, j) (value got) (value expected)] -        compareFloatElement tol (j, got) expected-          | isNaN got, isNaN expected = []-          | isInfinite got, isInfinite expected,-            signum got == signum expected = []-          | otherwise = compareElement tol (j, got) expected+    compareFloatElement tol (j, got) expected+      | isNaN got, isNaN expected = []+      | isInfinite got,+        isInfinite expected,+        signum got == signum expected =+        []+      | otherwise = compareElement tol (j, got) expected -        compareBool (j, got) expected-          | got == expected = []-          | otherwise = [PrimValueMismatch (i,j) (value got) (value expected)]+    compareBool (j, got) expected+      | got == expected = []+      | otherwise = [PrimValueMismatch (i, j) (value got) (value expected)] -comparePrimValue :: (Ord num, Num num) =>-                    num -> num -> num -> Bool+comparePrimValue ::+  (Ord num, Num num) =>+  num ->+  num ->+  num ->+  Bool comparePrimValue tol x y =   diff < tol-  where diff = abs $ x - y+  where+    diff = abs $ x - y  minTolerance :: Fractional a => a minTolerance = 0.002 -- 0.2%  tolerance :: (RealFloat a, UVec.Unbox a) => Vector a -> a tolerance = UVec.foldl tolerance' minTolerance . UVec.filter (not . nanOrInf)-  where tolerance' t v = max t $ minTolerance * v-        nanOrInf x = isInfinite x || isNaN x+  where+    tolerance' t v = max t $ minTolerance * v+    nanOrInf x = isInfinite x || isNaN x
src/Futhark/Tools.hs view
@@ -1,29 +1,25 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}+ -- | An unstructured grab-bag of various tools and inspection -- functions that didn't really fit anywhere else. module Futhark.Tools-  (-    module Futhark.Construct--  , redomapToMapAndReduce-  , dissectScrema-  , sequentialStreamWholeArray--  , partitionChunkedFoldParameters+  ( module Futhark.Construct,+    redomapToMapAndReduce,+    dissectScrema,+    sequentialStreamWholeArray,+    partitionChunkedFoldParameters, -  -- * Primitive expressions-  , module Futhark.Analysis.PrimExp.Convert+    -- * Primitive expressions+    module Futhark.Analysis.PrimExp.Convert,   ) where  import Control.Monad.Identity-+import Futhark.Analysis.PrimExp.Convert+import Futhark.Construct import Futhark.IR import Futhark.IR.SOACS.SOAC-import Futhark.MonadFreshNames-import Futhark.Construct-import Futhark.Analysis.PrimExp.Convert import Futhark.Util  -- | Turns a binding of a @redomap@ into two seperate bindings, a@@ -33,30 +29,42 @@ -- pattern with new 'Ident's for the result of the @map@. -- -- Only handles a pattern with an empty 'patternContextElements'.-redomapToMapAndReduce :: (MonadFreshNames m, Bindable lore,-                          ExpDec lore ~ (), Op lore ~ SOAC lore) =>-                         Pattern lore-                      -> ( SubExp-                         , Commutativity-                         , LambdaT lore, LambdaT lore, [SubExp]-                         , [VName])-                      -> m (Stm lore, Stm lore)-redomapToMapAndReduce (Pattern [] patelems)-                      (w, comm, redlam, map_lam, accs, arrs) = do-  (map_pat, red_pat, red_args) <--    splitScanOrRedomap patelems w map_lam accs-  let map_bnd = mkLet [] map_pat $ Op $ Screma w (mapSOAC map_lam) arrs-      (nes, red_arrs) = unzip red_args-  red_bnd <- Let red_pat (defAux ()) . Op <$>-             (Screma w <$> reduceSOAC [Reduce comm redlam nes] <*> pure red_arrs)-  return (map_bnd, red_bnd)+redomapToMapAndReduce ::+  ( MonadFreshNames m,+    Bindable lore,+    ExpDec lore ~ (),+    Op lore ~ SOAC lore+  ) =>+  Pattern lore ->+  ( SubExp,+    Commutativity,+    LambdaT lore,+    LambdaT lore,+    [SubExp],+    [VName]+  ) ->+  m (Stm lore, Stm lore)+redomapToMapAndReduce+  (Pattern [] patelems)+  (w, comm, redlam, map_lam, accs, arrs) = do+    (map_pat, red_pat, red_args) <-+      splitScanOrRedomap patelems w map_lam accs+    let map_bnd = mkLet [] map_pat $ Op $ Screma w (mapSOAC map_lam) arrs+        (nes, red_arrs) = unzip red_args+    red_bnd <-+      Let red_pat (defAux ()) . Op+        <$> (Screma w <$> reduceSOAC [Reduce comm redlam nes] <*> pure red_arrs)+    return (map_bnd, red_bnd) redomapToMapAndReduce _ _ =   error "redomapToMapAndReduce does not handle a non-empty 'patternContextElements'" -splitScanOrRedomap :: (Typed dec, MonadFreshNames m) =>-                      [PatElemT dec]-                   -> SubExp -> LambdaT lore -> [SubExp]-                   -> m ([Ident], PatternT dec, [(SubExp, VName)])+splitScanOrRedomap ::+  (Typed dec, MonadFreshNames m) =>+  [PatElemT dec] ->+  SubExp ->+  LambdaT lore ->+  [SubExp] ->+  m ([Ident], PatternT dec, [(SubExp, VName)]) splitScanOrRedomap patelems w map_lam accs = do   let (acc_patelems, arr_patelems) = splitAt (length accs) patelems       (acc_ts, _arr_ts) = splitAt (length accs) $ lambdaReturnType map_lam@@ -74,10 +82,16 @@ -- Redomap.  This is used to handle Scremas that are so complicated -- that we cannot directly generate efficient parallel code for them. -- In essense, what happens is the opposite of horisontal fusion.-dissectScrema :: (MonadBinder m, Op (Lore m) ~ SOAC (Lore m),-                  Bindable (Lore m)) =>-                 Pattern (Lore m) -> SubExp -> ScremaForm (Lore m) -> [VName]-              -> m ()+dissectScrema ::+  ( MonadBinder m,+    Op (Lore m) ~ SOAC (Lore m),+    Bindable (Lore m)+  ) =>+  Pattern (Lore m) ->+  SubExp ->+  ScremaForm (Lore m) ->+  [VName] ->+  m () dissectScrema pat w (ScremaForm scans reds map_lam) arrs = do   let num_reds = redResults reds       num_scans = scanResults scans@@ -95,11 +109,14 @@  -- | Turn a stream SOAC into statements that apply the stream lambda -- to the entire input.-sequentialStreamWholeArray :: (MonadBinder m, Bindable (Lore m)) =>-                              Pattern (Lore m)-                           -> SubExp -> [SubExp]-                           -> LambdaT (Lore m) -> [VName]-                           -> m ()+sequentialStreamWholeArray ::+  (MonadBinder m, Bindable (Lore m)) =>+  Pattern (Lore m) ->+  SubExp ->+  [SubExp] ->+  LambdaT (Lore m) ->+  [VName] ->+  m () sequentialStreamWholeArray pat w nes lam arrs = do   -- We just set the chunksize to w and inline the lambda body.  There   -- is no difference between parallel and sequential streams here.@@ -116,8 +133,9 @@   -- Finally, the array parameters are set to the arrays (but reshaped   -- to make the types work out; this will be simplified rapidly).   forM_ (zip arr_params arrs) $ \(p, arr) ->-    letBindNames [paramName p] $ BasicOp $-      Reshape (map DimCoercion $ arrayDims $ paramType p) arr+    letBindNames [paramName p] $+      BasicOp $+        Reshape (map DimCoercion $ arrayDims $ paramType p) arr    -- Then we just inline the lambda body.   mapM_ addStm $ bodyStms $ lambdaBody lam@@ -129,17 +147,19 @@     case (arrayDims $ patElemType pe, se) of       (dims, Var v)         | not $ null dims ->-            letBindNames [patElemName pe] $ BasicOp $ Reshape (map DimCoercion dims) v+          letBindNames [patElemName pe] $ BasicOp $ Reshape (map DimCoercion dims) v       _ -> letBindNames [patElemName pe] $ BasicOp $ SubExp se  -- | Split the parameters of a stream reduction lambda into the chunk -- size parameter, the accumulator parameters, and the input chunk -- parameters.  The integer argument is how many accumulators are -- used.-partitionChunkedFoldParameters :: Int -> [Param dec]-                               -> (Param dec, [Param dec], [Param dec])+partitionChunkedFoldParameters ::+  Int ->+  [Param dec] ->+  (Param dec, [Param dec], [Param dec]) partitionChunkedFoldParameters _ [] =   error "partitionChunkedFoldParameters: lambda takes no parameters" partitionChunkedFoldParameters num_accs (chunk_param : params) =   let (acc_params, arr_params) = splitAt num_accs params-  in (chunk_param, acc_params, arr_params)+   in (chunk_param, acc_params, arr_params)
src/Futhark/Transform/CopyPropagate.hs view
@@ -1,41 +1,45 @@-{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+ -- | Perform copy propagation.  This is done by invoking the -- simplifier with no rules, so hoisting and dead-code elimination may -- also take place. module Futhark.Transform.CopyPropagate-       ( copyPropagateInProg-       , copyPropagateInStms-       , copyPropagateInFun-       )+  ( copyPropagateInProg,+    copyPropagateInStms,+    copyPropagateInFun,+  ) where -import Futhark.Pass-import Futhark.MonadFreshNames+import qualified Futhark.Analysis.SymbolTable as ST import Futhark.IR+import Futhark.MonadFreshNames import Futhark.Optimise.Simplify-import qualified Futhark.Analysis.SymbolTable as ST import Futhark.Optimise.Simplify.Lore (Wise)+import Futhark.Pass  -- | Run copy propagation on an entire program.-copyPropagateInProg :: SimplifiableLore lore =>-                       SimpleOps lore-                    -> Prog lore-                    -> PassM (Prog lore)+copyPropagateInProg ::+  SimplifiableLore lore =>+  SimpleOps lore ->+  Prog lore ->+  PassM (Prog lore) copyPropagateInProg simpl = simplifyProg simpl mempty neverHoist  -- | Run copy propagation on some statements.-copyPropagateInStms :: (MonadFreshNames m, SimplifiableLore lore) =>-                       SimpleOps lore-                    -> Scope lore-                    -> Stms lore-                    -> m (ST.SymbolTable (Wise lore), Stms lore)+copyPropagateInStms ::+  (MonadFreshNames m, SimplifiableLore lore) =>+  SimpleOps lore ->+  Scope lore ->+  Stms lore ->+  m (ST.SymbolTable (Wise lore), Stms lore) copyPropagateInStms simpl = simplifyStms simpl mempty neverHoist  -- | Run copy propagation on a function.-copyPropagateInFun :: (MonadFreshNames m, SimplifiableLore lore) =>-                       SimpleOps lore-                   -> ST.SymbolTable (Wise lore)-                   -> FunDef lore-                   -> m (FunDef lore)+copyPropagateInFun ::+  (MonadFreshNames m, SimplifiableLore lore) =>+  SimpleOps lore ->+  ST.SymbolTable (Wise lore) ->+  FunDef lore ->+  m (FunDef lore) copyPropagateInFun simpl = simplifyFun simpl mempty neverHoist
src/Futhark/Transform/FirstOrderTransform.hs view
@@ -1,28 +1,27 @@ {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}+ -- | The code generator cannot handle the array combinators (@map@ and -- friends), so this module was written to transform them into the -- equivalent do-loops.  The transformation is currently rather naive, -- and - it's certainly worth considering when we can express such -- transformations in-place. module Futhark.Transform.FirstOrderTransform-  ( transformFunDef-  , transformConsts--  , FirstOrderLore-  , Transformer-  , transformStmRecursively-  , transformLambda-  , transformSOAC+  ( transformFunDef,+    transformConsts,+    FirstOrderLore,+    Transformer,+    transformStmRecursively,+    transformLambda,+    transformSOAC,   )-  where+where  import Control.Monad.Except import Control.Monad.State-import qualified Data.Map.Strict as M import Data.List (zip4)-+import qualified Data.Map.Strict as M import qualified Futhark.IR as AST import Futhark.IR.SOACS import Futhark.MonadFreshNames@@ -32,65 +31,84 @@ -- | The constraints that must hold for a lore in order to be the -- target of first-order transformation. type FirstOrderLore lore =-  (Bindable lore, BinderOps lore,-   LetDec SOACS ~ LetDec lore,-   LParamInfo SOACS ~ LParamInfo lore)+  ( Bindable lore,+    BinderOps lore,+    LetDec SOACS ~ LetDec lore,+    LParamInfo SOACS ~ LParamInfo lore+  )  -- | First-order-transform a single function, with the given scope -- provided by top-level constants.-transformFunDef :: (MonadFreshNames m, FirstOrderLore tolore) =>-                   Scope tolore -> FunDef SOACS -> m (AST.FunDef tolore)+transformFunDef ::+  (MonadFreshNames m, FirstOrderLore tolore) =>+  Scope tolore ->+  FunDef SOACS ->+  m (AST.FunDef tolore) transformFunDef consts_scope (FunDef entry attrs fname rettype params body) = do-  (body',_) <- modifyNameSource $ runState $ runBinderT m consts_scope+  (body', _) <- modifyNameSource $ runState $ runBinderT m consts_scope   return $ FunDef entry attrs fname rettype params body'-  where m = localScope (scopeOfFParams params) $ insertStmsM $ transformBody body+  where+    m = localScope (scopeOfFParams params) $ insertStmsM $ transformBody body  -- | First-order-transform these top-level constants.-transformConsts :: (MonadFreshNames m, FirstOrderLore tolore) =>-                 Stms SOACS -> m (AST.Stms tolore)+transformConsts ::+  (MonadFreshNames m, FirstOrderLore tolore) =>+  Stms SOACS ->+  m (AST.Stms tolore) transformConsts stms =   fmap snd $ modifyNameSource $ runState $ runBinderT m mempty-  where m = mapM_ transformStmRecursively stms+  where+    m = mapM_ transformStmRecursively stms  -- | The constraints that a monad must uphold in order to be used for -- first-order transformation.-type Transformer m = (MonadBinder m, LocalScope (Lore m) m,-                      Bindable (Lore m), BinderOps (Lore m),-                      LParamInfo SOACS ~ LParamInfo (Lore m))+type Transformer m =+  ( MonadBinder m,+    LocalScope (Lore m) m,+    Bindable (Lore m),+    BinderOps (Lore m),+    LParamInfo SOACS ~ LParamInfo (Lore m)+  ) -transformBody :: (Transformer m, LetDec (Lore m) ~ LetDec SOACS) =>-                 Body -> m (AST.Body (Lore m))+transformBody ::+  (Transformer m, LetDec (Lore m) ~ LetDec SOACS) =>+  Body ->+  m (AST.Body (Lore m)) transformBody (Body () bnds res) = insertStmsM $ do   mapM_ transformStmRecursively bnds   return $ resultBody res  -- | First transform any nested t'Body' or t'Lambda' elements, then -- apply 'transformSOAC' if the expression is a SOAC.-transformStmRecursively :: (Transformer m, LetDec (Lore m) ~ LetDec SOACS) =>-                           Stm -> m ()-+transformStmRecursively ::+  (Transformer m, LetDec (Lore m) ~ LetDec SOACS) =>+  Stm ->+  m () transformStmRecursively (Let pat aux (Op soac)) =   auxing aux $ transformSOAC pat =<< mapSOACM soacTransform soac-  where soacTransform = identitySOACMapper { mapOnSOACLambda = transformLambda }-+  where+    soacTransform = identitySOACMapper {mapOnSOACLambda = transformLambda} transformStmRecursively (Let pat aux e) =   auxing aux $ letBind pat =<< mapExpM transform e-  where transform = identityMapper { mapOnBody = \scope -> localScope scope . transformBody-                                   , mapOnRetType = return-                                   , mapOnBranchType = return-                                   , mapOnFParam = return-                                   , mapOnLParam = return-                                   , mapOnOp = error "Unhandled Op in first order transform"-                                   }+  where+    transform =+      identityMapper+        { mapOnBody = \scope -> localScope scope . transformBody,+          mapOnRetType = return,+          mapOnBranchType = return,+          mapOnFParam = return,+          mapOnLParam = return,+          mapOnOp = error "Unhandled Op in first order transform"+        }  -- | Transform a single 'SOAC' into a do-loop.  The body of the lambda -- is untouched, and may or may not contain further 'SOAC's depending -- on the given lore.-transformSOAC :: Transformer m =>-                 AST.Pattern (Lore m)-              -> SOAC (Lore m)-              -> m ()-+transformSOAC ::+  Transformer m =>+  AST.Pattern (Lore m) ->+  SOAC (Lore m) ->+  m () transformSOAC pat (Screma w form@(ScremaForm scans reds map_lam) arrs) = do   -- Start by combining all the reduction parts into a single operator   let Reduce _ red_lam red_nes = singleReduce reds@@ -116,54 +134,68 @@   redout_params <- mapM (newParam "redout" . flip toDecl Nonunique) $ lambdaReturnType red_lam   mapout_params <- mapM (newParam "mapout" . flip toDecl Unique) map_arr_ts -  let merge = concat [zip scanacc_params scan_nes,-                      zip scanout_params $ map Var scan_arrs,-                      zip redout_params red_nes,-                      zip mapout_params $ map Var map_arrs]+  let merge =+        concat+          [ zip scanacc_params scan_nes,+            zip scanout_params $ map Var scan_arrs,+            zip redout_params red_nes,+            zip mapout_params $ map Var map_arrs+          ]   i <- newVName "i"-  let loopform = ForLoop i Int32 w []+  let loopform = ForLoop i Int64 w []    loop_body <- runBodyBinder $-               localScope (scopeOfFParams $ map fst merge) $-               inScopeOf loopform $ do--    forM_ (zip (lambdaParams map_lam) arrs) $ \(p, arr) -> do-      arr_t <- lookupType arr-      letBindNames [paramName p] $ BasicOp $ Index arr $-        fullSlice arr_t [DimFix $ Var i]+    localScope (scopeOfFParams $ map fst merge) $+      inScopeOf loopform $ do+        forM_ (zip (lambdaParams map_lam) arrs) $ \(p, arr) -> do+          arr_t <- lookupType arr+          letBindNames [paramName p] $+            BasicOp $+              Index arr $+                fullSlice arr_t [DimFix $ Var i] -    -- Insert the statements of the lambda.  We have taken care to-    -- ensure that the parameters are bound at this point.-    mapM_ addStm $ bodyStms $ lambdaBody map_lam-    -- Split into scan results, reduce results, and map results.-    let (scan_res, red_res, map_res) =-          splitAt3 (length scan_nes) (length red_nes) $-          bodyResult $ lambdaBody map_lam+        -- Insert the statements of the lambda.  We have taken care to+        -- ensure that the parameters are bound at this point.+        mapM_ addStm $ bodyStms $ lambdaBody map_lam+        -- Split into scan results, reduce results, and map results.+        let (scan_res, red_res, map_res) =+              splitAt3 (length scan_nes) (length red_nes) $+                bodyResult $ lambdaBody map_lam -    scan_res' <- eLambda scan_lam $ map (pure . BasicOp . SubExp) $-                 map (Var . paramName) scanacc_params ++ scan_res-    red_res' <- eLambda red_lam $ map (pure . BasicOp . SubExp) $-                map (Var . paramName) redout_params ++ red_res+        scan_res' <-+          eLambda scan_lam $+            map (pure . BasicOp . SubExp) $+              map (Var . paramName) scanacc_params ++ scan_res+        red_res' <-+          eLambda red_lam $+            map (pure . BasicOp . SubExp) $+              map (Var . paramName) redout_params ++ red_res -    -- Write the scan accumulator to the scan result arrays.-    scan_outarrs <- letwith (map paramName scanout_params) (pexp (Var i)) $-                    map (BasicOp . SubExp) scan_res'+        -- Write the scan accumulator to the scan result arrays.+        scan_outarrs <-+          letwith (map paramName scanout_params) (pexp (Var i)) $+            map (BasicOp . SubExp) scan_res' -    -- Write the map results to the map result arrays.-    map_outarrs <- letwith (map paramName mapout_params) (pexp (Var i)) $-                   map (BasicOp . SubExp) map_res+        -- Write the map results to the map result arrays.+        map_outarrs <-+          letwith (map paramName mapout_params) (pexp (Var i)) $+            map (BasicOp . SubExp) map_res -    return $ resultBody $ concat [scan_res',-                                  map Var scan_outarrs,-                                  red_res',-                                  map Var map_outarrs]+        return $+          resultBody $+            concat+              [ scan_res',+                map Var scan_outarrs,+                red_res',+                map Var map_outarrs+              ]    -- We need to discard the final scan accumulators, as they are not   -- bound in the original pattern.-  names <- (++patternNames pat)-           <$> replicateM (length scanacc_params) (newVName "discard")+  names <-+    (++ patternNames pat)+      <$> replicateM (length scanacc_params) (newVName "discard")   letBindNames names $ DoLoop [] merge loopform loop_body- transformSOAC pat (Stream w stream_form lam arrs) = do   -- Create a loop that repeatedly applies the lambda body to a   -- chunksize of 1.  Hopefully this will lead to this outer loop@@ -176,40 +208,50 @@   mapout_merge <- forM (drop (length nes) $ lambdaReturnType lam) $ \t ->     let t' = t `setOuterSize` w         scratch = BasicOp $ Scratch (elemType t') (arrayDims t')-    in (,)-       <$> newParam "stream_mapout" (toDecl t' Unique)-       <*> letSubExp "stream_mapout_scratch" scratch+     in (,)+          <$> newParam "stream_mapout" (toDecl t' Unique)+          <*> letSubExp "stream_mapout_scratch" scratch -  let merge = zip (map (fmap (`toDecl` Nonunique)) fold_params) nes ++-              mapout_merge+  let merge =+        zip (map (fmap (`toDecl` Nonunique)) fold_params) nes+          ++ mapout_merge       merge_params = map fst merge       mapout_params = map fst mapout_merge    i <- newVName "i" -  let loop_form = ForLoop i Int32 w []+  let loop_form = ForLoop i Int64 w []    letBindNames [paramName chunk_size_param] $-    BasicOp $ SubExp $ intConst Int32 1+    BasicOp $ SubExp $ intConst Int64 1 -  loop_body <- runBodyBinder $ localScope (scopeOf loop_form <>-                                           scopeOfFParams merge_params) $ do-    let slice =-          [DimSlice (Var i) (Var (paramName chunk_size_param)) (intConst Int32 1)]-    forM_ (zip chunk_params arrs) $ \(p, arr) ->-      letBindNames [paramName p] $ BasicOp $ Index arr $-      fullSlice (paramType p) slice+  loop_body <- runBodyBinder $+    localScope+      ( scopeOf loop_form+          <> scopeOfFParams merge_params+      )+      $ do+        let slice =+              [DimSlice (Var i) (Var (paramName chunk_size_param)) (intConst Int64 1)]+        forM_ (zip chunk_params arrs) $ \(p, arr) ->+          letBindNames [paramName p] $+            BasicOp $+              Index arr $+                fullSlice (paramType p) slice -    (res, mapout_res) <- splitAt (length nes) <$> bodyBind (lambdaBody lam)+        (res, mapout_res) <- splitAt (length nes) <$> bodyBind (lambdaBody lam) -    mapout_res' <- forM (zip mapout_params mapout_res) $ \(p, se) ->-      letSubExp "mapout_res" $ BasicOp $ Update (paramName p)-      (fullSlice (paramType p) slice) se+        mapout_res' <- forM (zip mapout_params mapout_res) $ \(p, se) ->+          letSubExp "mapout_res" $+            BasicOp $+              Update+                (paramName p)+                (fullSlice (paramType p) slice)+                se -    resultBodyM $ res ++ mapout_res'+        resultBodyM $ res ++ mapout_res'    letBind pat $ DoLoop [] merge loop_form loop_body- transformSOAC pat (Scatter len lam ivs as) = do   iter <- newVName "write_iter" @@ -222,101 +264,118 @@   -- Scatter is in-place, so we use the input array as the output array.   let merge = loopMerge asOuts $ map Var as_vs   loopBody <- runBodyBinder $-    localScope (M.insert iter (IndexName Int32) $-                scopeOfFParams $ map fst merge) $ do-    ivs' <- forM ivs $ \iv -> do-      iv_t <- lookupType iv-      letSubExp "write_iv" $ BasicOp $ Index iv $ fullSlice iv_t [DimFix $ Var iter]-    ivs'' <- bindLambda lam (map (BasicOp . SubExp) ivs')--    let indexes = chunks as_ns $ take ivsLen ivs''-        values = chunks as_ns $ drop ivsLen ivs''+    localScope+      ( M.insert iter (IndexName Int64) $+          scopeOfFParams $ map fst merge+      )+      $ do+        ivs' <- forM ivs $ \iv -> do+          iv_t <- lookupType iv+          letSubExp "write_iv" $ BasicOp $ Index iv $ fullSlice iv_t [DimFix $ Var iter]+        ivs'' <- bindLambda lam (map (BasicOp . SubExp) ivs') -    ress <- forM (zip3 indexes values (map identName asOuts)) $ \(indexes', values', arr) -> do-      let saveInArray arr' (indexCur, valueCur) =-            letExp "write_out" =<< eWriteArray arr' [eSubExp indexCur] (eSubExp valueCur)+        let indexes = chunks as_ns $ take ivsLen ivs''+            values = chunks as_ns $ drop ivsLen ivs'' -      foldM saveInArray arr $ zip indexes' values'-    return $ resultBody (map Var ress)-  letBind pat $ DoLoop [] merge (ForLoop iter Int32 len []) loopBody+        ress <- forM (zip3 indexes values (map identName asOuts)) $ \(indexes', values', arr) -> do+          let saveInArray arr' (indexCur, valueCur) =+                letExp "write_out" =<< eWriteArray arr' [eSubExp indexCur] (eSubExp valueCur) +          foldM saveInArray arr $ zip indexes' values'+        return $ resultBody (map Var ress)+  letBind pat $ DoLoop [] merge (ForLoop iter Int64 len []) loopBody transformSOAC pat (Hist len ops bucket_fun imgs) = do   iter <- newVName "iter"    -- Bind arguments to parameters for the merge-variables.-  hists_ts  <- mapM lookupType $ concatMap histDest ops+  hists_ts <- mapM lookupType $ concatMap histDest ops   hists_out <- mapM (newIdent "dests") hists_ts   let merge = loopMerge hists_out $ concatMap (map Var . histDest) ops    -- Bind lambda-bodies for operators.   loopBody <- runBodyBinder $-    localScope (M.insert iter (IndexName Int32) $-                scopeOfFParams $ map fst merge) $ do--    -- Bind images to parameters of bucket function.-    imgs' <- forM imgs $ \img -> do-      img_t <- lookupType img-      letSubExp "pixel" $ BasicOp $ Index img $ fullSlice img_t [DimFix $ Var iter]-    imgs'' <- bindLambda bucket_fun $ map (BasicOp . SubExp) imgs'+    localScope+      ( M.insert iter (IndexName Int64) $+          scopeOfFParams $ map fst merge+      )+      $ do+        -- Bind images to parameters of bucket function.+        imgs' <- forM imgs $ \img -> do+          img_t <- lookupType img+          letSubExp "pixel" $ BasicOp $ Index img $ fullSlice img_t [DimFix $ Var iter]+        imgs'' <- bindLambda bucket_fun $ map (BasicOp . SubExp) imgs' -    -- Split out values from bucket function.-    let lens = length ops-        inds = take lens imgs''-        vals = chunks (map (length . lambdaReturnType . histOp) ops) $ drop lens imgs''-        hists_out' = chunks (map (length . lambdaReturnType . histOp) ops) $-                     map identName hists_out+        -- Split out values from bucket function.+        let lens = length ops+            inds = take lens imgs''+            vals = chunks (map (length . lambdaReturnType . histOp) ops) $ drop lens imgs''+            hists_out' =+              chunks (map (length . lambdaReturnType . histOp) ops) $+                map identName hists_out -    hists_out'' <- forM (zip4 hists_out' ops inds vals) $ \(hist, op, idx, val) -> do-      -- Check whether the indexes are in-bound.  If they are not, we-      -- return the histograms unchanged.-      let outside_bounds_branch = insertStmsM $ resultBodyM $ map Var hist-          oob = case hist of [] -> eSubExp $ constant True-                             arr:_ -> eOutOfBounds arr [eSubExp idx]+        hists_out'' <- forM (zip4 hists_out' ops inds vals) $ \(hist, op, idx, val) -> do+          -- Check whether the indexes are in-bound.  If they are not, we+          -- return the histograms unchanged.+          let outside_bounds_branch = insertStmsM $ resultBodyM $ map Var hist+              oob = case hist of+                [] -> eSubExp $ constant True+                arr : _ -> eOutOfBounds arr [eSubExp idx] -      letTupExp "new_histo" <=<-        eIf oob outside_bounds_branch $ do-        -- Read values from histogram.-        h_val <- forM hist $ \arr -> do-          arr_t <- lookupType arr-          letSubExp "read_hist" $ BasicOp $ Index arr $ fullSlice arr_t [DimFix idx]+          letTupExp "new_histo"+            <=< eIf oob outside_bounds_branch+            $ do+              -- Read values from histogram.+              h_val <- forM hist $ \arr -> do+                arr_t <- lookupType arr+                letSubExp "read_hist" $ BasicOp $ Index arr $ fullSlice arr_t [DimFix idx] -        -- Apply operator.-        h_val' <- bindLambda (histOp op) $+              -- Apply operator.+              h_val' <-+                bindLambda (histOp op) $                   map (BasicOp . SubExp) $ h_val ++ val -        -- Write values back to histograms.-        hist' <- forM (zip hist h_val') $  \(arr, v) -> do-          arr_t <- lookupType arr-          letInPlace "hist_out" arr (fullSlice arr_t [DimFix idx]) $-            BasicOp $ SubExp v+              -- Write values back to histograms.+              hist' <- forM (zip hist h_val') $ \(arr, v) -> do+                arr_t <- lookupType arr+                letInPlace "hist_out" arr (fullSlice arr_t [DimFix idx]) $+                  BasicOp $ SubExp v -        return $ resultBody $ map Var hist'+              return $ resultBody $ map Var hist' -    return $ resultBody $ map Var $ concat hists_out''+        return $ resultBody $ map Var $ concat hists_out''    -- Wrap up the above into a for-loop.-  letBind pat $ DoLoop [] merge (ForLoop iter Int32 len []) loopBody+  letBind pat $ DoLoop [] merge (ForLoop iter Int64 len []) loopBody  -- | Recursively first-order-transform a lambda.-transformLambda :: (MonadFreshNames m,-                    Bindable lore, BinderOps lore,-                    LocalScope somelore m,-                    SameScope somelore lore,-                    LetDec lore ~ LetDec SOACS) =>-                   Lambda -> m (AST.Lambda lore)+transformLambda ::+  ( MonadFreshNames m,+    Bindable lore,+    BinderOps lore,+    LocalScope somelore m,+    SameScope somelore lore,+    LetDec lore ~ LetDec SOACS+  ) =>+  Lambda ->+  m (AST.Lambda lore) transformLambda (Lambda params body rettype) = do-  body' <- runBodyBinder $-           localScope (scopeOfLParams params) $-           transformBody body+  body' <-+    runBodyBinder $+      localScope (scopeOfLParams params) $+        transformBody body   return $ Lambda params body' rettype  resultArray :: Transformer m => [Type] -> m [VName] resultArray = mapM oneArray-  where oneArray t = letExp "result" $ BasicOp $ Scratch (elemType t) (arrayDims t)+  where+    oneArray t = letExp "result" $ BasicOp $ Scratch (elemType t) (arrayDims t) -letwith :: Transformer m =>-           [VName] -> m (AST.Exp (Lore m)) -> [AST.Exp (Lore m)]-        -> m [VName]+letwith ::+  Transformer m =>+  [VName] ->+  m (AST.Exp (Lore m)) ->+  [AST.Exp (Lore m)] ->+  m [VName] letwith ks i vs = do   vs' <- letSubExps "values" vs   i' <- letSubExp "i" =<< i@@ -328,19 +387,23 @@ pexp :: Applicative f => SubExp -> f (AST.Exp lore) pexp = pure . BasicOp . SubExp -bindLambda :: Transformer m =>-              AST.Lambda (Lore m) -> [AST.Exp (Lore m)]-           -> m [SubExp]+bindLambda ::+  Transformer m =>+  AST.Lambda (Lore m) ->+  [AST.Exp (Lore m)] ->+  m [SubExp] bindLambda (Lambda params body _) args = do   forM_ (zip params args) $ \(param, arg) ->     if primType $ paramType param-    then letBindNames [paramName param] arg-    else letBindNames [paramName param] =<< eCopy (pure arg)+      then letBindNames [paramName param] arg+      else letBindNames [paramName param] =<< eCopy (pure arg)   bodyBind body  loopMerge :: [Ident] -> [SubExp] -> [(Param DeclType, SubExp)] loopMerge vars = loopMerge' $ zip vars $ repeat Unique -loopMerge' :: [(Ident,Uniqueness)] -> [SubExp] -> [(Param DeclType, SubExp)]-loopMerge' vars vals = [ (Param pname $ toDecl ptype u, val)-                       | ((Ident pname ptype, u),val) <- zip vars vals ]+loopMerge' :: [(Ident, Uniqueness)] -> [SubExp] -> [(Param DeclType, SubExp)]+loopMerge' vars vals =+  [ (Param pname $ toDecl ptype u, val)+    | ((Ident pname ptype, u), val) <- zip vars vals+  ]
src/Futhark/Transform/Rename.hs view
@@ -1,106 +1,128 @@-{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE Trustworthy #-}+{-# LANGUAGE UndecidableInstances #-}+ -- | This module provides facilities for transforming Futhark programs -- such that names are unique, via the 'renameProg' function. module Futhark.Transform.Rename-  (-  -- * Renaming programs-   renameProg-  -- * Renaming parts of a program.-  ---  -- These all require execution in a 'MonadFreshNames' environment.-  , renameExp-  , renameStm-  , renameBody-  , renameLambda-  , renamePattern-  -- * Renaming annotations-  , RenameM-  , substituteRename-  , renamingStms-  , Rename (..)-  , Renameable+  ( -- * Renaming programs+    renameProg,++    -- * Renaming parts of a program.++    --+    -- These all require execution in a 'MonadFreshNames' environment.+    renameExp,+    renameStm,+    renameBody,+    renameLambda,+    renamePattern,++    -- * Renaming annotations+    RenameM,+    substituteRename,+    renamingStms,+    Rename (..),+    Renameable,   )-  where+where -import Control.Monad.State import Control.Monad.Reader+import Control.Monad.State import qualified Data.Map.Strict as M import Data.Maybe--import Futhark.IR.Syntax-import Futhark.IR.Traversals+import Futhark.FreshNames hiding (newName) import Futhark.IR.Prop.Names import Futhark.IR.Prop.Patterns-import Futhark.FreshNames hiding (newName)-import Futhark.MonadFreshNames (MonadFreshNames(..), modifyNameSource, newName)+import Futhark.IR.Syntax+import Futhark.IR.Traversals+import Futhark.MonadFreshNames (MonadFreshNames (..), modifyNameSource, newName) import Futhark.Transform.Substitute  runRenamer :: RenameM a -> VNameSource -> (a, VNameSource) runRenamer (RenameM m) src = runReader (runStateT m src) env-  where env = RenameEnv M.empty+  where+    env = RenameEnv M.empty  -- | Rename variables such that each is unique.  The semantics of the -- program are unaffected, under the assumption that the program was -- correct to begin with.  In particular, the renaming may make an -- invalid program valid.-renameProg :: (Renameable lore, MonadFreshNames m) =>-              Prog lore -> m (Prog lore)-renameProg prog = modifyNameSource $ runRenamer $-  renamingStms (progConsts prog) $ \consts -> do-  funs <- mapM rename (progFuns prog)-  return prog { progConsts = consts, progFuns = funs }+renameProg ::+  (Renameable lore, MonadFreshNames m) =>+  Prog lore ->+  m (Prog lore)+renameProg prog = modifyNameSource $+  runRenamer $+    renamingStms (progConsts prog) $ \consts -> do+      funs <- mapM rename (progFuns prog)+      return prog {progConsts = consts, progFuns = funs}  -- | Rename bound variables such that each is unique.  The semantics -- of the expression is unaffected, under the assumption that the -- expression was correct to begin with.  Any free variables are left -- untouched.-renameExp :: (Renameable lore, MonadFreshNames m) =>-             Exp lore -> m (Exp lore)+renameExp ::+  (Renameable lore, MonadFreshNames m) =>+  Exp lore ->+  m (Exp lore) renameExp = modifyNameSource . runRenamer . rename  -- | Rename bound variables such that each is unique.  The semantics -- of the binding is unaffected, under the assumption that the -- binding was correct to begin with.  Any free variables are left -- untouched, as are the names in the pattern of the binding.-renameStm :: (Renameable lore, MonadFreshNames m) =>-             Stm lore -> m (Stm lore)+renameStm ::+  (Renameable lore, MonadFreshNames m) =>+  Stm lore ->+  m (Stm lore) renameStm binding = do   e <- renameExp $ stmExp binding-  return binding { stmExp = e }+  return binding {stmExp = e}  -- | Rename bound variables such that each is unique.  The semantics -- of the body is unaffected, under the assumption that the body was -- correct to begin with.  Any free variables are left untouched.-renameBody :: (Renameable lore, MonadFreshNames m) =>-              Body lore -> m (Body lore)+renameBody ::+  (Renameable lore, MonadFreshNames m) =>+  Body lore ->+  m (Body lore) renameBody = modifyNameSource . runRenamer . rename  -- | Rename bound variables such that each is unique.  The semantics -- of the lambda is unaffected, under the assumption that the body was -- correct to begin with.  Any free variables are left untouched. -- Note in particular that the parameters of the lambda are renamed.-renameLambda :: (Renameable lore, MonadFreshNames m) =>-                Lambda lore -> m (Lambda lore)+renameLambda ::+  (Renameable lore, MonadFreshNames m) =>+  Lambda lore ->+  m (Lambda lore) renameLambda = modifyNameSource . runRenamer . rename  -- | Produce an equivalent pattern but with each pattern element given -- a new name.-renamePattern :: (Rename dec, MonadFreshNames m) =>-                 PatternT dec -> m (PatternT dec)+renamePattern ::+  (Rename dec, MonadFreshNames m) =>+  PatternT dec ->+  m (PatternT dec) renamePattern = modifyNameSource . runRenamer . rename'-  where rename' pat = bind (patternNames pat) $ rename pat+  where+    rename' pat = bind (patternNames pat) $ rename pat -newtype RenameEnv = RenameEnv { envNameMap :: M.Map VName VName }+newtype RenameEnv = RenameEnv {envNameMap :: M.Map VName VName}  -- | The monad in which renaming is performed. newtype RenameM a = RenameM (StateT VNameSource (Reader RenameEnv) a)-  deriving (Functor, Applicative, Monad,-            MonadFreshNames, MonadReader RenameEnv)+  deriving+    ( Functor,+      Applicative,+      Monad,+      MonadFreshNames,+      MonadReader RenameEnv+    )  -- | Produce a map of the substitutions that should be performed by -- the renamer.@@ -128,15 +150,15 @@ instance Rename a => Rename [a] where   rename = mapM rename -instance (Rename a, Rename b) => Rename (a,b) where-  rename (a,b) = (,) <$> rename a <*> rename b+instance (Rename a, Rename b) => Rename (a, b) where+  rename (a, b) = (,) <$> rename a <*> rename b -instance (Rename a, Rename b, Rename c) => Rename (a,b,c) where-  rename (a,b,c) = do+instance (Rename a, Rename b, Rename c) => Rename (a, b, c) where+  rename (a, b, c) = do     a' <- rename a     b' <- rename b     c' <- rename c-    return (a',b',c')+    return (a', b', c')  instance Rename a => Rename (Maybe a) where   rename = maybe (return Nothing) (fmap Just . rename)@@ -156,18 +178,24 @@   -- This works because map union prefers elements from left   -- operand.   local (bind' vars') body-  where bind' vars' env = env { envNameMap = M.fromList (zip vars vars')-                                             `M.union` envNameMap env }+  where+    bind' vars' env =+      env+        { envNameMap =+            M.fromList (zip vars vars')+              `M.union` envNameMap env+        }  -- | Rename some statements, then execute an action with the name -- substitutions induced by the statements active. renamingStms :: Renameable lore => Stms lore -> (Stms lore -> RenameM a) -> RenameM a renamingStms stms m = descend mempty stms-  where descend stms' rem_stms = case stmsHead rem_stms of-          Nothing -> m stms'-          Just (stm, rem_stms') -> bind (patternNames $ stmPattern stm) $ do-            stm' <- rename stm-            descend (stms' <> oneStm stm') rem_stms'+  where+    descend stms' rem_stms = case stmsHead rem_stms of+      Nothing -> m stms'+      Just (stm, rem_stms') -> bind (patternNames $ stmPattern stm) $ do+        stm' <- rename stm+        descend (stms' <> oneStm stm') rem_stms'  instance Renameable lore => Rename (FunDef lore) where   rename (FunDef entry attrs fname ret params body) =@@ -178,7 +206,7 @@       return $ FunDef entry attrs fname ret' params' body'  instance Rename SubExp where-  rename (Var v)      = Var <$> rename v+  rename (Var v) = Var <$> rename v   rename (Constant v) = return $ Constant v  instance Rename dec => Rename (Param dec) where@@ -223,40 +251,54 @@         let (loop_params, loop_arrs) = unzip loop_vars         boundexp' <- rename boundexp         loop_arrs' <- rename loop_arrs-        bind (map paramName (ctxparams++valparams) ++-              map paramName loop_params) $ do-          ctxparams' <- mapM rename ctxparams-          valparams' <- mapM rename valparams-          loop_params' <- mapM rename loop_params-          i' <- rename i-          loopbody' <- rename loopbody-          return $ DoLoop-            (zip ctxparams' ctxinit') (zip valparams' valinit')-            (ForLoop i' it boundexp' $-             zip loop_params' loop_arrs') loopbody'+        bind+          ( map paramName (ctxparams ++ valparams)+              ++ map paramName loop_params+          )+          $ do+            ctxparams' <- mapM rename ctxparams+            valparams' <- mapM rename valparams+            loop_params' <- mapM rename loop_params+            i' <- rename i+            loopbody' <- rename loopbody+            return $+              DoLoop+                (zip ctxparams' ctxinit')+                (zip valparams' valinit')+                ( ForLoop i' it boundexp' $+                    zip loop_params' loop_arrs'+                )+                loopbody'       WhileLoop cond ->-        bind (map paramName $ ctxparams++valparams) $ do+        bind (map paramName $ ctxparams ++ valparams) $ do           ctxparams' <- mapM rename ctxparams           valparams' <- mapM rename valparams           loopbody' <- rename loopbody-          cond'     <- rename cond-          return $ DoLoop-            (zip ctxparams' ctxinit') (zip valparams' valinit')-            (WhileLoop cond') loopbody'+          cond' <- rename cond+          return $+            DoLoop+              (zip ctxparams' ctxinit')+              (zip valparams' valinit')+              (WhileLoop cond')+              loopbody'   rename e = mapExpM mapper e-    where mapper = Mapper {-                      mapOnBody = const rename-                    , mapOnSubExp = rename-                    , mapOnVName = rename-                    , mapOnRetType = rename-                    , mapOnBranchType = rename-                    , mapOnFParam = rename-                    , mapOnLParam = rename-                    , mapOnOp = rename-                    }+    where+      mapper =+        Mapper+          { mapOnBody = const rename,+            mapOnSubExp = rename,+            mapOnVName = rename,+            mapOnRetType = rename,+            mapOnBranchType = rename,+            mapOnFParam = rename,+            mapOnLParam = rename,+            mapOnOp = rename+          } -instance Rename shape =>-         Rename (TypeBase shape u) where+instance+  Rename shape =>+  Rename (TypeBase shape u)+  where   rename (Array et size u) = do     size' <- rename size     return $ Array et size' u@@ -282,21 +324,23 @@  instance Rename ExtSize where   rename (Free se) = Free <$> rename se-  rename (Ext x)   = return $ Ext x+  rename (Ext x) = return $ Ext x  instance Rename () where   rename = return  instance Rename d => Rename (DimIndex d) where-  rename (DimFix i)       = DimFix <$> rename i+  rename (DimFix i) = DimFix <$> rename i   rename (DimSlice i n s) = DimSlice <$> rename i <*> rename n <*> rename s  -- | Lores in which all annotations are renameable.-type Renameable lore = (Rename (LetDec lore),-                        Rename (ExpDec lore),-                        Rename (BodyDec lore),-                        Rename (FParamInfo lore),-                        Rename (LParamInfo lore),-                        Rename (RetType lore),-                        Rename (BranchType lore),-                        Rename (Op lore))+type Renameable lore =+  ( Rename (LetDec lore),+    Rename (ExpDec lore),+    Rename (BodyDec lore),+    Rename (FParamInfo lore),+    Rename (LParamInfo lore),+    Rename (RetType lore),+    Rename (BranchType lore),+    Rename (Op lore)+  )
src/Futhark/Transform/Substitute.hs view
@@ -1,26 +1,27 @@-{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE Safe #-}+{-# LANGUAGE UndecidableInstances #-}+ -- | -- -- This module contains facilities for replacing variable names in -- syntactic constructs. module Futhark.Transform.Substitute-  (Substitutions,-   Substitute(..),-   Substitutable)-  where+  ( Substitutions,+    Substitute (..),+    Substitutable,+  )+where  import Control.Monad.Identity import qualified Data.Map.Strict as M--import Futhark.IR.Syntax-import Futhark.IR.Traversals-import Futhark.IR.Prop.Scope import Futhark.Analysis.PrimExp import Futhark.IR.Prop.Names+import Futhark.IR.Prop.Scope+import Futhark.IR.Syntax+import Futhark.IR.Traversals  -- | The substitutions to be made are given by a mapping from names to -- names.@@ -40,22 +41,24 @@ instance Substitute (Stm lore) => Substitute (Stms lore) where   substituteNames substs = fmap $ substituteNames substs -instance (Substitute a, Substitute b) => Substitute (a,b) where-  substituteNames substs (x,y) =+instance (Substitute a, Substitute b) => Substitute (a, b) where+  substituteNames substs (x, y) =     (substituteNames substs x, substituteNames substs y) -instance (Substitute a, Substitute b, Substitute c) => Substitute (a,b,c) where-  substituteNames substs (x,y,z) =-    (substituteNames substs x,-     substituteNames substs y,-     substituteNames substs z)+instance (Substitute a, Substitute b, Substitute c) => Substitute (a, b, c) where+  substituteNames substs (x, y, z) =+    ( substituteNames substs x,+      substituteNames substs y,+      substituteNames substs z+    ) -instance (Substitute a, Substitute b, Substitute c, Substitute d) => Substitute (a,b,c,d) where-  substituteNames substs (x,y,z,u) =-    (substituteNames substs x,-     substituteNames substs y,-     substituteNames substs z,-     substituteNames substs u)+instance (Substitute a, Substitute b, Substitute c, Substitute d) => Substitute (a, b, c, d) where+  substituteNames substs (x, y, z, u) =+    ( substituteNames substs x,+      substituteNames substs y,+      substituteNames substs z,+      substituteNames substs u+    )  instance Substitute a => Substitute (Maybe a) where   substituteNames substs = fmap $ substituteNames substs@@ -83,15 +86,15 @@ instance Substitute dec => Substitute (StmAux dec) where   substituteNames substs (StmAux cs attrs dec) =     StmAux-    (substituteNames substs cs)-    (substituteNames substs attrs)-    (substituteNames substs dec)+      (substituteNames substs cs)+      (substituteNames substs attrs)+      (substituteNames substs dec)  instance Substitute dec => Substitute (Param dec) where   substituteNames substs (Param name dec) =     Param-    (substituteNames substs name)-    (substituteNames substs dec)+      (substituteNames substs name)+      (substituteNames substs dec)  instance Substitute dec => Substitute (PatternT dec) where   substituteNames substs (Pattern context values) =@@ -104,28 +107,29 @@ instance Substitutable lore => Substitute (Stm lore) where   substituteNames substs (Let pat annot e) =     Let-    (substituteNames substs pat)-    (substituteNames substs annot)-    (substituteNames substs e)+      (substituteNames substs pat)+      (substituteNames substs annot)+      (substituteNames substs e)  instance Substitutable lore => Substitute (Body lore) where   substituteNames substs (Body dec stms res) =     Body-    (substituteNames substs dec)-    (substituteNames substs stms)-    (substituteNames substs res)+      (substituteNames substs dec)+      (substituteNames substs stms)+      (substituteNames substs res)  replace :: Substitutable lore => M.Map VName VName -> Mapper lore lore Identity-replace substs = Mapper {-                   mapOnVName = return . substituteNames substs-                 , mapOnSubExp = return . substituteNames substs-                 , mapOnBody = const $ return . substituteNames substs-                 , mapOnRetType = return . substituteNames substs-                 , mapOnBranchType = return . substituteNames substs-                 , mapOnFParam = return . substituteNames substs-                 , mapOnLParam = return . substituteNames substs-                 , mapOnOp = return . substituteNames substs-                 }+replace substs =+  Mapper+    { mapOnVName = return . substituteNames substs,+      mapOnSubExp = return . substituteNames substs,+      mapOnBody = const $ return . substituteNames substs,+      mapOnRetType = return . substituteNames substs,+      mapOnBranchType = return . substituteNames substs,+      mapOnFParam = return . substituteNames substs,+      mapOnLParam = return . substituteNames substs,+      mapOnOp = return . substituteNames substs+    }  instance Substitute Rank where   substituteNames _ = id@@ -139,7 +143,7 @@  instance Substitute d => Substitute (Ext d) where   substituteNames substs (Free x) = Free $ substituteNames substs x-  substituteNames _      (Ext x)  = Ext x+  substituteNames _ (Ext x) = Ext x  instance Substitute Names where   substituteNames = mapNames . substituteNames@@ -154,14 +158,15 @@ instance Substitutable lore => Substitute (Lambda lore) where   substituteNames substs (Lambda params body rettype) =     Lambda-    (substituteNames substs params)-    (substituteNames substs body)-    (map (substituteNames substs) rettype)+      (substituteNames substs params)+      (substituteNames substs body)+      (map (substituteNames substs) rettype)  instance Substitute Ident where   substituteNames substs v =-    v { identName = substituteNames substs $ identName v-      , identType = substituteNames substs $ identType v+    v+      { identName = substituteNames substs $ identName v,+        identType = substituteNames substs $ identType v       }  instance Substitute d => Substitute (DimChange d) where@@ -173,6 +178,10 @@ instance Substitute v => Substitute (PrimExp v) where   substituteNames substs = fmap $ substituteNames substs +instance Substitute v => Substitute (TPrimExp t v) where+  substituteNames substs =+    TPrimExp . fmap (substituteNames substs) . untyped+ instance Substitutable lore => Substitute (NameInfo lore) where   substituteNames subst (LetName dec) =     LetName $ substituteNames subst dec@@ -188,12 +197,14 @@  -- | Lores in which all annotations support name -- substitution.-type Substitutable lore = (Decorations lore,-                           Substitute (ExpDec lore),-                           Substitute (BodyDec lore),-                           Substitute (LetDec lore),-                           Substitute (FParamInfo lore),-                           Substitute (LParamInfo lore),-                           Substitute (RetType lore),-                           Substitute (BranchType lore),-                           Substitute (Op lore))+type Substitutable lore =+  ( Decorations lore,+    Substitute (ExpDec lore),+    Substitute (BodyDec lore),+    Substitute (LetDec lore),+    Substitute (FParamInfo lore),+    Substitute (LParamInfo lore),+    Substitute (RetType lore),+    Substitute (BranchType lore),+    Substitute (Op lore)+  )
src/Futhark/TypeCheck.hs view
@@ -1,1142 +1,1389 @@-{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, TypeFamilies, ScopedTypeVariables #-}-{-# LANGUAGE DefaultSignatures #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE Strict #-}-{-# LANGUAGE Trustworthy #-}--- | The type checker checks whether the program is type-consistent.-module Futhark.TypeCheck-  ( -- * Interface-    checkProg-  , TypeError (..)-  , ErrorCase (..)--    -- * Extensionality-  , TypeM-  , bad-  , context-  , message-  , Checkable (..)-  , CheckableOp (..)-  , lookupVar-  , lookupAliases-  , checkOpWith--    -- * Checkers-  , require-  , requireI-  , requirePrimExp-  , checkSubExp-  , checkExp-  , checkStms-  , checkStm-  , checkType-  , checkExtType-  , matchExtPattern-  , matchExtBranchType-  , argType-  , argAliases-  , noArgAliases-  , checkArg-  , checkSOACArrayArgs-  , checkLambda-  , checkBody-  , consume-  , consumeOnlyParams-  , binding-  )-  where--import Control.Parallel.Strategies-import Control.Monad.Reader-import Control.Monad.Writer-import Control.Monad.State-import Control.Monad.RWS.Strict-import Data.List (find, intercalate, sort)-import qualified Data.Map.Strict as M-import qualified Data.Set as S-import Data.Maybe--import Futhark.Analysis.PrimExp-import Futhark.Construct (instantiateShapes)-import Futhark.IR.Aliases-import Futhark.Util-import Futhark.Util.Pretty (Pretty, prettyDoc, indent, ppr, text, (<+>), align)---- | Information about an error during type checking.  The 'Show'--- instance for this type produces a human-readable description.-data ErrorCase lore =-    TypeError String-  | UnexpectedType (Exp lore) Type [Type]-  | ReturnTypeError Name [ExtType] [ExtType]-  | DupDefinitionError Name-  | DupParamError Name VName-  | DupPatternError VName-  | InvalidPatternError (Pattern (Aliases lore)) [ExtType] (Maybe String)-  | UnknownVariableError VName-  | UnknownFunctionError Name-  | ParameterMismatch (Maybe Name) [Type] [Type]-  | SlicingError Int Int-  | BadAnnotation String Type Type-  | ReturnAliased Name VName-  | UniqueReturnAliased Name-  | NotAnArray VName Type-  | PermutationError [Int] Int (Maybe VName)--instance Checkable lore => Show (ErrorCase lore) where-  show (TypeError msg) =-    "Type error:\n" ++ msg-  show (UnexpectedType e _ []) =-    "Type of expression\n" ++-    prettyDoc 160 (indent 2 $ ppr e) ++-    "\ncannot have any type - possibly a bug in the type checker."-  show (UnexpectedType e t ts) =-    "Type of expression\n" ++-    prettyDoc 160 (indent 2 $ ppr e) ++-    "\nmust be one of " ++ intercalate ", " (map pretty ts) ++ ", but is " ++-    pretty t ++ "."-  show (ReturnTypeError fname rettype bodytype) =-    "Declaration of function " ++ nameToString fname ++-    " declares return type\n  " ++ prettyTuple rettype ++-    "\nBut body has type\n  " ++ prettyTuple bodytype-  show (DupDefinitionError name) =-    "Duplicate definition of function " ++ nameToString name ++ ""-  show (DupParamError funname paramname) =-    "Parameter " ++ pretty paramname ++-    " mentioned multiple times in argument list of function " ++-    nameToString funname ++ "."-  show (DupPatternError name) =-    "Variable " ++ pretty name ++ " bound twice in pattern."-  show (InvalidPatternError pat t desc) =-    "Pattern " ++ pretty pat ++-    " cannot match value of type " ++ prettyTuple t ++ end-    where end = case desc of Nothing -> "."-                             Just desc' -> ":\n" ++ desc'-  show (UnknownVariableError name) =-    "Use of unknown variable " ++ pretty name ++ "."-  show (UnknownFunctionError fname) =-    "Call of unknown function " ++ nameToString fname ++ "."-  show (ParameterMismatch fname expected got) =-    "In call of " ++ fname' ++ ":\n" ++-    "expecting " ++ show nexpected ++ " arguments of type(s)\n" ++-     intercalate ", " (map pretty expected) ++-     "\nGot " ++ show ngot ++-    " arguments of types\n" ++-    intercalate ", " (map pretty got)-    where nexpected = length expected-          ngot = length got-          fname' = maybe "anonymous function" (("function "++) . nameToString) fname-  show (SlicingError dims got) =-    show got ++ " indices given, but type of indexee has " ++ show dims ++ " dimension(s)."-  show (BadAnnotation desc expected got) =-    "Annotation of \"" ++ desc ++ "\" type of expression is " ++ pretty expected ++-    ", but derived to be " ++ pretty got ++ "."-  show (ReturnAliased fname name) =-    "Unique return value of function " ++ nameToString fname ++-    " is aliased to " ++ pretty name ++ ", which is not consumed."-  show (UniqueReturnAliased fname) =-    "A unique tuple element of return value of function " ++-    nameToString fname ++ " is aliased to some other tuple component."-  show (NotAnArray e t) =-    "The expression " ++ pretty e ++-    " is expected to be an array, but is " ++ pretty t ++ "."-  show (PermutationError perm rank name) =-    "The permutation (" ++ intercalate ", " (map show perm) ++-    ") is not valid for array " ++ name' ++ "of rank " ++ show rank ++ "."-    where name' = maybe "" ((++" ") . pretty) name---- | A type error.-data TypeError lore = Error [String] (ErrorCase lore)--instance Checkable lore => Show (TypeError lore) where-  show (Error [] err) =-    show err-  show (Error msgs err) =-    intercalate "\n" msgs ++ "\n" ++ show err---- | A tuple of a return type and a list of parameters, possibly--- named.-type FunBinding lore = ([RetType (Aliases lore)], [FParam (Aliases lore)])--type VarBinding lore = NameInfo (Aliases lore)--data Usage = Consumed-           | Observed-             deriving (Eq, Ord, Show)--data Occurence = Occurence { observed :: Names-                           , consumed :: Names-                           }-             deriving (Eq, Show)--observation :: Names -> Occurence-observation = flip Occurence mempty--consumption :: Names -> Occurence-consumption = Occurence mempty--nullOccurence :: Occurence -> Bool-nullOccurence occ = observed occ == mempty && consumed occ == mempty--type Occurences = [Occurence]--allConsumed :: Occurences -> Names-allConsumed = mconcat . map consumed--seqOccurences :: Occurences -> Occurences -> Occurences-seqOccurences occurs1 occurs2 =-  filter (not . nullOccurence) (map filt occurs1) ++ occurs2-  where filt occ =-          occ { observed = observed occ `namesSubtract` postcons }-        postcons = allConsumed occurs2--altOccurences :: Occurences -> Occurences -> Occurences-altOccurences occurs1 occurs2 =-  filter (not . nullOccurence) (map filt occurs1) ++ occurs2-  where filt occ =-          occ { consumed = consumed occ `namesSubtract` postcons-              , observed = observed occ `namesSubtract` postcons }-        postcons = allConsumed occurs2--unOccur :: Names -> Occurences -> Occurences-unOccur to_be_removed = filter (not . nullOccurence) . map unOccur'-  where unOccur' occ =-          occ { observed = observed occ `namesSubtract` to_be_removed-              , consumed = consumed occ `namesSubtract` to_be_removed-              }---- | The 'Consumption' data structure is used to keep track of which--- variables have been consumed, as well as whether a violation has been detected.-data Consumption = ConsumptionError String-                 | Consumption Occurences-                 deriving (Show)--instance Semigroup Consumption where-  ConsumptionError e <> _ = ConsumptionError e-  _ <> ConsumptionError e = ConsumptionError e-  Consumption o1 <> Consumption o2-    | v:_ <- namesToList $ consumed_in_o1 `namesIntersection` used_in_o2 =-        ConsumptionError $ "Variable " <> pretty v <> " referenced after being consumed."-    | otherwise =-        Consumption $ o1 `seqOccurences` o2-    where consumed_in_o1 = mconcat $ map consumed o1-          used_in_o2 = mconcat $ map consumed o2 <> map observed o2--instance Monoid Consumption where-  mempty = Consumption mempty---- | The environment contains a variable table and a function table.--- Type checking happens with access to this environment.  The--- function table is only initialised at the very beginning, but the--- variable table will be extended during type-checking when--- let-expressions are encountered.-data Env lore =-  Env { envVtable :: M.Map VName (VarBinding lore)-      , envFtable :: M.Map Name (FunBinding lore)-      , envCheckOp :: OpWithAliases (Op lore) -> TypeM lore ()-      , envContext :: [String]-      }---- | The type checker runs in this monad.-newtype TypeM lore a = TypeM (RWST-                              (Env lore)  -- Reader-                              Consumption -- Writer-                              Names       -- State-                              (Either (TypeError lore)) -- Inner monad-                              a)-  deriving (Monad, Functor, Applicative,-            MonadReader (Env lore),-            MonadWriter Consumption,-            MonadState Names)--instance Checkable lore =>-         HasScope (Aliases lore) (TypeM lore) where-  lookupType = fmap typeOf . lookupVar-  askScope = asks $ M.fromList . mapMaybe varType . M.toList . envVtable-    where varType (name, dec) = Just (name, dec)--runTypeM :: Env lore -> TypeM lore a-         -> Either (TypeError lore) (a, Consumption)-runTypeM env (TypeM m) = evalRWST m env mempty--bad :: ErrorCase lore -> TypeM lore a-bad e = do-  messages <- asks envContext-  TypeM $ lift $ Left $ Error (reverse messages) e---- | Add information about what is being type-checked to the current--- context.  Liberal use of this combinator makes it easier to track--- type errors, as the strings are added to type errors signalled via--- 'bad'.-context :: String-        -> TypeM lore a-        -> TypeM lore a-context s = local $ \env -> env { envContext = s : envContext env}--message :: Pretty a =>-           String -> a -> String-message s x = prettyDoc 80 $-              text s <+> align (ppr x)---- | Mark a name as bound.  If the name has been bound previously in--- the program, report a type error.-bound :: VName -> TypeM lore ()-bound name = do already_seen <- gets $ nameIn name-                when already_seen $-                  bad $ TypeError $ "Name " ++ pretty name ++ " bound twice"-                modify (<>oneName name)--occur :: Occurences -> TypeM lore ()-occur = tell . Consumption . filter (not . nullOccurence)---- | Proclaim that we have made read-only use of the given variable.--- No-op unless the variable is array-typed.-observe :: Checkable lore =>-           VName -> TypeM lore ()-observe name = do-  dec <- lookupVar name-  unless (primType $ typeOf dec) $-    occur [observation $ oneName name <> aliases dec]---- | Proclaim that we have written to the given variables.-consume :: Checkable lore => Names -> TypeM lore ()-consume als = do-  scope <- askScope-  let isArray = maybe False ((>0) . arrayRank . typeOf) . (`M.lookup` scope)-  occur [consumption $ namesFromList $ filter isArray $ namesToList als]--collectOccurences :: TypeM lore a -> TypeM lore (a, Occurences)-collectOccurences m = pass $ do-  (x, c) <- listen m-  o <- checkConsumption c-  return ((x, o), const mempty)--checkOpWith :: (OpWithAliases (Op lore) -> TypeM lore ())-            -> TypeM lore a -> TypeM lore a-checkOpWith checker = local $ \env -> env { envCheckOp = checker }--checkConsumption :: Consumption -> TypeM lore Occurences-checkConsumption (ConsumptionError e) = bad $ TypeError e-checkConsumption (Consumption os)     = return os--alternative :: TypeM lore a -> TypeM lore b -> TypeM lore (a,b)-alternative m1 m2 = pass $ do-  (x, c1) <- listen m1-  (y, c2) <- listen m2-  os1 <- checkConsumption c1-  os2 <- checkConsumption c2-  let usage = Consumption $ os1 `altOccurences` os2-  return ((x, y), const usage)---- | Permit consumption of only the specified names.  If one of these--- names is consumed, the consumption will be rewritten to be a--- consumption of the corresponding alias set.  Consumption of--- anything else will result in a type error.-consumeOnlyParams :: [(VName, Names)] -> TypeM lore a -> TypeM lore a-consumeOnlyParams consumable m = do-  (x, os) <- collectOccurences m-  tell . Consumption =<< mapM inspect os-  return x-  where inspect o = do-          new_consumed <- mconcat <$> mapM wasConsumed (namesToList $ consumed o)-          return o { consumed = new_consumed }-        wasConsumed v-          | Just als <- lookup v consumable = return als-          | otherwise =-            bad $ TypeError $-            unlines [pretty v ++ " was invalidly consumed.",-                     what ++ " can be consumed here."]-        what | null consumable = "Nothing"-             | otherwise = "Only " ++ intercalate ", " (map (pretty . fst) consumable)---- | Given the immediate aliases, compute the full transitive alias--- set (including the immediate aliases).-expandAliases :: Names -> Env lore -> Names-expandAliases names env = names <> aliasesOfAliases-  where aliasesOfAliases =  mconcat . map look . namesToList $ names-        look k = case M.lookup k $ envVtable env of-          Just (LetName (als, _)) -> unAliases als-          _                       -> mempty--binding :: Checkable lore =>-           Scope (Aliases lore)-        -> TypeM lore a-        -> TypeM lore a-binding bnds = check . local (`bindVars` bnds)-  where bindVars = M.foldlWithKey' bindVar-        boundnames = M.keys bnds--        bindVar env name (LetName (AliasDec als, dec)) =-          let als' | primType (typeOf dec) = mempty-                   | otherwise = expandAliases als env-          in env { envVtable =-                     M.insert name (LetName (AliasDec als', dec)) $ envVtable env-                 }-        bindVar env name dec =-          env { envVtable = M.insert name dec $ envVtable env }--        -- Check whether the bound variables have been used correctly-        -- within their scope.-        check m = do-          mapM_ bound $ M.keys bnds-          (a, os) <- collectOccurences m-          tell $ Consumption $ unOccur (namesFromList boundnames) os-          return a--lookupVar :: VName -> TypeM lore (NameInfo (Aliases lore))-lookupVar name = do-  bnd <- asks $ M.lookup name . envVtable-  case bnd of-    Nothing -> bad $ UnknownVariableError name-    Just dec -> return dec--lookupAliases :: Checkable lore => VName -> TypeM lore Names-lookupAliases name = do-  info <- lookupVar name-  return $ if primType $ typeOf info-           then mempty-           else oneName name <> aliases info--aliases :: NameInfo (Aliases lore) -> Names-aliases (LetName (als, _)) = unAliases als-aliases _ = mempty--subExpAliasesM :: Checkable lore => SubExp -> TypeM lore Names-subExpAliasesM Constant{} = return mempty-subExpAliasesM (Var v)    = lookupAliases v--lookupFun :: Checkable lore =>-             Name-          -> [SubExp]-          -> TypeM lore ([RetType lore], [DeclType])-lookupFun fname args = do-  bnd <- asks $ M.lookup fname . envFtable-  case bnd of-    Nothing -> bad $ UnknownFunctionError fname-    Just (ftype, params) -> do-      argts <- mapM subExpType args-      case applyRetType ftype params $ zip args argts of-        Nothing ->-          bad $ ParameterMismatch (Just fname) (map paramType params) argts-        Just rt ->-          return (rt, map paramDeclType params)---- | @checkAnnotation loc s t1 t2@ checks if @t2@ is equal to--- @t1@.  If not, a 'BadAnnotation' is raised.-checkAnnotation :: String -> Type -> Type-                -> TypeM lore ()-checkAnnotation desc t1 t2-  | t2 == t1 = return ()-  | otherwise = bad $ BadAnnotation desc t1 t2---- | @require ts se@ causes a '(TypeError vn)' if the type of @se@ is--- not a subtype of one of the types in @ts@.-require :: Checkable lore => [Type] -> SubExp -> TypeM lore ()-require ts se = do-  t <- checkSubExp se-  unless (t `elem` ts) $-    bad $ UnexpectedType (BasicOp $ SubExp se) t ts---- | Variant of 'require' working on variable names.-requireI :: Checkable lore => [Type] -> VName -> TypeM lore ()-requireI ts ident = require ts $ Var ident--checkArrIdent :: Checkable lore =>-                 VName -> TypeM lore Type-checkArrIdent v = do-  t <- lookupType v-  case t of-    Array{} -> return t-    _       -> bad $ NotAnArray v t---- | Type check a program containing arbitrary type information,--- yielding either a type error or a program with complete type--- information.-checkProg :: Checkable lore =>-             Prog (Aliases lore) -> Either (TypeError lore) ()-checkProg (Prog consts funs) = do-  let typeenv = Env { envVtable = M.empty-                    , envFtable = mempty-                    , envContext = []-                    , envCheckOp = checkOp-                    }-  let onFunction ftable vtable fun =-        fmap fst $ runTypeM typeenv $-        local (\env -> env { envFtable = ftable, envVtable = vtable }) $-        checkFun fun-  (ftable, _) <- runTypeM typeenv buildFtable-  (vtable, _) <- runTypeM typeenv { envFtable = ftable } $-                 checkStms consts $ asks envVtable-  sequence_ $ parMap rpar (onFunction ftable vtable) funs-  where-    buildFtable = do table <- initialFtable-                     foldM expand table funs-    expand ftable (FunDef _ _ name ret params _)-      | M.member name ftable =-          bad $ DupDefinitionError name-      | otherwise =-          return $ M.insert name (ret,params) ftable--initialFtable :: Checkable lore =>-                 TypeM lore (M.Map Name (FunBinding lore))-initialFtable = fmap M.fromList $ mapM addBuiltin $ M.toList builtInFunctions-  where addBuiltin (fname, (t, ts)) = do-          ps <- mapM (primFParam name) ts-          return (fname, ([primRetType t], ps))-        name = VName (nameFromString "x") 0--checkFun :: Checkable lore =>-            FunDef (Aliases lore) -> TypeM lore ()-checkFun (FunDef _ _ fname rettype params body) =-  context ("In function " ++ nameToString fname) $-    checkFun' (fname,-               map declExtTypeOf rettype,-               funParamsToNameInfos params) consumable $ do-      checkFunParams params-      checkRetType rettype-      context "When checking function body" $ checkFunBody rettype body-        where consumable = [ (paramName param, mempty)-                           | param <- params-                           , unique $ paramDeclType param-                           ]--funParamsToNameInfos :: [FParam lore]-                     -> [(VName, NameInfo (Aliases lore))]-funParamsToNameInfos = map nameTypeAndLore-  where nameTypeAndLore fparam = (paramName fparam,-                                  FParamName $ paramDec fparam)--checkFunParams :: Checkable lore =>-                  [FParam lore] -> TypeM lore ()-checkFunParams = mapM_ $ \param ->-  context ("In function parameter " ++ pretty param) $-    checkFParamLore (paramName param) (paramDec param)--checkLambdaParams :: Checkable lore =>-                     [LParam lore] -> TypeM lore ()-checkLambdaParams = mapM_ $ \param ->-  context ("In lambda parameter " ++ pretty param) $-    checkLParamLore (paramName param) (paramDec param)--checkFun' :: Checkable lore =>-             (Name,-              [DeclExtType],-              [(VName, NameInfo (Aliases lore))])-          -> [(VName, Names)]-          -> TypeM lore [Names]-          -> TypeM lore ()-checkFun' (fname, rettype, params) consumable check = do-  checkNoDuplicateParams-  binding (M.fromList params) $-    consumeOnlyParams consumable $ do-      body_aliases <- check-      scope <- askScope-      let isArray = maybe False ((>0) . arrayRank . typeOf) . (`M.lookup` scope)-      context ("When checking the body aliases: " ++-               pretty (map namesToList body_aliases)) $-        checkReturnAlias $ map (namesFromList . filter isArray . namesToList) body_aliases-  where param_names = map fst params--        checkNoDuplicateParams = foldM_ expand [] param_names--        expand seen pname-          | Just _ <- find (==pname) seen =-            bad $ DupParamError fname pname-          | otherwise =-            return $ pname : seen--        -- | Check that unique return values do not alias a-        -- non-consumed parameter.-        checkReturnAlias =-          foldM_ checkReturnAlias' mempty . returnAliasing rettype--        checkReturnAlias' seen (Unique, names)-          | any (`S.member` S.map fst seen) $ namesToList names =-            bad $ UniqueReturnAliased fname-          | otherwise = do-            consume names-            return $ seen <> tag Unique names-        checkReturnAlias' seen (Nonunique, names)-          | any (`S.member` seen) $ tag Unique names =-            bad $ UniqueReturnAliased fname-          | otherwise = return $ seen <> tag Nonunique names--        tag u = S.fromList . map (,u) . namesToList--        returnAliasing expected got =-          reverse $-          zip (reverse (map uniqueness expected) ++ repeat Nonunique) $-          reverse got--checkSubExp :: Checkable lore => SubExp -> TypeM lore Type-checkSubExp (Constant val) =-  return $ Prim $ primValueType val-checkSubExp (Var ident) = context ("In subexp " ++ pretty ident) $ do-  observe ident-  lookupType ident--checkStms :: Checkable lore =>-             Stms (Aliases lore) -> TypeM lore a-          -> TypeM lore a-checkStms origbnds m = delve $ stmsToList origbnds-  where delve (stm@(Let pat _ e):bnds) = do-          context ("In expression of statement " ++ pretty pat) $-            checkExp e-          checkStm stm $-            delve bnds-        delve [] =-          m--checkResult :: Checkable lore =>-               Result -> TypeM lore ()-checkResult = mapM_ checkSubExp--checkFunBody :: Checkable lore =>-                [RetType lore]-             -> Body (Aliases lore)-             -> TypeM lore [Names]-checkFunBody rt (Body (_,lore) bnds res) = do-  checkBodyLore lore-  checkStms bnds $ do-    context "When checking body result" $ checkResult res-    context "When matching declared return type to result of body" $-      matchReturnType rt res-    map (`namesSubtract` bound_here) <$> mapM subExpAliasesM res-  where bound_here = namesFromList $ M.keys $ scopeOf bnds--checkLambdaBody :: Checkable lore =>-                   [Type] -> Body (Aliases lore) -> TypeM lore [Names]-checkLambdaBody ret (Body (_,lore) bnds res) = do-  checkBodyLore lore-  checkStms bnds $ do-    checkLambdaResult ret res-    map (`namesSubtract` bound_here) <$> mapM subExpAliasesM res-  where bound_here = namesFromList $ M.keys $ scopeOf bnds--checkLambdaResult :: Checkable lore =>-                     [Type] -> Result -> TypeM lore ()-checkLambdaResult ts es-  | length ts /= length es =-    bad $ TypeError $-    "Lambda has return type " ++ prettyTuple ts ++-    " describing " ++ show (length ts) ++ " values, but body returns " ++-    show (length es) ++ " values: " ++ prettyTuple es-  | otherwise = forM_ (zip ts es) $ \(t, e) -> do-      et <- checkSubExp e-      unless (et == t) $-        bad $ TypeError $-        "Subexpression " ++ pretty e ++ " has type " ++ pretty et ++-        " but expected " ++ pretty t--checkBody :: Checkable lore =>-             Body (Aliases lore) -> TypeM lore [Names]-checkBody (Body (_,lore) bnds res) = do-  checkBodyLore lore-  checkStms bnds $ do-    checkResult res-    map (`namesSubtract` bound_here) <$> mapM subExpAliasesM res-  where bound_here = namesFromList $ M.keys $ scopeOf bnds--checkBasicOp :: Checkable lore => BasicOp -> TypeM lore ()--checkBasicOp (SubExp es) =-  void $ checkSubExp es--checkBasicOp (Opaque es) =-  void $ checkSubExp es--checkBasicOp (ArrayLit [] _) =-  return ()--checkBasicOp (ArrayLit (e:es') t) = do-  let check elemt eleme = do-        elemet <- checkSubExp eleme-        unless (elemet == elemt) $-          bad $ TypeError $ pretty elemet ++-          " is not of expected type " ++ pretty elemt ++ "."-  et <- checkSubExp e--  -- Compare that type with the one given for the array literal.-  checkAnnotation "array-element" t et--  mapM_ (check et) es'--checkBasicOp (UnOp op e) = require [Prim $ unOpType op] e--checkBasicOp (BinOp op e1 e2) = checkBinOpArgs (binOpType op) e1 e2--checkBasicOp (CmpOp op e1 e2) = checkCmpOp op e1 e2--checkBasicOp (ConvOp op e) = require [Prim $ fst $ convOpType op] e--checkBasicOp (Index ident idxes) = do-  vt <- lookupType ident-  observe ident-  when (arrayRank vt /= length idxes) $-    bad $ SlicingError (arrayRank vt) (length idxes)-  mapM_ checkDimIndex idxes--checkBasicOp (Update src idxes se) = do-  src_t <- checkArrIdent src-  when (arrayRank src_t /= length idxes) $-    bad $ SlicingError (arrayRank src_t) (length idxes)--  se_aliases <- subExpAliasesM se-  when (src `nameIn` se_aliases) $-    bad $ TypeError "The target of an Update must not alias the value to be written."--  mapM_ checkDimIndex idxes-  require [Prim (elemType src_t) `arrayOfShape` Shape (sliceDims idxes)] se-  consume =<< lookupAliases src--checkBasicOp (Iota e x s et) = do-  require [Prim int32] e-  require [Prim $ IntType et] x-  require [Prim $ IntType et] s--checkBasicOp (Replicate (Shape dims) valexp) = do-  mapM_ (require [Prim int32]) dims-  void $ checkSubExp valexp--checkBasicOp (Scratch _ shape) =-  mapM_ checkSubExp shape--checkBasicOp (Reshape newshape arrexp) = do-  rank <- arrayRank <$> checkArrIdent arrexp-  mapM_ (require [Prim int32] . newDim) newshape-  zipWithM_ (checkDimChange rank) newshape [0..]-  where checkDimChange _ (DimNew _) _ =-          return ()-        checkDimChange rank (DimCoercion se) i-          | i >= rank =-            bad $ TypeError $-            "Asked to coerce dimension " ++ show i ++ " to " ++ pretty se ++-            ", but array " ++ pretty arrexp ++ " has only " ++ pretty rank ++ " dimensions"-          | otherwise =-            return ()--checkBasicOp (Rearrange perm arr) = do-  arrt <- lookupType arr-  let rank = arrayRank arrt-  when (length perm /= rank || sort perm /= [0..rank-1]) $-    bad $ PermutationError perm rank $ Just arr--checkBasicOp (Rotate rots arr) = do-  arrt <- lookupType arr-  let rank = arrayRank arrt-  mapM_ (require [Prim int32]) rots-  when (length rots /= rank) $-    bad $ TypeError $ "Cannot rotate " ++ show (length rots) ++-    " dimensions of " ++ show rank ++ "-dimensional array."--checkBasicOp (Concat i arr1exp arr2exps ressize) = do-  arr1t  <- checkArrIdent arr1exp-  arr2ts <- mapM checkArrIdent arr2exps-  let success = all ((== dropAt i 1 (arrayDims arr1t)).-                     dropAt i 1 . arrayDims) arr2ts-  unless success $-    bad $ TypeError $-    "Types of arguments to concat do not match.  Got " ++-    pretty arr1t ++ " and " ++ intercalate ", " (map pretty arr2ts)-  require [Prim int32] ressize--checkBasicOp (Copy e) =-  void $ checkArrIdent e--checkBasicOp (Manifest perm arr) =-  checkBasicOp $ Rearrange perm arr -- Basically same thing!--checkBasicOp (Assert e _ _) =-  require [Prim Bool] e--matchLoopResultExt :: Checkable lore =>-                      [Param DeclType] -> [Param DeclType]-                   -> [SubExp] -> TypeM lore ()-matchLoopResultExt ctx val loopres = do-  let rettype_ext =-        existentialiseExtTypes (map paramName ctx) $-        staticShapes $ map typeOf $ ctx ++ val--  bodyt <- mapM subExpType loopres--  case instantiateShapes (`maybeNth` loopres) rettype_ext of-    Nothing -> bad $ ReturnTypeError (nameFromString "<loop body>")-               rettype_ext (staticShapes bodyt)-    Just rettype' ->-      unless (bodyt `subtypesOf` rettype') $-      bad $ ReturnTypeError (nameFromString "<loop body>")-      (staticShapes rettype') (staticShapes bodyt)--checkExp :: Checkable lore =>-            Exp (Aliases lore) -> TypeM lore ()--checkExp (BasicOp op) = checkBasicOp op--checkExp (If e1 e2 e3 info) = do-  require [Prim Bool] e1-  _ <- checkBody e2 `alternative` checkBody e3-  context "in true branch" $ matchBranchType (ifReturns info) e2-  context "in false branch" $ matchBranchType (ifReturns info) e3--checkExp (Apply fname args rettype_annot _) = do-  (rettype_derived, paramtypes) <- lookupFun fname $ map fst args-  argflows <- mapM (checkArg . fst) args-  when (rettype_derived /= rettype_annot) $-    bad $ TypeError $ "Expected apply result type " ++ pretty rettype_derived-    ++ " but annotation is " ++ pretty rettype_annot-  checkFuncall (Just fname) paramtypes argflows--checkExp (DoLoop ctxmerge valmerge form loopbody) = do-  let merge = ctxmerge ++ valmerge-      (mergepat, mergeexps) = unzip merge-  mergeargs <- mapM checkArg mergeexps--  let val_free = freeIn $ map fst valmerge-      usedInVal p = paramName p `nameIn` val_free-  case find (not . usedInVal . fst) ctxmerge of-    Just p ->-      bad $ TypeError $ "Loop context parameter " ++ pretty p ++ " unused."-    Nothing ->-      return ()--  binding (scopeOf form) $ do-    case form of-      ForLoop loopvar it boundexp loopvars -> do-        iparam <- primFParam loopvar $ IntType it-        let funparams = iparam : mergepat-            paramts   = map paramDeclType funparams--        forM_ loopvars $ \(p,a) -> do-          a_t <- lookupType a-          observe a-          case peelArray 1 a_t of-            Just a_t_r -> do-              checkLParamLore (paramName p) $ paramDec p-              unless (a_t_r `subtypeOf` typeOf (paramDec p)) $-                 bad $ TypeError $ "Loop parameter " ++ pretty p ++-                 " not valid for element of " ++ pretty a ++ ", which has row type " ++ pretty a_t_r-            _ -> bad $ TypeError $ "Cannot loop over " ++ pretty a ++-                 " of type " ++ pretty a_t--        boundarg <- checkArg boundexp-        checkFuncall Nothing paramts $ boundarg : mergeargs--      WhileLoop cond -> do-        case find ((==cond) . paramName . fst) merge of-          Just (condparam,_) ->-            unless (paramType condparam == Prim Bool) $-            bad $ TypeError $-            "Conditional '" ++ pretty cond ++ "' of while-loop is not boolean, but " ++-            pretty (paramType condparam) ++ "."-          Nothing ->-            bad $ TypeError $-            "Conditional '" ++ pretty cond ++ "' of while-loop is not a merge variable."-        let funparams = mergepat-            paramts   = map paramDeclType funparams-        checkFuncall Nothing paramts mergeargs--    let rettype = map paramDeclType mergepat-        consumable = [ (paramName param, mempty)-                     | param <- mergepat,-                       unique $ paramDeclType param-                     ]--    context "Inside the loop body" $-      checkFun' (nameFromString "<loop body>",-                 staticShapes rettype,-                 funParamsToNameInfos mergepat) consumable $ do-          checkFunParams mergepat-          checkBodyLore $ snd $ bodyDec loopbody--          checkStms (bodyStms loopbody) $ do-            checkResult $ bodyResult loopbody--            context "When matching result of body with loop parameters" $-              matchLoopResult (map fst ctxmerge) (map fst valmerge) $-              bodyResult loopbody--            let bound_here = namesFromList $ M.keys $-                             scopeOf $ bodyStms loopbody-            map (`namesSubtract` bound_here) <$>-              mapM subExpAliasesM (bodyResult loopbody)--checkExp (Op op) = do checker <- asks envCheckOp-                      checker op--checkSOACArrayArgs :: Checkable lore =>-                      SubExp -> [VName] -> TypeM lore [Arg]-checkSOACArrayArgs width vs =-  forM vs $ \v -> do-    (vt, v') <- checkSOACArrayArg v-    let argSize = arraySize 0 vt-    unless (argSize == width) $-      bad $ TypeError $-      "SOAC argument " ++ pretty v ++ " has outer size " ++-      pretty argSize ++ ", but width of SOAC is " ++-      pretty width-    return v'-  where checkSOACArrayArg ident = do-          (t, als) <- checkArg $ Var ident-          case peelArray 1 t of-            Nothing -> bad $ TypeError $-                       "SOAC argument " ++ pretty ident ++ " is not an array"-            Just rt -> return (t, (rt, als))--checkType :: Checkable lore =>-             TypeBase Shape u -> TypeM lore ()-checkType (Mem (ScalarSpace d _)) = mapM_ (require [Prim int32]) d-checkType t = mapM_ checkSubExp $ arrayDims t--checkExtType :: Checkable lore =>-                TypeBase ExtShape u-             -> TypeM lore ()-checkExtType = mapM_ checkExtDim . shapeDims . arrayShape-  where checkExtDim (Free se) = void $ checkSubExp se-        checkExtDim (Ext _)   = return ()--checkCmpOp :: Checkable lore =>-              CmpOp -> SubExp -> SubExp-           -> TypeM lore ()-checkCmpOp (CmpEq t) x y = do-  require [Prim t] x-  require [Prim t] y-checkCmpOp (CmpUlt t) x y = checkBinOpArgs (IntType t) x y-checkCmpOp (CmpUle t) x y = checkBinOpArgs (IntType t) x y-checkCmpOp (CmpSlt t) x y = checkBinOpArgs (IntType t) x y-checkCmpOp (CmpSle t) x y = checkBinOpArgs (IntType t) x y-checkCmpOp (FCmpLt t) x y = checkBinOpArgs (FloatType t) x y-checkCmpOp (FCmpLe t) x y = checkBinOpArgs (FloatType t) x y-checkCmpOp CmpLlt x y = checkBinOpArgs Bool x y-checkCmpOp CmpLle x y = checkBinOpArgs Bool x y--checkBinOpArgs :: Checkable lore =>-                  PrimType -> SubExp -> SubExp -> TypeM lore ()-checkBinOpArgs t e1 e2 = do-  require [Prim t] e1-  require [Prim t] e2--checkPatElem :: Checkable lore =>-                PatElemT (LetDec lore) -> TypeM lore ()-checkPatElem (PatElem name dec) = context ("When checking pattern element " ++ pretty name) $-                                   checkLetBoundLore name dec--checkDimIndex :: Checkable lore =>-                 DimIndex SubExp -> TypeM lore ()-checkDimIndex (DimFix i) = require [Prim int32] i-checkDimIndex (DimSlice i n s) = mapM_ (require [Prim int32]) [i,n,s]--checkStm :: Checkable lore =>-            Stm (Aliases lore)-         -> TypeM lore a-         -> TypeM lore a-checkStm stm@(Let pat (StmAux (Certificates cs) _ (_,dec)) e) m = do-  context "When checking certificates" $ mapM_ (requireI [Prim Cert]) cs-  context "When checking expression annotation" $ checkExpLore dec-  context ("When matching\n" ++ message "  " pat ++ "\nwith\n" ++ message "  " e) $-    matchPattern pat e-  binding (maybeWithoutAliases $ scopeOf stm) $ do-    mapM_ checkPatElem (patternElements $ removePatternAliases pat)-    m-  where-    -- FIXME: this is wrong.  However, the core language type system-    -- is not strong enough to fully capture the aliases we want (see-    -- issue #803).  Since we eventually inline everything anyway, and-    -- our intra-procedural alias analysis is much simpler and-    -- correct, I could not justify spending time on improving the-    -- inter-procedural alias analysis.  If we ever stop inlining-    -- everything, probably we need to go back and refine this.-    maybeWithoutAliases =-      case stmExp stm of-        Apply{} -> M.map withoutAliases-        _ -> id-    withoutAliases (LetName (_, ldec)) = LetName (mempty, ldec)-    withoutAliases info = info--matchExtPattern :: Checkable lore =>-                   Pattern (Aliases lore) -> [ExtType] -> TypeM lore ()-matchExtPattern pat ts =-  unless (expExtTypesFromPattern pat == ts) $-    bad $ InvalidPatternError pat ts Nothing--matchExtReturnType :: Checkable lore =>-                      [ExtType] -> Result -> TypeM lore ()-matchExtReturnType rettype res = do-  ts <- mapM subExpType res-  matchExtReturns rettype res ts--matchExtBranchType :: Checkable lore =>-                      [ExtType] -> Body (Aliases lore) -> TypeM lore ()-matchExtBranchType rettype (Body _ stms res) = do-  ts <- extendedScope (traverse subExpType res) stmscope-  matchExtReturns rettype res ts-  where stmscope = scopeOf stms--matchExtReturns :: [ExtType] -> Result -> [Type] -> TypeM lore ()-matchExtReturns rettype res ts = do-  let problem :: TypeM lore a-      problem = bad $ TypeError $ unlines [ "Type annotation is"-                                          , "  " ++ prettyTuple rettype-                                          , "But result returns type"-                                          , "  " ++ prettyTuple ts ]--  let (ctx_res, val_res) = splitFromEnd (length rettype) res-      (ctx_ts, val_ts) = splitFromEnd (length rettype) ts--  unless (length val_res == length rettype) problem--  let num_exts = length $ S.fromList $-                 concatMap (mapMaybe isExt . arrayExtDims) rettype-  unless (num_exts == length ctx_res) $-    bad $ TypeError $-    "Number of context results does not match number of existentials in the return type.\n" ++-    "Type:\n  " ++-    prettyTuple rettype ++-    "\ncannot match context parameters:\n  " ++ prettyTuple ctx_res--  let ctx_vals = zip ctx_res ctx_ts-      instantiateExt i = case maybeNth i ctx_vals of-                           Just (se, Prim (IntType Int32)) -> return se-                           _ -> problem--  rettype' <- instantiateShapes instantiateExt rettype--  unless (rettype' == val_ts) problem--validApply :: ArrayShape shape =>-              [TypeBase shape Uniqueness]-           -> [TypeBase shape NoUniqueness]-           -> Bool-validApply expected got =-  length got == length expected &&-  and (zipWith subtypeOf-       (map rankShaped got)-       (map (fromDecl . rankShaped) expected))--type Arg = (Type, Names)--argType :: Arg -> Type-argType (t, _) = t---- | Remove all aliases from the 'Arg'.-argAliases :: Arg -> Names-argAliases (_, als) = als--noArgAliases :: Arg -> Arg-noArgAliases (t, _) = (t, mempty)--checkArg :: Checkable lore =>-            SubExp -> TypeM lore Arg-checkArg arg = do argt <- checkSubExp arg-                  als <- subExpAliasesM arg-                  return (argt, als)--checkFuncall :: Maybe Name-             -> [DeclType] -> [Arg]-             -> TypeM lore ()-checkFuncall fname paramts args = do-  let argts = map argType args-  unless (validApply paramts argts) $-    bad $ ParameterMismatch fname-          (map fromDecl paramts) $-          map argType args-  forM_ (zip (map diet paramts) args) $ \(d, (_, als)) ->-    occur [consumption (consumeArg als d)]-  where consumeArg als Consume = als-        consumeArg _   _       = mempty--checkLambda :: Checkable lore =>-               Lambda (Aliases lore) -> [Arg] -> TypeM lore ()-checkLambda (Lambda params body rettype) args = do-  let fname = nameFromString "<anonymous>"-  if length params == length args then do-    checkFuncall Nothing-      (map ((`toDecl` Nonunique) . paramType) params) args-    let consumable = zip (map paramName params) (map argAliases args)-    checkFun' (fname,-               staticShapes $ map (`toDecl` Nonunique) rettype,-               [ (paramName param,-                  LParamName $ paramDec param)-               | param <- params ]) consumable $ do-      checkLambdaParams params-      mapM_ checkType rettype-      checkLambdaBody rettype body-  else bad $ TypeError $ "Anonymous function defined with " ++ show (length params) ++ " parameters, but expected to take " ++ show (length args) ++ " arguments."--checkPrimExp :: Checkable lore => PrimExp VName -> TypeM lore ()-checkPrimExp ValueExp{} = return ()-checkPrimExp (LeafExp v pt) = requireI [Prim pt] v-checkPrimExp (BinOpExp op x y) = do requirePrimExp (binOpType op) x-                                    requirePrimExp (binOpType op) y-checkPrimExp (CmpOpExp op x y) = do requirePrimExp (cmpOpType op) x-                                    requirePrimExp (cmpOpType op) y-checkPrimExp (UnOpExp op x) = requirePrimExp (unOpType op) x-checkPrimExp (ConvOpExp op x) = requirePrimExp (fst $ convOpType op) x-checkPrimExp (FunExp h args t) = do-  (h_ts, h_ret, _) <- maybe (bad $ TypeError $ "Unknown function: " ++ h)-                      return $ M.lookup h primFuns-  when (length h_ts /= length args) $-    bad $ TypeError $ "Function expects " ++ show (length h_ts) ++-    " parameters, but given " ++ show (length args) ++ " arguments."-  when (h_ret /= t) $-    bad $ TypeError $ "Function return annotation is " ++ pretty t ++-    ", but expected " ++ pretty h_ret-  zipWithM_ requirePrimExp h_ts args--requirePrimExp :: Checkable lore => PrimType -> PrimExp VName -> TypeM lore ()-requirePrimExp t e = context ("in PrimExp " ++ pretty e) $ do-  checkPrimExp e-  unless (primExpType e == t) $ bad $ TypeError $-    pretty e ++ " must have type " ++ pretty t--class ASTLore lore => CheckableOp lore where-  checkOp :: OpWithAliases (Op lore) -> TypeM lore ()-  -- ^ Used at top level; can be locally changed with 'checkOpWith'.---- | The class of lores that can be type-checked.-class (ASTLore lore, CanBeAliased (Op lore), CheckableOp lore) => Checkable lore where-  checkExpLore :: ExpDec lore -> TypeM lore ()-  checkBodyLore :: BodyDec lore -> TypeM lore ()-  checkFParamLore :: VName -> FParamInfo lore -> TypeM lore ()-  checkLParamLore :: VName -> LParamInfo lore -> TypeM lore ()-  checkLetBoundLore :: VName -> LetDec lore -> TypeM lore ()-  checkRetType :: [RetType lore] -> TypeM lore ()-  matchPattern :: Pattern (Aliases lore) -> Exp (Aliases lore) -> TypeM lore ()-  primFParam :: VName -> PrimType -> TypeM lore (FParam (Aliases lore))-  matchReturnType :: [RetType lore] -> Result -> TypeM lore ()-  matchBranchType :: [BranchType lore] -> Body (Aliases lore) -> TypeM lore ()-  matchLoopResult :: [FParam (Aliases lore)] -> [FParam (Aliases lore)]-                  -> [SubExp] -> TypeM lore ()--  default checkExpLore :: ExpDec lore ~ () => ExpDec lore -> TypeM lore ()-  checkExpLore = return--  default checkBodyLore :: BodyDec lore ~ () => BodyDec lore -> TypeM lore ()-  checkBodyLore = return--  default checkFParamLore :: FParamInfo lore ~ DeclType => VName -> FParamInfo lore -> TypeM lore ()-  checkFParamLore _ = checkType--  default checkLParamLore :: LParamInfo lore ~ Type => VName -> LParamInfo lore -> TypeM lore ()-  checkLParamLore _ = checkType--  default checkLetBoundLore :: LetDec lore ~ Type => VName -> LetDec lore -> TypeM lore ()-  checkLetBoundLore _ = checkType--  default checkRetType :: RetType lore ~ DeclExtType => [RetType lore] -> TypeM lore ()-  checkRetType = mapM_ $ checkExtType . declExtTypeOf--  default matchPattern :: Pattern (Aliases lore) -> Exp (Aliases lore) -> TypeM lore ()-  matchPattern pat = matchExtPattern pat <=< expExtType--  default primFParam :: FParamInfo lore ~ DeclType => VName -> PrimType -> TypeM lore (FParam (Aliases lore))-  primFParam name t = return $ Param name (Prim t)--  default matchReturnType :: RetType lore ~ DeclExtType => [RetType lore] -> Result -> TypeM lore ()-  matchReturnType = matchExtReturnType . map fromDecl--  default matchBranchType :: BranchType lore ~ ExtType => [BranchType lore] -> Body (Aliases lore) -> TypeM lore ()-  matchBranchType = matchExtBranchType--  default matchLoopResult :: FParamInfo lore ~ DeclType =>-                             [FParam (Aliases lore)] -> [FParam (Aliases lore)]-                          -> [SubExp] -> TypeM lore ()+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE Strict #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}++-- | The type checker checks whether the program is type-consistent.+module Futhark.TypeCheck+  ( -- * Interface+    checkProg,+    TypeError (..),+    ErrorCase (..),++    -- * Extensionality+    TypeM,+    bad,+    context,+    message,+    Checkable (..),+    CheckableOp (..),+    lookupVar,+    lookupAliases,+    checkOpWith,++    -- * Checkers+    require,+    requireI,+    requirePrimExp,+    checkSubExp,+    checkExp,+    checkStms,+    checkStm,+    checkType,+    checkExtType,+    matchExtPattern,+    matchExtBranchType,+    argType,+    argAliases,+    noArgAliases,+    checkArg,+    checkSOACArrayArgs,+    checkLambda,+    checkBody,+    consume,+    consumeOnlyParams,+    binding,+  )+where++import Control.Monad.RWS.Strict+import Control.Parallel.Strategies+import Data.List (find, intercalate, sort)+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Data.Set as S+import Futhark.Analysis.PrimExp+import Futhark.Construct (instantiateShapes)+import Futhark.IR.Aliases+import Futhark.Util+import Futhark.Util.Pretty (Pretty, align, indent, ppr, prettyDoc, text, (<+>))++-- | Information about an error during type checking.  The 'Show'+-- instance for this type produces a human-readable description.+data ErrorCase lore+  = TypeError String+  | UnexpectedType (Exp lore) Type [Type]+  | ReturnTypeError Name [ExtType] [ExtType]+  | DupDefinitionError Name+  | DupParamError Name VName+  | DupPatternError VName+  | InvalidPatternError (Pattern (Aliases lore)) [ExtType] (Maybe String)+  | UnknownVariableError VName+  | UnknownFunctionError Name+  | ParameterMismatch (Maybe Name) [Type] [Type]+  | SlicingError Int Int+  | BadAnnotation String Type Type+  | ReturnAliased Name VName+  | UniqueReturnAliased Name+  | NotAnArray VName Type+  | PermutationError [Int] Int (Maybe VName)++instance Checkable lore => Show (ErrorCase lore) where+  show (TypeError msg) =+    "Type error:\n" ++ msg+  show (UnexpectedType e _ []) =+    "Type of expression\n"+      ++ prettyDoc 160 (indent 2 $ ppr e)+      ++ "\ncannot have any type - possibly a bug in the type checker."+  show (UnexpectedType e t ts) =+    "Type of expression\n"+      ++ prettyDoc 160 (indent 2 $ ppr e)+      ++ "\nmust be one of "+      ++ intercalate ", " (map pretty ts)+      ++ ", but is "+      ++ pretty t+      ++ "."+  show (ReturnTypeError fname rettype bodytype) =+    "Declaration of function " ++ nameToString fname+      ++ " declares return type\n  "+      ++ prettyTuple rettype+      ++ "\nBut body has type\n  "+      ++ prettyTuple bodytype+  show (DupDefinitionError name) =+    "Duplicate definition of function " ++ nameToString name ++ ""+  show (DupParamError funname paramname) =+    "Parameter " ++ pretty paramname+      ++ " mentioned multiple times in argument list of function "+      ++ nameToString funname+      ++ "."+  show (DupPatternError name) =+    "Variable " ++ pretty name ++ " bound twice in pattern."+  show (InvalidPatternError pat t desc) =+    "Pattern " ++ pretty pat+      ++ " cannot match value of type "+      ++ prettyTuple t+      ++ end+    where+      end = case desc of+        Nothing -> "."+        Just desc' -> ":\n" ++ desc'+  show (UnknownVariableError name) =+    "Use of unknown variable " ++ pretty name ++ "."+  show (UnknownFunctionError fname) =+    "Call of unknown function " ++ nameToString fname ++ "."+  show (ParameterMismatch fname expected got) =+    "In call of " ++ fname' ++ ":\n"+      ++ "expecting "+      ++ show nexpected+      ++ " arguments of type(s)\n"+      ++ intercalate ", " (map pretty expected)+      ++ "\nGot "+      ++ show ngot+      ++ " arguments of types\n"+      ++ intercalate ", " (map pretty got)+    where+      nexpected = length expected+      ngot = length got+      fname' = maybe "anonymous function" (("function " ++) . nameToString) fname+  show (SlicingError dims got) =+    show got ++ " indices given, but type of indexee has " ++ show dims ++ " dimension(s)."+  show (BadAnnotation desc expected got) =+    "Annotation of \"" ++ desc ++ "\" type of expression is " ++ pretty expected+      ++ ", but derived to be "+      ++ pretty got+      ++ "."+  show (ReturnAliased fname name) =+    "Unique return value of function " ++ nameToString fname+      ++ " is aliased to "+      ++ pretty name+      ++ ", which is not consumed."+  show (UniqueReturnAliased fname) =+    "A unique tuple element of return value of function "+      ++ nameToString fname+      ++ " is aliased to some other tuple component."+  show (NotAnArray e t) =+    "The expression " ++ pretty e+      ++ " is expected to be an array, but is "+      ++ pretty t+      ++ "."+  show (PermutationError perm rank name) =+    "The permutation (" ++ intercalate ", " (map show perm)+      ++ ") is not valid for array "+      ++ name'+      ++ "of rank "+      ++ show rank+      ++ "."+    where+      name' = maybe "" ((++ " ") . pretty) name++-- | A type error.+data TypeError lore = Error [String] (ErrorCase lore)++instance Checkable lore => Show (TypeError lore) where+  show (Error [] err) =+    show err+  show (Error msgs err) =+    intercalate "\n" msgs ++ "\n" ++ show err++-- | A tuple of a return type and a list of parameters, possibly+-- named.+type FunBinding lore = ([RetType (Aliases lore)], [FParam (Aliases lore)])++type VarBinding lore = NameInfo (Aliases lore)++data Usage+  = Consumed+  | Observed+  deriving (Eq, Ord, Show)++data Occurence = Occurence+  { observed :: Names,+    consumed :: Names+  }+  deriving (Eq, Show)++observation :: Names -> Occurence+observation = flip Occurence mempty++consumption :: Names -> Occurence+consumption = Occurence mempty++nullOccurence :: Occurence -> Bool+nullOccurence occ = observed occ == mempty && consumed occ == mempty++type Occurences = [Occurence]++allConsumed :: Occurences -> Names+allConsumed = mconcat . map consumed++seqOccurences :: Occurences -> Occurences -> Occurences+seqOccurences occurs1 occurs2 =+  filter (not . nullOccurence) (map filt occurs1) ++ occurs2+  where+    filt occ =+      occ {observed = observed occ `namesSubtract` postcons}+    postcons = allConsumed occurs2++altOccurences :: Occurences -> Occurences -> Occurences+altOccurences occurs1 occurs2 =+  filter (not . nullOccurence) (map filt occurs1) ++ occurs2+  where+    filt occ =+      occ+        { consumed = consumed occ `namesSubtract` postcons,+          observed = observed occ `namesSubtract` postcons+        }+    postcons = allConsumed occurs2++unOccur :: Names -> Occurences -> Occurences+unOccur to_be_removed = filter (not . nullOccurence) . map unOccur'+  where+    unOccur' occ =+      occ+        { observed = observed occ `namesSubtract` to_be_removed,+          consumed = consumed occ `namesSubtract` to_be_removed+        }++-- | The 'Consumption' data structure is used to keep track of which+-- variables have been consumed, as well as whether a violation has been detected.+data Consumption+  = ConsumptionError String+  | Consumption Occurences+  deriving (Show)++instance Semigroup Consumption where+  ConsumptionError e <> _ = ConsumptionError e+  _ <> ConsumptionError e = ConsumptionError e+  Consumption o1 <> Consumption o2+    | v : _ <- namesToList $ consumed_in_o1 `namesIntersection` used_in_o2 =+      ConsumptionError $ "Variable " <> pretty v <> " referenced after being consumed."+    | otherwise =+      Consumption $ o1 `seqOccurences` o2+    where+      consumed_in_o1 = mconcat $ map consumed o1+      used_in_o2 = mconcat $ map consumed o2 <> map observed o2++instance Monoid Consumption where+  mempty = Consumption mempty++-- | The environment contains a variable table and a function table.+-- Type checking happens with access to this environment.  The+-- function table is only initialised at the very beginning, but the+-- variable table will be extended during type-checking when+-- let-expressions are encountered.+data Env lore = Env+  { envVtable :: M.Map VName (VarBinding lore),+    envFtable :: M.Map Name (FunBinding lore),+    envCheckOp :: OpWithAliases (Op lore) -> TypeM lore (),+    envContext :: [String]+  }++-- | The type checker runs in this monad.+newtype TypeM lore a+  = TypeM+      ( RWST+          (Env lore) -- Reader+          Consumption -- Writer+          Names -- State+          (Either (TypeError lore)) -- Inner monad+          a+      )+  deriving+    ( Monad,+      Functor,+      Applicative,+      MonadReader (Env lore),+      MonadWriter Consumption,+      MonadState Names+    )++instance+  Checkable lore =>+  HasScope (Aliases lore) (TypeM lore)+  where+  lookupType = fmap typeOf . lookupVar+  askScope = asks $ M.fromList . mapMaybe varType . M.toList . envVtable+    where+      varType (name, dec) = Just (name, dec)++runTypeM ::+  Env lore ->+  TypeM lore a ->+  Either (TypeError lore) (a, Consumption)+runTypeM env (TypeM m) = evalRWST m env mempty++bad :: ErrorCase lore -> TypeM lore a+bad e = do+  messages <- asks envContext+  TypeM $ lift $ Left $ Error (reverse messages) e++-- | Add information about what is being type-checked to the current+-- context.  Liberal use of this combinator makes it easier to track+-- type errors, as the strings are added to type errors signalled via+-- 'bad'.+context ::+  String ->+  TypeM lore a ->+  TypeM lore a+context s = local $ \env -> env {envContext = s : envContext env}++message ::+  Pretty a =>+  String ->+  a ->+  String+message s x =+  prettyDoc 80 $+    text s <+> align (ppr x)++-- | Mark a name as bound.  If the name has been bound previously in+-- the program, report a type error.+bound :: VName -> TypeM lore ()+bound name = do+  already_seen <- gets $ nameIn name+  when already_seen $+    bad $ TypeError $ "Name " ++ pretty name ++ " bound twice"+  modify (<> oneName name)++occur :: Occurences -> TypeM lore ()+occur = tell . Consumption . filter (not . nullOccurence)++-- | Proclaim that we have made read-only use of the given variable.+-- No-op unless the variable is array-typed.+observe ::+  Checkable lore =>+  VName ->+  TypeM lore ()+observe name = do+  dec <- lookupVar name+  unless (primType $ typeOf dec) $+    occur [observation $ oneName name <> aliases dec]++-- | Proclaim that we have written to the given variables.+consume :: Checkable lore => Names -> TypeM lore ()+consume als = do+  scope <- askScope+  let isArray = maybe False ((> 0) . arrayRank . typeOf) . (`M.lookup` scope)+  occur [consumption $ namesFromList $ filter isArray $ namesToList als]++collectOccurences :: TypeM lore a -> TypeM lore (a, Occurences)+collectOccurences m = pass $ do+  (x, c) <- listen m+  o <- checkConsumption c+  return ((x, o), const mempty)++checkOpWith ::+  (OpWithAliases (Op lore) -> TypeM lore ()) ->+  TypeM lore a ->+  TypeM lore a+checkOpWith checker = local $ \env -> env {envCheckOp = checker}++checkConsumption :: Consumption -> TypeM lore Occurences+checkConsumption (ConsumptionError e) = bad $ TypeError e+checkConsumption (Consumption os) = return os++alternative :: TypeM lore a -> TypeM lore b -> TypeM lore (a, b)+alternative m1 m2 = pass $ do+  (x, c1) <- listen m1+  (y, c2) <- listen m2+  os1 <- checkConsumption c1+  os2 <- checkConsumption c2+  let usage = Consumption $ os1 `altOccurences` os2+  return ((x, y), const usage)++-- | Permit consumption of only the specified names.  If one of these+-- names is consumed, the consumption will be rewritten to be a+-- consumption of the corresponding alias set.  Consumption of+-- anything else will result in a type error.+consumeOnlyParams :: [(VName, Names)] -> TypeM lore a -> TypeM lore a+consumeOnlyParams consumable m = do+  (x, os) <- collectOccurences m+  tell . Consumption =<< mapM inspect os+  return x+  where+    inspect o = do+      new_consumed <- mconcat <$> mapM wasConsumed (namesToList $ consumed o)+      return o {consumed = new_consumed}+    wasConsumed v+      | Just als <- lookup v consumable = return als+      | otherwise =+        bad $+          TypeError $+            unlines+              [ pretty v ++ " was invalidly consumed.",+                what ++ " can be consumed here."+              ]+    what+      | null consumable = "Nothing"+      | otherwise = "Only " ++ intercalate ", " (map (pretty . fst) consumable)++-- | Given the immediate aliases, compute the full transitive alias+-- set (including the immediate aliases).+expandAliases :: Names -> Env lore -> Names+expandAliases names env = names <> aliasesOfAliases+  where+    aliasesOfAliases = mconcat . map look . namesToList $ names+    look k = case M.lookup k $ envVtable env of+      Just (LetName (als, _)) -> unAliases als+      _ -> mempty++binding ::+  Checkable lore =>+  Scope (Aliases lore) ->+  TypeM lore a ->+  TypeM lore a+binding bnds = check . local (`bindVars` bnds)+  where+    bindVars = M.foldlWithKey' bindVar+    boundnames = M.keys bnds++    bindVar env name (LetName (AliasDec als, dec)) =+      let als'+            | primType (typeOf dec) = mempty+            | otherwise = expandAliases als env+       in env+            { envVtable =+                M.insert name (LetName (AliasDec als', dec)) $ envVtable env+            }+    bindVar env name dec =+      env {envVtable = M.insert name dec $ envVtable env}++    -- Check whether the bound variables have been used correctly+    -- within their scope.+    check m = do+      mapM_ bound $ M.keys bnds+      (a, os) <- collectOccurences m+      tell $ Consumption $ unOccur (namesFromList boundnames) os+      return a++lookupVar :: VName -> TypeM lore (NameInfo (Aliases lore))+lookupVar name = do+  bnd <- asks $ M.lookup name . envVtable+  case bnd of+    Nothing -> bad $ UnknownVariableError name+    Just dec -> return dec++lookupAliases :: Checkable lore => VName -> TypeM lore Names+lookupAliases name = do+  info <- lookupVar name+  return $+    if primType $ typeOf info+      then mempty+      else oneName name <> aliases info++aliases :: NameInfo (Aliases lore) -> Names+aliases (LetName (als, _)) = unAliases als+aliases _ = mempty++subExpAliasesM :: Checkable lore => SubExp -> TypeM lore Names+subExpAliasesM Constant {} = return mempty+subExpAliasesM (Var v) = lookupAliases v++lookupFun ::+  Checkable lore =>+  Name ->+  [SubExp] ->+  TypeM lore ([RetType lore], [DeclType])+lookupFun fname args = do+  bnd <- asks $ M.lookup fname . envFtable+  case bnd of+    Nothing -> bad $ UnknownFunctionError fname+    Just (ftype, params) -> do+      argts <- mapM subExpType args+      case applyRetType ftype params $ zip args argts of+        Nothing ->+          bad $ ParameterMismatch (Just fname) (map paramType params) argts+        Just rt ->+          return (rt, map paramDeclType params)++-- | @checkAnnotation loc s t1 t2@ checks if @t2@ is equal to+-- @t1@.  If not, a 'BadAnnotation' is raised.+checkAnnotation ::+  String ->+  Type ->+  Type ->+  TypeM lore ()+checkAnnotation desc t1 t2+  | t2 == t1 = return ()+  | otherwise = bad $ BadAnnotation desc t1 t2++-- | @require ts se@ causes a '(TypeError vn)' if the type of @se@ is+-- not a subtype of one of the types in @ts@.+require :: Checkable lore => [Type] -> SubExp -> TypeM lore ()+require ts se = do+  t <- checkSubExp se+  unless (t `elem` ts) $+    bad $ UnexpectedType (BasicOp $ SubExp se) t ts++-- | Variant of 'require' working on variable names.+requireI :: Checkable lore => [Type] -> VName -> TypeM lore ()+requireI ts ident = require ts $ Var ident++checkArrIdent ::+  Checkable lore =>+  VName ->+  TypeM lore Type+checkArrIdent v = do+  t <- lookupType v+  case t of+    Array {} -> return t+    _ -> bad $ NotAnArray v t++-- | Type check a program containing arbitrary type information,+-- yielding either a type error or a program with complete type+-- information.+checkProg ::+  Checkable lore =>+  Prog (Aliases lore) ->+  Either (TypeError lore) ()+checkProg (Prog consts funs) = do+  let typeenv =+        Env+          { envVtable = M.empty,+            envFtable = mempty,+            envContext = [],+            envCheckOp = checkOp+          }+  let onFunction ftable vtable fun =+        fmap fst $+          runTypeM typeenv $+            local (\env -> env {envFtable = ftable, envVtable = vtable}) $+              checkFun fun+  (ftable, _) <- runTypeM typeenv buildFtable+  (vtable, _) <-+    runTypeM typeenv {envFtable = ftable} $+      checkStms consts $ asks envVtable+  sequence_ $ parMap rpar (onFunction ftable vtable) funs+  where+    buildFtable = do+      table <- initialFtable+      foldM expand table funs+    expand ftable (FunDef _ _ name ret params _)+      | M.member name ftable =+        bad $ DupDefinitionError name+      | otherwise =+        return $ M.insert name (ret, params) ftable++initialFtable ::+  Checkable lore =>+  TypeM lore (M.Map Name (FunBinding lore))+initialFtable = fmap M.fromList $ mapM addBuiltin $ M.toList builtInFunctions+  where+    addBuiltin (fname, (t, ts)) = do+      ps <- mapM (primFParam name) ts+      return (fname, ([primRetType t], ps))+    name = VName (nameFromString "x") 0++checkFun ::+  Checkable lore =>+  FunDef (Aliases lore) ->+  TypeM lore ()+checkFun (FunDef _ _ fname rettype params body) =+  context ("In function " ++ nameToString fname) $+    checkFun'+      ( fname,+        map declExtTypeOf rettype,+        funParamsToNameInfos params+      )+      consumable+      $ do+        checkFunParams params+        checkRetType rettype+        context "When checking function body" $ checkFunBody rettype body+  where+    consumable =+      [ (paramName param, mempty)+        | param <- params,+          unique $ paramDeclType param+      ]++funParamsToNameInfos ::+  [FParam lore] ->+  [(VName, NameInfo (Aliases lore))]+funParamsToNameInfos = map nameTypeAndLore+  where+    nameTypeAndLore fparam =+      ( paramName fparam,+        FParamName $ paramDec fparam+      )++checkFunParams ::+  Checkable lore =>+  [FParam lore] ->+  TypeM lore ()+checkFunParams = mapM_ $ \param ->+  context ("In function parameter " ++ pretty param) $+    checkFParamLore (paramName param) (paramDec param)++checkLambdaParams ::+  Checkable lore =>+  [LParam lore] ->+  TypeM lore ()+checkLambdaParams = mapM_ $ \param ->+  context ("In lambda parameter " ++ pretty param) $+    checkLParamLore (paramName param) (paramDec param)++checkFun' ::+  Checkable lore =>+  ( Name,+    [DeclExtType],+    [(VName, NameInfo (Aliases lore))]+  ) ->+  [(VName, Names)] ->+  TypeM lore [Names] ->+  TypeM lore ()+checkFun' (fname, rettype, params) consumable check = do+  checkNoDuplicateParams+  binding (M.fromList params) $+    consumeOnlyParams consumable $ do+      body_aliases <- check+      scope <- askScope+      let isArray = maybe False ((> 0) . arrayRank . typeOf) . (`M.lookup` scope)+      context+        ( "When checking the body aliases: "+            ++ pretty (map namesToList body_aliases)+        )+        $ checkReturnAlias $ map (namesFromList . filter isArray . namesToList) body_aliases+  where+    param_names = map fst params++    checkNoDuplicateParams = foldM_ expand [] param_names++    expand seen pname+      | Just _ <- find (== pname) seen =+        bad $ DupParamError fname pname+      | otherwise =+        return $ pname : seen+    checkReturnAlias =+      foldM_ checkReturnAlias' mempty . returnAliasing rettype++    checkReturnAlias' seen (Unique, names)+      | any (`S.member` S.map fst seen) $ namesToList names =+        bad $ UniqueReturnAliased fname+      | otherwise = do+        consume names+        return $ seen <> tag Unique names+    checkReturnAlias' seen (Nonunique, names)+      | any (`S.member` seen) $ tag Unique names =+        bad $ UniqueReturnAliased fname+      | otherwise = return $ seen <> tag Nonunique names++    tag u = S.fromList . map (,u) . namesToList++    returnAliasing expected got =+      reverse $+        zip (reverse (map uniqueness expected) ++ repeat Nonunique) $+          reverse got++checkSubExp :: Checkable lore => SubExp -> TypeM lore Type+checkSubExp (Constant val) =+  return $ Prim $ primValueType val+checkSubExp (Var ident) = context ("In subexp " ++ pretty ident) $ do+  observe ident+  lookupType ident++checkStms ::+  Checkable lore =>+  Stms (Aliases lore) ->+  TypeM lore a ->+  TypeM lore a+checkStms origbnds m = delve $ stmsToList origbnds+  where+    delve (stm@(Let pat _ e) : bnds) = do+      context ("In expression of statement " ++ pretty pat) $+        checkExp e+      checkStm stm $+        delve bnds+    delve [] =+      m++checkResult ::+  Checkable lore =>+  Result ->+  TypeM lore ()+checkResult = mapM_ checkSubExp++checkFunBody ::+  Checkable lore =>+  [RetType lore] ->+  Body (Aliases lore) ->+  TypeM lore [Names]+checkFunBody rt (Body (_, lore) bnds res) = do+  checkBodyLore lore+  checkStms bnds $ do+    context "When checking body result" $ checkResult res+    context "When matching declared return type to result of body" $+      matchReturnType rt res+    map (`namesSubtract` bound_here) <$> mapM subExpAliasesM res+  where+    bound_here = namesFromList $ M.keys $ scopeOf bnds++checkLambdaBody ::+  Checkable lore =>+  [Type] ->+  Body (Aliases lore) ->+  TypeM lore [Names]+checkLambdaBody ret (Body (_, lore) bnds res) = do+  checkBodyLore lore+  checkStms bnds $ do+    checkLambdaResult ret res+    map (`namesSubtract` bound_here) <$> mapM subExpAliasesM res+  where+    bound_here = namesFromList $ M.keys $ scopeOf bnds++checkLambdaResult ::+  Checkable lore =>+  [Type] ->+  Result ->+  TypeM lore ()+checkLambdaResult ts es+  | length ts /= length es =+    bad $+      TypeError $+        "Lambda has return type " ++ prettyTuple ts+          ++ " describing "+          ++ show (length ts)+          ++ " values, but body returns "+          ++ show (length es)+          ++ " values: "+          ++ prettyTuple es+  | otherwise = forM_ (zip ts es) $ \(t, e) -> do+    et <- checkSubExp e+    unless (et == t) $+      bad $+        TypeError $+          "Subexpression " ++ pretty e ++ " has type " ++ pretty et+            ++ " but expected "+            ++ pretty t++checkBody ::+  Checkable lore =>+  Body (Aliases lore) ->+  TypeM lore [Names]+checkBody (Body (_, lore) bnds res) = do+  checkBodyLore lore+  checkStms bnds $ do+    checkResult res+    map (`namesSubtract` bound_here) <$> mapM subExpAliasesM res+  where+    bound_here = namesFromList $ M.keys $ scopeOf bnds++checkBasicOp :: Checkable lore => BasicOp -> TypeM lore ()+checkBasicOp (SubExp es) =+  void $ checkSubExp es+checkBasicOp (Opaque es) =+  void $ checkSubExp es+checkBasicOp (ArrayLit [] _) =+  return ()+checkBasicOp (ArrayLit (e : es') t) = do+  let check elemt eleme = do+        elemet <- checkSubExp eleme+        unless (elemet == elemt) $+          bad $+            TypeError $+              pretty elemet+                ++ " is not of expected type "+                ++ pretty elemt+                ++ "."+  et <- checkSubExp e++  -- Compare that type with the one given for the array literal.+  checkAnnotation "array-element" t et++  mapM_ (check et) es'+checkBasicOp (UnOp op e) = require [Prim $ unOpType op] e+checkBasicOp (BinOp op e1 e2) = checkBinOpArgs (binOpType op) e1 e2+checkBasicOp (CmpOp op e1 e2) = checkCmpOp op e1 e2+checkBasicOp (ConvOp op e) = require [Prim $ fst $ convOpType op] e+checkBasicOp (Index ident idxes) = do+  vt <- lookupType ident+  observe ident+  when (arrayRank vt /= length idxes) $+    bad $ SlicingError (arrayRank vt) (length idxes)+  mapM_ checkDimIndex idxes+checkBasicOp (Update src idxes se) = do+  src_t <- checkArrIdent src+  when (arrayRank src_t /= length idxes) $+    bad $ SlicingError (arrayRank src_t) (length idxes)++  se_aliases <- subExpAliasesM se+  when (src `nameIn` se_aliases) $+    bad $ TypeError "The target of an Update must not alias the value to be written."++  mapM_ checkDimIndex idxes+  require [Prim (elemType src_t) `arrayOfShape` Shape (sliceDims idxes)] se+  consume =<< lookupAliases src+checkBasicOp (Iota e x s et) = do+  require [Prim int64] e+  require [Prim $ IntType et] x+  require [Prim $ IntType et] s+checkBasicOp (Replicate (Shape dims) valexp) = do+  mapM_ (require [Prim int64]) dims+  void $ checkSubExp valexp+checkBasicOp (Scratch _ shape) =+  mapM_ checkSubExp shape+checkBasicOp (Reshape newshape arrexp) = do+  rank <- arrayRank <$> checkArrIdent arrexp+  mapM_ (require [Prim int64] . newDim) newshape+  zipWithM_ (checkDimChange rank) newshape [0 ..]+  where+    checkDimChange _ (DimNew _) _ =+      return ()+    checkDimChange rank (DimCoercion se) i+      | i >= rank =+        bad $+          TypeError $+            "Asked to coerce dimension " ++ show i ++ " to " ++ pretty se+              ++ ", but array "+              ++ pretty arrexp+              ++ " has only "+              ++ pretty rank+              ++ " dimensions"+      | otherwise =+        return ()+checkBasicOp (Rearrange perm arr) = do+  arrt <- lookupType arr+  let rank = arrayRank arrt+  when (length perm /= rank || sort perm /= [0 .. rank -1]) $+    bad $ PermutationError perm rank $ Just arr+checkBasicOp (Rotate rots arr) = do+  arrt <- lookupType arr+  let rank = arrayRank arrt+  mapM_ (require [Prim int64]) rots+  when (length rots /= rank) $+    bad $+      TypeError $+        "Cannot rotate " ++ show (length rots)+          ++ " dimensions of "+          ++ show rank+          ++ "-dimensional array."+checkBasicOp (Concat i arr1exp arr2exps ressize) = do+  arr1t <- checkArrIdent arr1exp+  arr2ts <- mapM checkArrIdent arr2exps+  let success =+        all+          ( (== dropAt i 1 (arrayDims arr1t))+              . dropAt i 1+              . arrayDims+          )+          arr2ts+  unless success $+    bad $+      TypeError $+        "Types of arguments to concat do not match.  Got "+          ++ pretty arr1t+          ++ " and "+          ++ intercalate ", " (map pretty arr2ts)+  require [Prim int64] ressize+checkBasicOp (Copy e) =+  void $ checkArrIdent e+checkBasicOp (Manifest perm arr) =+  checkBasicOp $ Rearrange perm arr -- Basically same thing!+checkBasicOp (Assert e _ _) =+  require [Prim Bool] e++matchLoopResultExt ::+  Checkable lore =>+  [Param DeclType] ->+  [Param DeclType] ->+  [SubExp] ->+  TypeM lore ()+matchLoopResultExt ctx val loopres = do+  let rettype_ext =+        existentialiseExtTypes (map paramName ctx) $+          staticShapes $ map typeOf $ ctx ++ val++  bodyt <- mapM subExpType loopres++  case instantiateShapes (`maybeNth` loopres) rettype_ext of+    Nothing ->+      bad $+        ReturnTypeError+          (nameFromString "<loop body>")+          rettype_ext+          (staticShapes bodyt)+    Just rettype' ->+      unless (bodyt `subtypesOf` rettype') $+        bad $+          ReturnTypeError+            (nameFromString "<loop body>")+            (staticShapes rettype')+            (staticShapes bodyt)++checkExp ::+  Checkable lore =>+  Exp (Aliases lore) ->+  TypeM lore ()+checkExp (BasicOp op) = checkBasicOp op+checkExp (If e1 e2 e3 info) = do+  require [Prim Bool] e1+  _ <- checkBody e2 `alternative` checkBody e3+  context "in true branch" $ matchBranchType (ifReturns info) e2+  context "in false branch" $ matchBranchType (ifReturns info) e3+checkExp (Apply fname args rettype_annot _) = do+  (rettype_derived, paramtypes) <- lookupFun fname $ map fst args+  argflows <- mapM (checkArg . fst) args+  when (rettype_derived /= rettype_annot) $+    bad $+      TypeError $+        "Expected apply result type " ++ pretty rettype_derived+          ++ " but annotation is "+          ++ pretty rettype_annot+  checkFuncall (Just fname) paramtypes argflows+checkExp (DoLoop ctxmerge valmerge form loopbody) = do+  let merge = ctxmerge ++ valmerge+      (mergepat, mergeexps) = unzip merge+  mergeargs <- mapM checkArg mergeexps++  let val_free = freeIn $ map fst valmerge+      usedInVal p = paramName p `nameIn` val_free+  case find (not . usedInVal . fst) ctxmerge of+    Just p ->+      bad $ TypeError $ "Loop context parameter " ++ pretty p ++ " unused."+    Nothing ->+      return ()++  binding (scopeOf form) $ do+    case form of+      ForLoop loopvar it boundexp loopvars -> do+        iparam <- primFParam loopvar $ IntType it+        let funparams = iparam : mergepat+            paramts = map paramDeclType funparams++        forM_ loopvars $ \(p, a) -> do+          a_t <- lookupType a+          observe a+          case peelArray 1 a_t of+            Just a_t_r -> do+              checkLParamLore (paramName p) $ paramDec p+              unless (a_t_r `subtypeOf` typeOf (paramDec p)) $+                bad $+                  TypeError $+                    "Loop parameter " ++ pretty p+                      ++ " not valid for element of "+                      ++ pretty a+                      ++ ", which has row type "+                      ++ pretty a_t_r+            _ ->+              bad $+                TypeError $+                  "Cannot loop over " ++ pretty a+                    ++ " of type "+                    ++ pretty a_t++        boundarg <- checkArg boundexp+        checkFuncall Nothing paramts $ boundarg : mergeargs+      WhileLoop cond -> do+        case find ((== cond) . paramName . fst) merge of+          Just (condparam, _) ->+            unless (paramType condparam == Prim Bool) $+              bad $+                TypeError $+                  "Conditional '" ++ pretty cond ++ "' of while-loop is not boolean, but "+                    ++ pretty (paramType condparam)+                    ++ "."+          Nothing ->+            bad $+              TypeError $+                "Conditional '" ++ pretty cond ++ "' of while-loop is not a merge variable."+        let funparams = mergepat+            paramts = map paramDeclType funparams+        checkFuncall Nothing paramts mergeargs++    let rettype = map paramDeclType mergepat+        consumable =+          [ (paramName param, mempty)+            | param <- mergepat,+              unique $ paramDeclType param+          ]++    context "Inside the loop body" $+      checkFun'+        ( nameFromString "<loop body>",+          staticShapes rettype,+          funParamsToNameInfos mergepat+        )+        consumable+        $ do+          checkFunParams mergepat+          checkBodyLore $ snd $ bodyDec loopbody++          checkStms (bodyStms loopbody) $ do+            checkResult $ bodyResult loopbody++            context "When matching result of body with loop parameters" $+              matchLoopResult (map fst ctxmerge) (map fst valmerge) $+                bodyResult loopbody++            let bound_here =+                  namesFromList $+                    M.keys $+                      scopeOf $ bodyStms loopbody+            map (`namesSubtract` bound_here)+              <$> mapM subExpAliasesM (bodyResult loopbody)+checkExp (Op op) = do+  checker <- asks envCheckOp+  checker op++checkSOACArrayArgs ::+  Checkable lore =>+  SubExp ->+  [VName] ->+  TypeM lore [Arg]+checkSOACArrayArgs width vs =+  forM vs $ \v -> do+    (vt, v') <- checkSOACArrayArg v+    let argSize = arraySize 0 vt+    unless (argSize == width) $+      bad $+        TypeError $+          "SOAC argument " ++ pretty v ++ " has outer size "+            ++ pretty argSize+            ++ ", but width of SOAC is "+            ++ pretty width+    return v'+  where+    checkSOACArrayArg ident = do+      (t, als) <- checkArg $ Var ident+      case peelArray 1 t of+        Nothing ->+          bad $+            TypeError $+              "SOAC argument " ++ pretty ident ++ " is not an array"+        Just rt -> return (t, (rt, als))++checkType ::+  Checkable lore =>+  TypeBase Shape u ->+  TypeM lore ()+checkType (Mem (ScalarSpace d _)) = mapM_ (require [Prim int64]) d+checkType t = mapM_ checkSubExp $ arrayDims t++checkExtType ::+  Checkable lore =>+  TypeBase ExtShape u ->+  TypeM lore ()+checkExtType = mapM_ checkExtDim . shapeDims . arrayShape+  where+    checkExtDim (Free se) = void $ checkSubExp se+    checkExtDim (Ext _) = return ()++checkCmpOp ::+  Checkable lore =>+  CmpOp ->+  SubExp ->+  SubExp ->+  TypeM lore ()+checkCmpOp (CmpEq t) x y = do+  require [Prim t] x+  require [Prim t] y+checkCmpOp (CmpUlt t) x y = checkBinOpArgs (IntType t) x y+checkCmpOp (CmpUle t) x y = checkBinOpArgs (IntType t) x y+checkCmpOp (CmpSlt t) x y = checkBinOpArgs (IntType t) x y+checkCmpOp (CmpSle t) x y = checkBinOpArgs (IntType t) x y+checkCmpOp (FCmpLt t) x y = checkBinOpArgs (FloatType t) x y+checkCmpOp (FCmpLe t) x y = checkBinOpArgs (FloatType t) x y+checkCmpOp CmpLlt x y = checkBinOpArgs Bool x y+checkCmpOp CmpLle x y = checkBinOpArgs Bool x y++checkBinOpArgs ::+  Checkable lore =>+  PrimType ->+  SubExp ->+  SubExp ->+  TypeM lore ()+checkBinOpArgs t e1 e2 = do+  require [Prim t] e1+  require [Prim t] e2++checkPatElem ::+  Checkable lore =>+  PatElemT (LetDec lore) ->+  TypeM lore ()+checkPatElem (PatElem name dec) =+  context ("When checking pattern element " ++ pretty name) $+    checkLetBoundLore name dec++checkDimIndex ::+  Checkable lore =>+  DimIndex SubExp ->+  TypeM lore ()+checkDimIndex (DimFix i) = require [Prim int64] i+checkDimIndex (DimSlice i n s) = mapM_ (require [Prim int64]) [i, n, s]++checkStm ::+  Checkable lore =>+  Stm (Aliases lore) ->+  TypeM lore a ->+  TypeM lore a+checkStm stm@(Let pat (StmAux (Certificates cs) _ (_, dec)) e) m = do+  context "When checking certificates" $ mapM_ (requireI [Prim Cert]) cs+  context "When checking expression annotation" $ checkExpLore dec+  context ("When matching\n" ++ message "  " pat ++ "\nwith\n" ++ message "  " e) $+    matchPattern pat e+  binding (maybeWithoutAliases $ scopeOf stm) $ do+    mapM_ checkPatElem (patternElements $ removePatternAliases pat)+    m+  where+    -- FIXME: this is wrong.  However, the core language type system+    -- is not strong enough to fully capture the aliases we want (see+    -- issue #803).  Since we eventually inline everything anyway, and+    -- our intra-procedural alias analysis is much simpler and+    -- correct, I could not justify spending time on improving the+    -- inter-procedural alias analysis.  If we ever stop inlining+    -- everything, probably we need to go back and refine this.+    maybeWithoutAliases =+      case stmExp stm of+        Apply {} -> M.map withoutAliases+        _ -> id+    withoutAliases (LetName (_, ldec)) = LetName (mempty, ldec)+    withoutAliases info = info++matchExtPattern ::+  Checkable lore =>+  Pattern (Aliases lore) ->+  [ExtType] ->+  TypeM lore ()+matchExtPattern pat ts =+  unless (expExtTypesFromPattern pat == ts) $+    bad $ InvalidPatternError pat ts Nothing++matchExtReturnType ::+  Checkable lore =>+  [ExtType] ->+  Result ->+  TypeM lore ()+matchExtReturnType rettype res = do+  ts <- mapM subExpType res+  matchExtReturns rettype res ts++matchExtBranchType ::+  Checkable lore =>+  [ExtType] ->+  Body (Aliases lore) ->+  TypeM lore ()+matchExtBranchType rettype (Body _ stms res) = do+  ts <- extendedScope (traverse subExpType res) stmscope+  matchExtReturns rettype res ts+  where+    stmscope = scopeOf stms++matchExtReturns :: [ExtType] -> Result -> [Type] -> TypeM lore ()+matchExtReturns rettype res ts = do+  let problem :: TypeM lore a+      problem =+        bad $+          TypeError $+            unlines+              [ "Type annotation is",+                "  " ++ prettyTuple rettype,+                "But result returns type",+                "  " ++ prettyTuple ts+              ]++  let (ctx_res, val_res) = splitFromEnd (length rettype) res+      (ctx_ts, val_ts) = splitFromEnd (length rettype) ts++  unless (length val_res == length rettype) problem++  let num_exts =+        length $+          S.fromList $+            concatMap (mapMaybe isExt . arrayExtDims) rettype+  unless (num_exts == length ctx_res) $+    bad $+      TypeError $+        "Number of context results does not match number of existentials in the return type.\n"+          ++ "Type:\n  "+          ++ prettyTuple rettype+          ++ "\ncannot match context parameters:\n  "+          ++ prettyTuple ctx_res++  let ctx_vals = zip ctx_res ctx_ts+      instantiateExt i = case maybeNth i ctx_vals of+        Just (se, Prim (IntType Int64)) -> return se+        _ -> problem++  rettype' <- instantiateShapes instantiateExt rettype++  unless (rettype' == val_ts) problem++validApply ::+  ArrayShape shape =>+  [TypeBase shape Uniqueness] ->+  [TypeBase shape NoUniqueness] ->+  Bool+validApply expected got =+  length got == length expected+    && and+      ( zipWith+          subtypeOf+          (map rankShaped got)+          (map (fromDecl . rankShaped) expected)+      )++type Arg = (Type, Names)++argType :: Arg -> Type+argType (t, _) = t++-- | Remove all aliases from the 'Arg'.+argAliases :: Arg -> Names+argAliases (_, als) = als++noArgAliases :: Arg -> Arg+noArgAliases (t, _) = (t, mempty)++checkArg ::+  Checkable lore =>+  SubExp ->+  TypeM lore Arg+checkArg arg = do+  argt <- checkSubExp arg+  als <- subExpAliasesM arg+  return (argt, als)++checkFuncall ::+  Maybe Name ->+  [DeclType] ->+  [Arg] ->+  TypeM lore ()+checkFuncall fname paramts args = do+  let argts = map argType args+  unless (validApply paramts argts) $+    bad $+      ParameterMismatch+        fname+        (map fromDecl paramts)+        $ map argType args+  forM_ (zip (map diet paramts) args) $ \(d, (_, als)) ->+    occur [consumption (consumeArg als d)]+  where+    consumeArg als Consume = als+    consumeArg _ _ = mempty++checkLambda ::+  Checkable lore =>+  Lambda (Aliases lore) ->+  [Arg] ->+  TypeM lore ()+checkLambda (Lambda params body rettype) args = do+  let fname = nameFromString "<anonymous>"+  if length params == length args+    then do+      checkFuncall+        Nothing+        (map ((`toDecl` Nonunique) . paramType) params)+        args+      let consumable = zip (map paramName params) (map argAliases args)+      checkFun'+        ( fname,+          staticShapes $ map (`toDecl` Nonunique) rettype,+          [ ( paramName param,+              LParamName $ paramDec param+            )+            | param <- params+          ]+        )+        consumable+        $ do+          checkLambdaParams params+          mapM_ checkType rettype+          checkLambdaBody rettype body+    else bad $ TypeError $ "Anonymous function defined with " ++ show (length params) ++ " parameters, but expected to take " ++ show (length args) ++ " arguments."++checkPrimExp :: Checkable lore => PrimExp VName -> TypeM lore ()+checkPrimExp ValueExp {} = return ()+checkPrimExp (LeafExp v pt) = requireI [Prim pt] v+checkPrimExp (BinOpExp op x y) = do+  requirePrimExp (binOpType op) x+  requirePrimExp (binOpType op) y+checkPrimExp (CmpOpExp op x y) = do+  requirePrimExp (cmpOpType op) x+  requirePrimExp (cmpOpType op) y+checkPrimExp (UnOpExp op x) = requirePrimExp (unOpType op) x+checkPrimExp (ConvOpExp op x) = requirePrimExp (fst $ convOpType op) x+checkPrimExp (FunExp h args t) = do+  (h_ts, h_ret, _) <-+    maybe+      (bad $ TypeError $ "Unknown function: " ++ h)+      return+      $ M.lookup h primFuns+  when (length h_ts /= length args) $+    bad $+      TypeError $+        "Function expects " ++ show (length h_ts)+          ++ " parameters, but given "+          ++ show (length args)+          ++ " arguments."+  when (h_ret /= t) $+    bad $+      TypeError $+        "Function return annotation is " ++ pretty t+          ++ ", but expected "+          ++ pretty h_ret+  zipWithM_ requirePrimExp h_ts args++requirePrimExp :: Checkable lore => PrimType -> PrimExp VName -> TypeM lore ()+requirePrimExp t e = context ("in PrimExp " ++ pretty e) $ do+  checkPrimExp e+  unless (primExpType e == t) $+    bad $+      TypeError $+        pretty e ++ " must have type " ++ pretty t++class ASTLore lore => CheckableOp lore where+  checkOp :: OpWithAliases (Op lore) -> TypeM lore ()+  -- ^ Used at top level; can be locally changed with 'checkOpWith'.++-- | The class of lores that can be type-checked.+class (ASTLore lore, CanBeAliased (Op lore), CheckableOp lore) => Checkable lore where+  checkExpLore :: ExpDec lore -> TypeM lore ()+  checkBodyLore :: BodyDec lore -> TypeM lore ()+  checkFParamLore :: VName -> FParamInfo lore -> TypeM lore ()+  checkLParamLore :: VName -> LParamInfo lore -> TypeM lore ()+  checkLetBoundLore :: VName -> LetDec lore -> TypeM lore ()+  checkRetType :: [RetType lore] -> TypeM lore ()+  matchPattern :: Pattern (Aliases lore) -> Exp (Aliases lore) -> TypeM lore ()+  primFParam :: VName -> PrimType -> TypeM lore (FParam (Aliases lore))+  matchReturnType :: [RetType lore] -> Result -> TypeM lore ()+  matchBranchType :: [BranchType lore] -> Body (Aliases lore) -> TypeM lore ()+  matchLoopResult ::+    [FParam (Aliases lore)] ->+    [FParam (Aliases lore)] ->+    [SubExp] ->+    TypeM lore ()++  default checkExpLore :: ExpDec lore ~ () => ExpDec lore -> TypeM lore ()+  checkExpLore = return++  default checkBodyLore :: BodyDec lore ~ () => BodyDec lore -> TypeM lore ()+  checkBodyLore = return++  default checkFParamLore :: FParamInfo lore ~ DeclType => VName -> FParamInfo lore -> TypeM lore ()+  checkFParamLore _ = checkType++  default checkLParamLore :: LParamInfo lore ~ Type => VName -> LParamInfo lore -> TypeM lore ()+  checkLParamLore _ = checkType++  default checkLetBoundLore :: LetDec lore ~ Type => VName -> LetDec lore -> TypeM lore ()+  checkLetBoundLore _ = checkType++  default checkRetType :: RetType lore ~ DeclExtType => [RetType lore] -> TypeM lore ()+  checkRetType = mapM_ $ checkExtType . declExtTypeOf++  default matchPattern :: Pattern (Aliases lore) -> Exp (Aliases lore) -> TypeM lore ()+  matchPattern pat = matchExtPattern pat <=< expExtType++  default primFParam :: FParamInfo lore ~ DeclType => VName -> PrimType -> TypeM lore (FParam (Aliases lore))+  primFParam name t = return $ Param name (Prim t)++  default matchReturnType :: RetType lore ~ DeclExtType => [RetType lore] -> Result -> TypeM lore ()+  matchReturnType = matchExtReturnType . map fromDecl++  default matchBranchType :: BranchType lore ~ ExtType => [BranchType lore] -> Body (Aliases lore) -> TypeM lore ()+  matchBranchType = matchExtBranchType++  default matchLoopResult ::+    FParamInfo lore ~ DeclType =>+    [FParam (Aliases lore)] ->+    [FParam (Aliases lore)] ->+    [SubExp] ->+    TypeM lore ()   matchLoopResult = matchLoopResultExt
src/Futhark/Util.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE Trustworthy #-}+ -- | Non-Futhark-specific utilities.  If you find yourself writing -- general functions on generic data structures, consider putting them -- here.@@ -8,66 +9,77 @@ -- note where you got it from (and make sure that the license is -- compatible). module Futhark.Util-       (mapAccumLM,-        maxinum,-        chunk,-        chunks,-        dropAt,-        takeLast,-        dropLast,-        mapEither,-        maybeNth,-        maybeHead,-        splitFromEnd,-        splitAt3,-        focusNth,-        unixEnvironment,-        isEnvVarSet,-        fancyTerminal,-        runProgramWithExitCode,-        directoryContents,-        roundFloat, ceilFloat, floorFloat,-        roundDouble, ceilDouble, floorDouble,-        lgamma, lgammaf, tgamma, tgammaf,-        fromPOSIX,-        toPOSIX,-        trim,-        pmapIO,-        UserString,-        EncodedString,-        zEncodeString-       )-       where+  ( mapAccumLM,+    maxinum,+    chunk,+    chunks,+    dropAt,+    takeLast,+    dropLast,+    mapEither,+    maybeNth,+    maybeHead,+    splitFromEnd,+    splitAt3,+    focusNth,+    unixEnvironment,+    isEnvVarSet,+    fancyTerminal,+    runProgramWithExitCode,+    directoryContents,+    roundFloat,+    ceilFloat,+    floorFloat,+    roundDouble,+    ceilDouble,+    floorDouble,+    lgamma,+    lgammaf,+    tgamma,+    tgammaf,+    fromPOSIX,+    toPOSIX,+    trim,+    pmapIO,+    UserString,+    EncodedString,+    zEncodeString,+  )+where -import Numeric import Control.Concurrent import Control.Exception import Control.Monad import qualified Data.ByteString as BS-import qualified Data.Text as T-import qualified Data.Text.Encoding as T-import qualified Data.Text.Encoding.Error as T import Data.Char-import Data.List (foldl', genericDrop, genericSplitAt) import Data.Either+import Data.List (foldl', genericDrop, genericSplitAt) import Data.Maybe+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.Encoding.Error as T+import Numeric+import qualified System.Directory.Tree as Dir import System.Environment+import System.Exit+import qualified System.FilePath as Native+import qualified System.FilePath.Posix as Posix import System.IO (hIsTerminalDevice, stdout) import System.IO.Unsafe-import qualified System.Directory.Tree as Dir import System.Process.ByteString-import System.Exit-import qualified System.FilePath.Posix as Posix-import qualified System.FilePath as Native  -- | Like 'Data.Traversable.mapAccumL', but monadic.-mapAccumLM :: Monad m =>-              (acc -> x -> m (acc, y)) -> acc -> [x] -> m (acc, [y])+mapAccumLM ::+  Monad m =>+  (acc -> x -> m (acc, y)) ->+  acc ->+  [x] ->+  m (acc, [y]) mapAccumLM _ acc [] = return (acc, [])-mapAccumLM f acc (x:xs) = do+mapAccumLM f acc (x : xs) = do   (acc', x') <- f acc x   (acc'', xs') <- mapAccumLM f acc' xs-  return (acc'', x':xs')+  return (acc'', x' : xs')  -- | @chunk n a@ splits @a@ into @n@-size-chunks.  If the length of -- @a@ is not divisible by @n@, the last chunk will have fewer than@@ -75,17 +87,17 @@ chunk :: Int -> [a] -> [[a]] chunk _ [] = [] chunk n xs =-  let (bef,aft) = splitAt n xs-  in bef : chunk n aft+  let (bef, aft) = splitAt n xs+   in bef : chunk n aft  -- | @chunks ns a@ splits @a@ into chunks determined by the elements -- of @ns@.  It must hold that @sum ns == length a@, or the resulting -- list may contain too few chunks, or not all elements of @a@. chunks :: [Int] -> [a] -> [[a]] chunks [] _ = []-chunks (n:ns) xs =-  let (bef,aft) = splitAt n xs-  in bef : chunks ns aft+chunks (n : ns) xs =+  let (bef, aft) = splitAt n xs+   in bef : chunks ns aft  -- | Like 'maximum', but returns zero for an empty list. maxinum :: (Num a, Ord a, Foldable f) => f a -> a@@ -93,7 +105,7 @@  -- | @dropAt i n@ drops @n@ elements starting at element @i@. dropAt :: Int -> Int -> [a] -> [a]-dropAt i n xs = take i xs ++ drop (i+n) xs+dropAt i n xs = take i xs ++ drop (i + n) xs  -- | @takeLast n l@ takes the last @n@ elements of @l@. takeLast :: Int -> [a] -> [a]@@ -110,13 +122,13 @@ -- | Return the list element at the given index, if the index is valid. maybeNth :: Integral int => int -> [a] -> Maybe a maybeNth i l-  | i >= 0, v:_ <- genericDrop i l = Just v-  | otherwise                      = Nothing+  | i >= 0, v : _ <- genericDrop i l = Just v+  | otherwise = Nothing  -- | Return the first element of the list, if it exists. maybeHead :: [a] -> Maybe a maybeHead [] = Nothing-maybeHead (x:_) = Just x+maybeHead (x : _) = Just x  -- | Like 'splitAt', but from the end. splitFromEnd :: Int -> [a] -> ([a], [a])@@ -127,18 +139,19 @@ splitAt3 n m l =   let (xs, l') = splitAt n l       (ys, zs) = splitAt m l'-  in (xs, ys, zs)+   in (xs, ys, zs)  -- | Return the list element at the given index, if the index is -- valid, along with the elements before and after. focusNth :: Integral int => int -> [a] -> Maybe ([a], a, [a]) focusNth i xs-  | (bef, x:aft) <- genericSplitAt i xs = Just (bef, x, aft)-  | otherwise                           = Nothing+  | (bef, x : aft) <- genericSplitAt i xs = Just (bef, x, aft)+  | otherwise = Nothing  {-# NOINLINE unixEnvironment #-}+ -- | The Unix environment when the Futhark compiler started.-unixEnvironment :: [(String,String)]+unixEnvironment :: [(String, String)] unixEnvironment = unsafePerformIO getEnvironment  -- | Is an environment variable set to 0 or 1?  If 0, return False; if@@ -152,6 +165,7 @@     _ -> Nothing  {-# NOINLINE fancyTerminal #-}+ -- | Are we running in a terminal capable of fancy commands and -- visualisation? fancyTerminal :: Bool@@ -163,14 +177,18 @@ -- | Like 'readProcessWithExitCode', but also wraps exceptions when -- the indicated binary cannot be launched, or some other exception is -- thrown.  Also does shenanigans to handle improperly encoded outputs.-runProgramWithExitCode :: FilePath -> [String] -> BS.ByteString-                       -> IO (Either IOException (ExitCode, String, String))+runProgramWithExitCode ::+  FilePath ->+  [String] ->+  BS.ByteString ->+  IO (Either IOException (ExitCode, String, String)) runProgramWithExitCode exe args inp =   (Right . postprocess <$> readProcessWithExitCode exe args inp)-  `catch` \e -> return (Left e)-  where decode = T.unpack . T.decodeUtf8With T.lenientDecode-        postprocess (code, out, err) =-          (code, decode out, decode err)+    `catch` \e -> return (Left e)+  where+    decode = T.unpack . T.decodeUtf8With T.lenientDecode+    postprocess (code, out, err) =+      (code, decode out, decode err)  -- | Every non-directory file contained in a directory tree. directoryContents :: FilePath -> IO [FilePath]@@ -179,14 +197,20 @@   case Dir.failures tree of     Dir.Failed _ err : _ -> throw err     _ -> return $ mapMaybe isFile $ Dir.flattenDir tree-  where isFile (Dir.File _ path) = Just path-        isFile _                 = Nothing+  where+    isFile (Dir.File _ path) = Just path+    isFile _ = Nothing  foreign import ccall "nearbyint" c_nearbyint :: Double -> Double+ foreign import ccall "nearbyintf" c_nearbyintf :: Float -> Float+ foreign import ccall "ceil" c_ceil :: Double -> Double+ foreign import ccall "ceilf" c_ceilf :: Float -> Float+ foreign import ccall "floor" c_floor :: Double -> Double+ foreign import ccall "floorf" c_floorf :: Float -> Float  -- | Round a single-precision floating point number correctly.@@ -214,8 +238,11 @@ floorDouble = c_floor  foreign import ccall "lgamma" c_lgamma :: Double -> Double+ foreign import ccall "lgammaf" c_lgammaf :: Float -> Float+ foreign import ccall "tgamma" c_tgamma :: Double -> Double+ foreign import ccall "tgammaf" c_tgammaf :: Float -> Float  -- | The system-level @lgamma()@ function.@@ -250,10 +277,13 @@ trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace  fork :: (a -> IO b) -> a -> IO (MVar b)-fork f x = do cell <- newEmptyMVar-              void $ forkIO $ do result <- f x-                                 putMVar cell result-              return cell+fork f x = do+  cell <- newEmptyMVar+  void $+    forkIO $ do+      result <- f x+      putMVar cell result+  return cell  -- | Run various 'IO' actions concurrently, possibly with a bound on -- the number of threads.@@ -263,8 +293,8 @@     go [] res = return res     go xs res = do       numThreads <- maybe getNumCapabilities pure concurrency-      let (e,es) = splitAt numThreads xs-      mvars  <- mapM (fork f') e+      let (e, es) = splitAt numThreads xs+      mvars <- mapM (fork f') e       result <- mapM takeMVar mvars       case sequence result of         Left err -> throw (err :: SomeException)@@ -290,56 +320,61 @@ -- programming languages. zEncodeString :: UserString -> EncodedString zEncodeString "" = ""-zEncodeString (c:cs) = encodeDigitChar c ++ concatMap encodeChar cs+zEncodeString (c : cs) = encodeDigitChar c ++ concatMap encodeChar cs -unencodedChar :: Char -> Bool   -- True for chars that don't need encoding+unencodedChar :: Char -> Bool -- True for chars that don't need encoding unencodedChar 'Z' = False unencodedChar 'z' = False unencodedChar '_' = True-unencodedChar c   =  isAsciiLower c-                  || isAsciiUpper c-                  || isDigit c+unencodedChar c =+  isAsciiLower c+    || isAsciiUpper c+    || isDigit c  -- If a digit is at the start of a symbol then we need to encode it. -- Otherwise names like 9pH-0.1 give linker errors. encodeDigitChar :: Char -> EncodedString-encodeDigitChar c | isDigit c = encodeAsUnicodeCharar c-                  | otherwise = encodeChar c+encodeDigitChar c+  | isDigit c = encodeAsUnicodeCharar c+  | otherwise = encodeChar c  encodeChar :: Char -> EncodedString-encodeChar c | unencodedChar c = [c]     -- Common case first+encodeChar c | unencodedChar c = [c] -- Common case first  -- Constructors-encodeChar '('  = "ZL"   -- Needed for things like (,), and (->)-encodeChar ')'  = "ZR"   -- For symmetry with (-encodeChar '['  = "ZM"-encodeChar ']'  = "ZN"-encodeChar ':'  = "ZC"-encodeChar 'Z'  = "ZZ"-+encodeChar '(' = "ZL" -- Needed for things like (,), and (->)+encodeChar ')' = "ZR" -- For symmetry with (+encodeChar '[' = "ZM"+encodeChar ']' = "ZN"+encodeChar ':' = "ZC"+encodeChar 'Z' = "ZZ" -- Variables-encodeChar 'z'  = "zz"-encodeChar '&'  = "za"-encodeChar '|'  = "zb"-encodeChar '^'  = "zc"-encodeChar '$'  = "zd"-encodeChar '='  = "ze"-encodeChar '>'  = "zg"-encodeChar '#'  = "zh"-encodeChar '.'  = "zi"-encodeChar '<'  = "zl"-encodeChar '-'  = "zm"-encodeChar '!'  = "zn"-encodeChar '+'  = "zp"+encodeChar 'z' = "zz"+encodeChar '&' = "za"+encodeChar '|' = "zb"+encodeChar '^' = "zc"+encodeChar '$' = "zd"+encodeChar '=' = "ze"+encodeChar '>' = "zg"+encodeChar '#' = "zh"+encodeChar '.' = "zi"+encodeChar '<' = "zl"+encodeChar '-' = "zm"+encodeChar '!' = "zn"+encodeChar '+' = "zp" encodeChar '\'' = "zq" encodeChar '\\' = "zr"-encodeChar '/'  = "zs"-encodeChar '*'  = "zt"-encodeChar '_'  = "zu"-encodeChar '%'  = "zv"-encodeChar c    = encodeAsUnicodeCharar c+encodeChar '/' = "zs"+encodeChar '*' = "zt"+encodeChar '_' = "zu"+encodeChar '%' = "zv"+encodeChar c = encodeAsUnicodeCharar c  encodeAsUnicodeCharar :: Char -> EncodedString-encodeAsUnicodeCharar c = 'z' : if isDigit (head hex_str) then hex_str-                                                           else '0':hex_str-  where hex_str = showHex (ord c) "U"+encodeAsUnicodeCharar c =+  'z' :+  if isDigit (head hex_str)+    then hex_str+    else '0' : hex_str+  where+    hex_str = showHex (ord c) "U"
src/Futhark/Util/Console.hs view
@@ -1,11 +1,11 @@ -- | Some utility functions for working with pretty console output. module Futhark.Util.Console-       ( color-       , inRed-       , inGreen-       , inBold-       )-       where+  ( color,+    inRed,+    inGreen,+    inBold,+  )+where  import System.Console.ANSI 
src/Futhark/Util/IntegralExp.hs view
@@ -15,10 +15,10 @@ -- typeclasses that have been modified to make generic functions -- slightly easier to write. module Futhark.Util.IntegralExp-       ( IntegralExp (..)-       , Wrapped (..)-       )-       where+  ( IntegralExp (..),+    Wrapped (..),+  )+where  import Data.Int import Prelude@@ -38,22 +38,22 @@   divUp x y =     (x + y - 1) `Futhark.Util.IntegralExp.div` y -  fromInt8  :: Int8 -> e-  fromInt16 :: Int16 -> e-  fromInt32 :: Int32 -> e-  fromInt64 :: Int64 -> e- -- | This wrapper allows you to use a type that is an instance of the -- true class whenever the simile class is required.-newtype Wrapped a = Wrapped { wrappedValue :: a }-                  deriving (Eq, Ord, Show)+newtype Wrapped a = Wrapped {wrappedValue :: a}+  deriving (Eq, Ord, Show) -liftOp :: (a -> a)-        -> Wrapped a -> Wrapped a+liftOp ::+  (a -> a) ->+  Wrapped a ->+  Wrapped a liftOp op (Wrapped x) = Wrapped $ op x -liftOp2 :: (a -> a -> a)-        -> Wrapped a -> Wrapped a -> Wrapped a+liftOp2 ::+  (a -> a -> a) ->+  Wrapped a ->+  Wrapped a ->+  Wrapped a liftOp2 op (Wrapped x) (Wrapped y) = Wrapped $ x `op` y  instance Num a => Num (Wrapped a) where@@ -71,8 +71,3 @@   div = liftOp2 Prelude.div   mod = liftOp2 Prelude.mod   sgn = Just . fromIntegral . signum . toInteger . wrappedValue--  fromInt8  = fromInteger . toInteger-  fromInt16 = fromInteger . toInteger-  fromInt32 = fromInteger . toInteger-  fromInt64 = fromInteger . toInteger
src/Futhark/Util/Loc.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE Trustworthy #-}+ -- | A Safe Haskell-trusted re-export of the @srcloc@ package.-module Futhark.Util.Loc ( module Data.Loc ) where+module Futhark.Util.Loc (module Data.Loc) where  import Data.Loc
src/Futhark/Util/Log.hs view
@@ -1,25 +1,25 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-}+ -- | Opaque type for an operations log that provides fast O(1) -- appends. module Futhark.Util.Log-       ( Log-       , toText-       , ToLog (..)-       , MonadLogger (..)-       )-+  ( Log,+    toText,+    ToLog (..),+    MonadLogger (..),+  ) where -import Control.Monad.Writer-import qualified Control.Monad.RWS.Strict import qualified Control.Monad.RWS.Lazy-import qualified Data.Text as T+import qualified Control.Monad.RWS.Strict+import Control.Monad.Writer import qualified Data.DList as DL+import qualified Data.Text as T  -- | An efficiently catenable sequence of log entries.-newtype Log = Log { unLog :: DL.DList T.Text }+newtype Log = Log {unLog :: DL.DList T.Text}  instance Semigroup Log where   Log l1 <> Log l2 = Log $ l1 <> l2
src/Futhark/Util/Options.hs view
@@ -1,16 +1,16 @@ -- | Common code for parsing command line options based on getopt. module Futhark.Util.Options-       ( FunOptDescr-       , mainWithOptions-       , commonOptions-       ) where+  ( FunOptDescr,+    mainWithOptions,+    commonOptions,+  )+where  import Control.Monad.IO.Class-import System.IO-import System.Exit-import System.Console.GetOpt- import Futhark.Version+import System.Console.GetOpt+import System.Exit+import System.IO  -- | A command line option that either purely updates a configuration, -- or performs an IO action (and stops).@@ -18,30 +18,34 @@  -- | Generate a main action that parses the given command line options -- (while always adding 'commonOptions').-mainWithOptions :: cfg-                -> [FunOptDescr cfg]-                -> String-                -> ([String] -> cfg -> Maybe (IO ()))-                -> String-                -> [String]-                -> IO ()+mainWithOptions ::+  cfg ->+  [FunOptDescr cfg] ->+  String ->+  ([String] -> cfg -> Maybe (IO ())) ->+  String ->+  [String] ->+  IO () mainWithOptions emptyConfig commandLineOptions usage f prog args =   case getOpt' Permute commandLineOptions' args of     (opts, nonopts, [], []) ->       case applyOpts opts of         Right config           | Just m <- f nonopts config -> m-          | otherwise                  -> invalid nonopts [] []-        Left m       -> m+          | otherwise -> invalid nonopts [] []+        Left m -> m     (_, nonopts, unrecs, errs) -> invalid nonopts unrecs errs-  where applyOpts opts = do fs <- sequence opts-                            return $ foldl (.) id (reverse fs) emptyConfig+  where+    applyOpts opts = do+      fs <- sequence opts+      return $ foldl (.) id (reverse fs) emptyConfig -        invalid nonopts unrecs errs = do help <- helpStr prog usage commandLineOptions'-                                         badOptions help nonopts errs unrecs+    invalid nonopts unrecs errs = do+      help <- helpStr prog usage commandLineOptions'+      badOptions help nonopts errs unrecs -        commandLineOptions' =-          commonOptions prog usage commandLineOptions ++ commandLineOptions+    commandLineOptions' =+      commonOptions prog usage commandLineOptions ++ commandLineOptions  helpStr :: String -> String -> [OptDescr a] -> IO String helpStr prog usage opts = do@@ -63,20 +67,30 @@ -- options. commonOptions :: String -> String -> [FunOptDescr cfg] -> [FunOptDescr cfg] commonOptions prog usage options =-  [ Option "V" ["version"]-    (NoArg $ Left $ do header-                       exitSuccess)-    "Print version information and exit."--  , Option "h" ["help"]-    (NoArg $ Left $ do header-                       putStrLn ""-                       putStrLn =<< helpStr prog usage (commonOptions prog usage [] ++ options)-                       exitSuccess)-    "Print help and exit."+  [ Option+      "V"+      ["version"]+      ( NoArg $+          Left $ do+            header+            exitSuccess+      )+      "Print version information and exit.",+    Option+      "h"+      ["help"]+      ( NoArg $+          Left $ do+            header+            putStrLn ""+            putStrLn =<< helpStr prog usage (commonOptions prog usage [] ++ options)+            exitSuccess+      )+      "Print help and exit."   ]-  where header = do-          putStrLn $ "Futhark " ++ versionString-          putStrLn "Copyright (C) DIKU, University of Copenhagen, released under the ISC license."-          putStrLn "This is free software: you are free to change and redistribute it."-          putStrLn "There is NO WARRANTY, to the extent permitted by law."+  where+    header = do+      putStrLn $ "Futhark " ++ versionString+      putStrLn "Copyright (C) DIKU, University of Copenhagen, released under the ISC license."+      putStrLn "This is free software: you are free to change and redistribute it."+      putStrLn "There is NO WARRANTY, to the extent permitted by law."
src/Futhark/Util/Pretty.hs view
@@ -1,30 +1,29 @@ {-# LANGUAGE Trustworthy #-} {-# OPTIONS_GHC -fno-warn-orphans #-}+ -- | A re-export of the prettyprinting library, along with some convenience functions. module Futhark.Util.Pretty-       ( module Text.PrettyPrint.Mainland-       , module Text.PrettyPrint.Mainland.Class-       , pretty-       , prettyDoc-       , prettyTuple-       , prettyText-       , prettyOneLine--       , apply-       , oneLine-       , annot-       , nestedBlock-       , textwrap-       , shorten-       )-       where+  ( module Text.PrettyPrint.Mainland,+    module Text.PrettyPrint.Mainland.Class,+    pretty,+    prettyDoc,+    prettyTuple,+    prettyText,+    prettyOneLine,+    apply,+    oneLine,+    annot,+    nestedBlock,+    textwrap,+    shorten,+  )+where  import Data.Text (Text) import qualified Data.Text.Lazy as LT- import Text.PrettyPrint.Mainland hiding (pretty)-import Text.PrettyPrint.Mainland.Class import qualified Text.PrettyPrint.Mainland as PP+import Text.PrettyPrint.Mainland.Class  -- | Prettyprint a value, wrapped to 80 characters. pretty :: Pretty a => a -> String@@ -36,7 +35,7 @@  -- | Prettyprint a value without any width restriction. prettyOneLine :: Pretty a => a -> String-prettyOneLine = ($"") . displayS . renderCompact . oneLine . ppr+prettyOneLine = ($ "") . displayS . renderCompact . oneLine . ppr  -- | Re-export of 'PP.pretty'. prettyDoc :: Int -> Doc -> String@@ -72,14 +71,17 @@ -- | Surround the given document with enclosers and add linebreaks and -- indents. nestedBlock :: String -> String -> Doc -> Doc-nestedBlock pre post body = text pre </>-                            PP.indent 2 body </>-                            text post+nestedBlock pre post body =+  text pre+    </> PP.indent 2 body+    </> text post  -- | Prettyprint on a single line up to at most some appropriate -- number of characters, with trailing ... if necessary.  Used for -- error messages. shorten :: Pretty a => a -> Doc-shorten a | length s > 70 = text (take 70 s) <> text "..."-          | otherwise = text s-  where s = pretty a+shorten a+  | length s > 70 = text (take 70 s) <> text "..."+  | otherwise = text s+  where+    s = pretty a
src/Futhark/Util/Table.hs view
@@ -1,15 +1,15 @@ -- | Basic table building for prettier futhark-test output. module Futhark.Util.Table-     ( buildTable-     , mkEntry-     , Entry-     ) where+  ( buildTable,+    mkEntry,+    Entry,+  )+where  import Data.List (intercalate, transpose)-import System.Console.ANSI- import Futhark.Util (maxinum) import Futhark.Util.Console (color)+import System.Console.ANSI  data RowTemplate = RowTemplate [Int] Int deriving (Show) @@ -22,32 +22,35 @@ mkEntry s = (s, [])  buildRowTemplate :: [[Entry]] -> Int -> RowTemplate- buildRowTemplate rows = RowTemplate widths-  where widths = map (maxinum . map (length . fst)) . transpose $ rows+  where+    widths = map (maxinum . map (length . fst)) . transpose $ rows  buildRow :: RowTemplate -> [Entry] -> String buildRow (RowTemplate widths pad) entries = cells ++ "\n"-  where bar   = "\x2502"-        cells = concatMap buildCell (zip entries widths) ++ bar-        buildCell ((entry, sgr), width) =-          let padding = width - length entry + pad-          in  bar ++ " " ++ color sgr entry ++ replicate padding ' '+  where+    bar = "\x2502"+    cells = concatMap buildCell (zip entries widths) ++ bar+    buildCell ((entry, sgr), width) =+      let padding = width - length entry + pad+       in bar ++ " " ++ color sgr entry ++ replicate padding ' '  buildSep :: Char -> Char -> Char -> RowTemplate -> String buildSep lCorner rCorner sep (RowTemplate widths pad) =   corners . concatMap cellFloor $ widths-  where cellFloor width = replicate (width + pad + 1) '\x2500' ++ [sep]-        corners [] = ""-        corners s  = [lCorner] ++ init s ++ [rCorner]+  where+    cellFloor width = replicate (width + pad + 1) '\x2500' ++ [sep]+    corners [] = ""+    corners s = [lCorner] ++ init s ++ [rCorner]  -- | Builds a table from a list of entries and a padding amount that -- determines padding from the right side of the widest entry in each column. buildTable :: [[Entry]] -> Int -> String buildTable rows pad = buildTop template ++ sepRows ++ buildBottom template-  where sepRows       = intercalate (buildFloor template) builtRows-        builtRows     = map (buildRow template) rows-        template      = buildRowTemplate rows pad-        buildTop rt   = buildSep '\x250C' '\x2510' '\x252C' rt ++ "\n"-        buildFloor rt = buildSep '\x251C' '\x2524' '\x253C' rt ++ "\n"-        buildBottom   = buildSep '\x2514' '\x2518' '\x2534'+  where+    sepRows = intercalate (buildFloor template) builtRows+    builtRows = map (buildRow template) rows+    template = buildRowTemplate rows pad+    buildTop rt = buildSep '\x250C' '\x2510' '\x252C' rt ++ "\n"+    buildFloor rt = buildSep '\x251C' '\x2524' '\x253C' rt ++ "\n"+    buildBottom = buildSep '\x2514' '\x2518' '\x2534'
src/Futhark/Version.hs view
@@ -1,17 +1,16 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE Trustworthy #-}+ -- | This module exports version information about the Futhark -- compiler. module Futhark.Version-       (-         version-       , versionString-       )-       where+  ( version,+    versionString,+  )+where  import Data.Version import Development.GitRev- import qualified Paths_futhark  -- | The version of Futhark that we are using.  This is equivalent to@@ -21,20 +20,27 @@  -- | The version of Futhark that we are using, as a 'String' versionString :: String-versionString = showVersion version ++-                if used_hash /= "UNKNOWN"-                then "\n" ++ gitversion-                else ""+versionString =+  showVersion version+    ++ if used_hash /= "UNKNOWN"+      then "\n" ++ gitversion+      else ""   where     used_hash = take 7 $(gitHash) -    gitversion = concat ["git: "-                        , branch-                        , used_hash-                        , " (", $(gitCommitDate), ")"-                        , dirty-                        ]-    branch | $(gitBranch) == "master" = ""-           | otherwise = $(gitBranch) ++ " @ "-    dirty | $(gitDirtyTracked) = " [modified]"-          | otherwise   = ""+    gitversion =+      concat+        [ "git: ",+          branch,+          used_hash,+          " (",+          $(gitCommitDate),+          ")",+          dirty+        ]+    branch+      | $(gitBranch) == "master" = ""+      | otherwise = $(gitBranch) ++ " @ "+    dirty+      | $(gitDirtyTracked) = " [modified]"+      | otherwise = ""
src/Language/Futhark.hs view
@@ -1,22 +1,35 @@ {-# LANGUAGE Safe #-}+ -- | Re-export the external Futhark modules for convenience. module Language.Futhark-  ( module Language.Futhark.Syntax-  , module Language.Futhark.Prop-  , module Language.Futhark.Pretty--  , Ident, DimIndex, Exp, Pattern-  , ModExp, ModParam, SigExp, ModBind, SigBind-  , ValBind, Dec, Spec, Prog-  , TypeBind, TypeDecl-  , StructTypeArg, ScalarType-  , TypeParam, Case+  ( module Language.Futhark.Syntax,+    module Language.Futhark.Prop,+    module Language.Futhark.Pretty,+    Ident,+    DimIndex,+    Exp,+    Pattern,+    ModExp,+    ModParam,+    SigExp,+    ModBind,+    SigBind,+    ValBind,+    Dec,+    Spec,+    Prog,+    TypeBind,+    TypeDecl,+    StructTypeArg,+    ScalarType,+    TypeParam,+    Case,   )-  where+where -import Language.Futhark.Syntax-import Language.Futhark.Prop import Language.Futhark.Pretty+import Language.Futhark.Prop+import Language.Futhark.Syntax  -- | An identifier with type- and aliasing information. type Ident = IdentBase Info VName
src/Language/Futhark/Core.hs view
@@ -1,61 +1,84 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Trustworthy #-}+ -- | This module contains very basic definitions for Futhark - so basic, -- that they can be shared between the internal and external -- representation. module Language.Futhark.Core-  ( Uniqueness(..)-  , Commutativity(..)+  ( Uniqueness (..),+    Commutativity (..), -  -- * Location utilities-  , SrcLoc-  , Loc-  , Located(..)-  , srclocOf-  , locStr-  , locStrRel-  , prettyStacktrace+    -- * Location utilities+    SrcLoc,+    Loc,+    Located (..),+    srclocOf,+    locStr,+    locStrRel,+    prettyStacktrace, -  -- * Name handling-  , Name-  , nameToString-  , nameFromString-  , nameToText-  , nameFromText-  , VName(..)-  , baseTag-  , baseName-  , baseString-  , pretty-  , quote-  , pquote+    -- * Name handling+    Name,+    nameToString,+    nameFromString,+    nameToText,+    nameFromText,+    VName (..),+    baseTag,+    baseName,+    baseString,+    pretty,+    quote,+    pquote, -  -- * Special identifiers-  , defaultEntryPoint+    -- * Special identifiers+    defaultEntryPoint,      -- * Integer re-export-  , Int8, Int16, Int32, Int64-  , Word8, Word16, Word32, Word64+    Int8,+    Int16,+    Int32,+    Int64,+    Word8,+    Word16,+    Word32,+    Word64,   )- where -import Data.Int (Int8, Int16, Int32, Int64)+import Control.Category+import Data.Int (Int16, Int32, Int64, Int8) import Data.String-import Data.Word (Word8, Word16, Word32, Word64) import qualified Data.Text as T--import Futhark.Util.Pretty+import Data.Word (Word16, Word32, Word64, Word8) import Futhark.Util.Loc+import Futhark.Util.Pretty+import GHC.Generics+import Language.SexpGrammar as Sexp+import Language.SexpGrammar.Generic+import Text.Read+import Prelude hiding (id, (.))  -- | The uniqueness attribute of a type.  This essentially indicates -- whether or not in-place modifications are acceptable.  With respect -- to ordering, 'Unique' is greater than 'Nonunique'.-data Uniqueness = Nonunique -- ^ May have references outside current function.-                | Unique    -- ^ No references outside current function.-                  deriving (Eq, Ord, Show)+data Uniqueness+  = -- | May have references outside current function.+    Nonunique+  | -- | No references outside current function.+    Unique+  deriving (Eq, Ord, Show, Generic) +instance SexpIso Uniqueness where+  sexpIso =+    match $+      With (. Sexp.sym "nonunique") $+        With+          (. Sexp.sym "unique")+          End+ instance Semigroup Uniqueness where   (<>) = min @@ -68,10 +91,19 @@  -- | Whether some operator is commutative or not.  The 'Monoid' -- instance returns the least commutative of its arguments.-data Commutativity = Noncommutative-                   | Commutative-                     deriving (Eq, Ord, Show)+data Commutativity+  = Noncommutative+  | Commutative+  deriving (Eq, Ord, Show, Generic) +instance SexpIso Commutativity where+  sexpIso =+    match $+      With (. Sexp.sym "noncommutative") $+        With+          (. Sexp.sym "commutative")+          End+ instance Semigroup Commutativity where   (<>) = min @@ -86,8 +118,11 @@ -- compiler.  'String's, being lists of characters, are very slow, -- while 'T.Text's are based on byte-arrays. newtype Name = Name T.Text-  deriving (Show, Eq, Ord, IsString, Semigroup)+  deriving (Show, Eq, Ord, IsString, Semigroup, Generic) +instance SexpIso Name where+  sexpIso = with (. symbol)+ instance Pretty Name where   ppr = text . nameToString @@ -119,13 +154,13 @@   case locOf a of     NoLoc -> "unknown location"     Loc (Pos file line1 col1 _) (Pos _ line2 col2 _)-    -- Do not show line2 if it is identical to line1.+      -- Do not show line2 if it is identical to line1.       | line1 == line2 ->-          first_part ++ "-" ++ show col2+        first_part ++ "-" ++ show col2       | otherwise ->-          first_part ++ "-" ++ show line2 ++ ":" ++ show col2-      where first_part = file ++ ":" ++ show line1 ++ ":" ++ show col1-+        first_part ++ "-" ++ show line2 ++ ":" ++ show col2+      where+        first_part = file ++ ":" ++ show line1 ++ ":" ++ show col1  -- | Like 'locStr', but @locStrRel prev now@ prints the location @now@ -- with the file name left out if the same as @prev@.  This is useful@@ -138,10 +173,11 @@     (Loc (Pos a_file _ _ _) _, Loc (Pos b_file line1 col1 _) (Pos _ line2 col2 _))       | a_file == b_file,         line1 == line2 ->-          first_part ++ "-" ++ show col2+        first_part ++ "-" ++ show col2       | a_file == b_file ->-          first_part ++ "-" ++ show line2 ++ ":" ++ show col2-      where first_part = show line1 ++ ":" ++ show col1+        first_part ++ "-" ++ show line2 ++ ":" ++ show col2+      where+        first_part = show line1 ++ ":" ++ show col1     _ -> locStr b  -- | Given a list of strings representing entries in the stack trace@@ -150,18 +186,39 @@ -- should also be preceded by a newline.  The most recent stack frame -- must come first in the list. prettyStacktrace :: Int -> [String] -> String-prettyStacktrace cur = unlines . zipWith f [(0::Int)..]-  where -- Formatting hack: assume no stack is deeper than 100-        -- elements.  Since Futhark does not support recursion, going-        -- beyond that would require a truly perverse program.-        f i x = (if cur == i then "-> " else "   ") ++-                '#' : show i ++-                (if i > 9 then "" else " ") ++ " " ++ x+prettyStacktrace cur = unlines . zipWith f [(0 :: Int) ..]+  where+    -- Formatting hack: assume no stack is deeper than 100+    -- elements.  Since Futhark does not support recursion, going+    -- beyond that would require a truly perverse program.+    f i x =+      (if cur == i then "-> " else "   ")+        ++ '#' :+      show i+        ++ (if i > 9 then "" else " ")+        ++ " "+        ++ x  -- | A name tagged with some integer.  Only the integer is used in -- comparisons, no matter the type of @vn@. data VName = VName !Name !Int-  deriving (Show)+  deriving (Show, Generic)++instance SexpIso VName where+  sexpIso = with $ \vname ->+    Sexp.symbol+      >>> flipped+        ( pair+            >>> partialIso+              (\(nm, i) -> T.pack $ nameToString nm ++ "_" ++ show i)+              ( \s ->+                  let (nm, i) = T.breakOnEnd "_" s+                   in case readMaybe $ T.unpack i of+                        Just i' -> Right (nameFromString $ T.unpack $ T.init nm, i')+                        Nothing -> Left $ expected "Couldn't parse int of vname"+              )+        )+      >>> vname  -- | Return the tag contained in the 'VName'. baseTag :: VName -> Int
src/Language/Futhark/Interpreter.hs view
@@ -1,1535 +1,1713 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE DeriveTraversable #-}-{-# LANGUAGE OverloadedStrings #-}--- | An interpreter operating on type-checked source Futhark terms.--- Relatively slow.-module Language.Futhark.Interpreter-  ( Ctx(..)-  , Env-  , InterpreterError-  , initialCtx-  , interpretExp-  , interpretDec-  , interpretImport-  , interpretFunction-  , ExtOp(..)-  , BreakReason(..)-  , StackFrame(..)-  , typeCheckerEnv-  , Value (ValuePrim, ValueArray, ValueRecord)-  , fromTuple-  , isEmptyArray-  , prettyEmptyArray-  ) where--import Control.Monad.Trans.Maybe-import Control.Monad.Free.Church-import Control.Monad.Except-import Control.Monad.State-import Control.Monad.Reader-import Data.Array-import Data.Bifunctor (first)-import Data.List-  (transpose, genericLength, isPrefixOf, foldl', find, intercalate)-import Data.Maybe-import qualified Data.Map as M-import qualified Data.List.NonEmpty as NE-import Data.Monoid hiding (Sum)--import Language.Futhark hiding (Value, matchDims)-import qualified Language.Futhark as F-import Futhark.IR.Primitive (intValue, floatValue)-import qualified Futhark.IR.Primitive as P-import qualified Language.Futhark.Semantic as T--import Futhark.Util.Pretty hiding (apply, bool)-import Futhark.Util (chunk, splitFromEnd, maybeHead)-import Futhark.Util.Loc--import Prelude hiding (mod, break)--data StackFrame = StackFrame { stackFrameLoc :: Loc-                             , stackFrameCtx :: Ctx-                             }--instance Located StackFrame where-  locOf = stackFrameLoc---- | What is the reason for this break point?-data BreakReason-  = BreakPoint -- ^ An explicit breakpoint in the program.-  | BreakNaN -- ^ A--data ExtOp a = ExtOpTrace Loc String a-             | ExtOpBreak BreakReason (NE.NonEmpty StackFrame) a-             | ExtOpError InterpreterError--instance Functor ExtOp where-  fmap f (ExtOpTrace w s x) = ExtOpTrace w s $ f x-  fmap f (ExtOpBreak why backtrace x) = ExtOpBreak why backtrace $ f x-  fmap _ (ExtOpError err) = ExtOpError err--type Stack = [StackFrame]--type Sizes = M.Map VName Int32---- | The monad in which evaluation takes place.-newtype EvalM a = EvalM (ReaderT (Stack, M.Map FilePath Env)-                         (StateT Sizes (F ExtOp)) a)-  deriving (Monad, Applicative, Functor,-            MonadFree ExtOp,-            MonadReader (Stack, M.Map FilePath Env),-            MonadState Sizes)--runEvalM :: M.Map FilePath Env -> EvalM a -> F ExtOp a-runEvalM imports (EvalM m) = evalStateT (runReaderT m (mempty, imports)) mempty--stacking :: SrcLoc -> Env -> EvalM a -> EvalM a-stacking loc env = local $ \(ss, imports) ->-  if isNoLoc loc-  then (ss, imports)-  else let s = StackFrame (locOf loc) (Ctx env imports)-       in (s:ss, imports)-  where isNoLoc :: SrcLoc -> Bool-        isNoLoc = (==NoLoc) . locOf--stacktrace :: EvalM [Loc]-stacktrace = asks $ map stackFrameLoc . fst--lookupImport :: FilePath -> EvalM (Maybe Env)-lookupImport f = asks $ M.lookup f . snd--putExtSize :: VName -> Int32 -> EvalM ()-putExtSize v x = modify $ M.insert v x--getSizes :: EvalM Sizes-getSizes = get--extSizeEnv :: EvalM Env-extSizeEnv = i32Env <$> getSizes--prettyRecord :: Pretty a => M.Map Name a -> Doc-prettyRecord m-  | Just vs <- areTupleFields m =-      parens $ commasep $ map ppr vs-  | otherwise =-      braces $ commasep $ map field $ M.toList m-      where field (k, v) = ppr k <+> equals <+> ppr v--valueStructType :: ValueType -> StructType-valueStructType = first (ConstDim . fromIntegral)---- | A shape is a tree to accomodate the case of records.  It is--- parameterised over the representation of dimensions.-data Shape d = ShapeDim d (Shape d)-             | ShapeLeaf-             | ShapeRecord (M.Map Name (Shape d))-             | ShapeSum (M.Map Name [Shape d])-             deriving (Eq, Show, Functor, Foldable, Traversable)--type ValueShape = Shape Int32--instance Pretty d => Pretty (Shape d) where-  ppr ShapeLeaf = mempty-  ppr (ShapeDim d s) = brackets (ppr d) <> ppr s-  ppr (ShapeRecord m) = prettyRecord m-  ppr (ShapeSum cs) =-    mconcat (punctuate (text " | ") cs')-    where ppConstr (name, fs) = sep $ (text "#" <> ppr name) : map ppr fs-          cs' = map ppConstr $ M.toList cs--emptyShape :: ValueShape -> Bool-emptyShape (ShapeDim d s) = d == 0 || emptyShape s-emptyShape _ = False--typeShape :: M.Map VName (Shape d) -> TypeBase d () -> Shape d-typeShape shapes = go-  where go (Array _ _ et shape) =-          foldr ShapeDim (go (Scalar et)) $ shapeDims shape-        go (Scalar (Record fs)) =-          ShapeRecord $ M.map go fs-        go (Scalar (Sum cs)) =-          ShapeSum $ M.map (map go) cs-        go (Scalar (TypeVar _ _ (TypeName [] v) []))-          | Just shape <- M.lookup v shapes =-              shape-        go _ =-          ShapeLeaf--structTypeShape :: M.Map VName ValueShape -> StructType -> Shape (Maybe Int32)-structTypeShape shapes = fmap dim . typeShape shapes'-  where dim (ConstDim d) = Just $ fromIntegral d-        dim _ = Nothing-        shapes' = M.map (fmap $ ConstDim . fromIntegral) shapes--resolveTypeParams :: [VName] -> StructType -> StructType -> Env-resolveTypeParams names = match-  where match (Scalar (TypeVar _ _ tn _)) t-          | typeLeaf tn `elem` names =-              typeEnv $ M.singleton (typeLeaf tn) t-        match (Scalar (Record poly_fields)) (Scalar (Record fields)) =-          mconcat $ M.elems $-          M.intersectionWith match poly_fields fields-        match (Scalar (Sum poly_fields)) (Scalar (Sum fields)) =-          mconcat $ map mconcat $ M.elems $-          M.intersectionWith (zipWith match) poly_fields fields-        match (Scalar (Arrow _ _  poly_t1 poly_t2)) (Scalar (Arrow _ _ t1 t2)) =-          match poly_t1 t1 <> match poly_t2 t2-        match poly_t t-          | d1 : _ <- shapeDims (arrayShape poly_t),-            d2 : _ <- shapeDims (arrayShape t) =-              matchDims d1 d2 <> match (stripArray 1 poly_t) (stripArray 1 t)-        match _ _ = mempty--        matchDims (NamedDim (QualName _ d1)) (ConstDim d2)-          | d1 `elem` names =-              i32Env $ M.singleton d1 $ fromIntegral d2-        matchDims _ _ = mempty--resolveExistentials :: [VName] -> StructType -> ValueShape -> M.Map VName Int32-resolveExistentials names = match-  where match (Scalar (Record poly_fields)) (ShapeRecord fields) =-          mconcat $ M.elems $-          M.intersectionWith match poly_fields fields-        match (Scalar (Sum poly_fields)) (ShapeSum fields) =-          mconcat $ map mconcat $ M.elems $-          M.intersectionWith (zipWith match) poly_fields fields-        match poly_t (ShapeDim d2 rowshape)-          | d1 : _ <- shapeDims (arrayShape poly_t) =-              matchDims d1 d2 <> match (stripArray 1 poly_t) rowshape-        match _ _ = mempty--        matchDims (NamedDim (QualName _ d1)) d2-          | d1 `elem` names = M.singleton d1 d2-        matchDims _ _ = mempty---- | A fully evaluated Futhark value.-data Value = ValuePrim !PrimValue-           | ValueArray ValueShape !(Array Int Value)-             -- Stores the full shape.-           | ValueRecord (M.Map Name Value)-           | ValueFun (Value -> EvalM Value)-           | ValueSum ValueShape Name [Value]-             -- Stores the full shape.--instance Eq Value where-  ValuePrim x == ValuePrim y = x == y-  ValueArray _ x == ValueArray _ y = x == y-  ValueRecord x == ValueRecord y = x == y-  ValueSum _ n1 vs1 == ValueSum _ n2 vs2 = n1 == n2 && vs1 == vs2-  _ == _ = False--instance Pretty Value where-  ppr (ValuePrim v)  = ppr v-  ppr (ValueArray _ a) =-    let elements  = elems a -- [Value]-        (x:_)     = elements-        separator = case x of-                      ValueArray _ _ -> comma <> line-                      _              -> comma <> space-     in brackets $ cat $ punctuate separator (map ppr elements)--  ppr (ValueRecord m) = prettyRecord m-  ppr ValueFun{} = text "#<fun>"-  ppr (ValueSum _ n vs) = text "#" <> sep (ppr n : map ppr vs)--valueShape :: Value -> ValueShape-valueShape (ValueArray shape _) = shape-valueShape (ValueRecord fs) = ShapeRecord $ M.map valueShape fs-valueShape (ValueSum shape _ _) = shape-valueShape _ = ShapeLeaf--checkShape :: Shape (Maybe Int32) -> ValueShape -> Maybe ValueShape-checkShape (ShapeDim Nothing shape1) (ShapeDim d2 shape2) =-  ShapeDim d2 <$> checkShape shape1 shape2-checkShape (ShapeDim (Just d1) shape1) (ShapeDim d2 shape2) = do-  guard $ d1 == d2-  ShapeDim d2 <$> checkShape shape1 shape2-checkShape (ShapeDim d1 shape1) ShapeLeaf =-  -- This case is for handling polymorphism, when a function doesn't-  -- know that the array it produced actually has more dimensions.-  ShapeDim (fromMaybe 0 d1) <$> checkShape shape1 ShapeLeaf-checkShape (ShapeRecord shapes1) (ShapeRecord shapes2) =-  ShapeRecord <$> sequence (M.intersectionWith checkShape shapes1 shapes2)-checkShape (ShapeRecord shapes1) ShapeLeaf =-  Just $ fromMaybe 0 <$> ShapeRecord shapes1-checkShape (ShapeSum shapes1) (ShapeSum shapes2) =-  ShapeSum <$> sequence (M.intersectionWith (zipWithM checkShape) shapes1 shapes2)-checkShape (ShapeSum shapes1) ShapeLeaf =-  Just $ fromMaybe 0 <$> ShapeSum shapes1-checkShape _ shape2 =-  Just shape2---- | Does the value correspond to an empty array?-isEmptyArray :: Value -> Bool-isEmptyArray = emptyShape . valueShape---- | String representation of an empty array with the provided element--- type.  This is pretty ad-hoc - don't expect good results unless the--- element type is a primitive.-prettyEmptyArray :: TypeBase () () -> Value -> String-prettyEmptyArray t v =-  "empty(" ++ dims (valueShape v) ++ pretty t' ++ ")"-  where t' = stripArray (arrayRank t) t-        dims (ShapeDim n rowshape) =-          "[" ++ pretty n ++ "]" ++ dims rowshape-        dims _ = ""---- | Create an array value; failing if that would result in an--- irregular array.-mkArray :: TypeBase Int32 () -> [Value] -> Maybe Value-mkArray t [] =-  return $ toArray (typeShape mempty t) []-mkArray _ (v:vs) = do-  let v_shape = valueShape v-  guard $ all ((==v_shape) . valueShape) vs-  return $ toArray' v_shape $ v:vs--arrayLength :: Integral int => Array Int Value -> int-arrayLength = fromIntegral . (+1) . snd . bounds--toTuple :: [Value] -> Value-toTuple = ValueRecord . M.fromList . zip tupleFieldNames--fromTuple :: Value -> Maybe [Value]-fromTuple (ValueRecord m) = areTupleFields m-fromTuple _ = Nothing--asInteger :: Value -> Integer-asInteger (ValuePrim (SignedValue v)) = P.valueIntegral v-asInteger (ValuePrim (UnsignedValue v)) =-  toInteger (P.valueIntegral (P.doZExt v Int64) :: Word64)-asInteger v = error $ "Unexpectedly not an integer: " ++ pretty v--asInt :: Value -> Int-asInt = fromIntegral . asInteger--asSigned :: Value -> IntValue-asSigned (ValuePrim (SignedValue v)) = v-asSigned v = error $ "Unexpected not a signed integer: " ++ pretty v--asInt32 :: Value -> Int32-asInt32 = fromIntegral . asInteger--asBool :: Value -> Bool-asBool (ValuePrim (BoolValue x)) = x-asBool v = error $ "Unexpectedly not a boolean: " ++ pretty v--lookupInEnv :: (Env -> M.Map VName x)-            -> QualName VName -> Env -> Maybe x-lookupInEnv onEnv qv env = f env $ qualQuals qv-  where f m (q:qs) =-          case M.lookup q $ envTerm m of-            Just (TermModule (Module mod)) -> f mod qs-            _ -> Nothing-        f m [] = M.lookup (qualLeaf qv) $ onEnv m--lookupVar :: QualName VName -> Env -> Maybe TermBinding-lookupVar = lookupInEnv envTerm--lookupType :: QualName VName -> Env -> Maybe T.TypeBinding-lookupType = lookupInEnv envType---- | A TermValue with a 'Nothing' type annotation is an intrinsic.-data TermBinding = TermValue (Maybe T.BoundV) Value-                 | TermPoly (Maybe T.BoundV) (StructType -> EvalM Value)-                   -- ^ A polymorphic value that must be instantiated.-                 | TermModule Module--data Module = Module Env-            | ModuleFun (Module -> EvalM Module)---- | The actual type- and value environment.-data Env = Env { envTerm :: M.Map VName TermBinding-               , envType :: M.Map VName T.TypeBinding-               , envShapes :: M.Map VName ValueShape-                 -- ^ A mapping from type parameters to the shapes of-                 -- the value to which they were initially bound.-               }--instance Monoid Env where-  mempty = Env mempty mempty mempty--instance Semigroup Env where-  Env vm1 tm1 sm1 <> Env vm2 tm2 sm2 =-    Env (vm1 <> vm2) (tm1 <> tm2) (sm1 <> sm2)---- | An error occurred during interpretation due to an error in the--- user program.  Actual interpreter errors will be signaled with an--- IO exception ('error').-newtype InterpreterError = InterpreterError String--valEnv :: M.Map VName (Maybe T.BoundV, Value) -> Env-valEnv m = Env { envTerm = M.map (uncurry TermValue) m-               , envType = mempty-               , envShapes = mempty-               }--modEnv :: M.Map VName Module -> Env-modEnv m = Env { envTerm = M.map TermModule m-               , envType = mempty-               , envShapes = mempty-               }--typeEnv :: M.Map VName StructType -> Env-typeEnv m = Env { envTerm = mempty-                , envType = M.map tbind m-                , envShapes = mempty-                }-  where tbind = T.TypeAbbr Unlifted []--i32Env :: M.Map VName Int32 -> Env-i32Env = valEnv . M.map f-  where f x = (Just $ T.BoundV [] $ Scalar $ Prim $ Signed Int32,-               ValuePrim $ SignedValue $ Int32Value x)--instance Show InterpreterError where-  show (InterpreterError s) = s--bad :: SrcLoc -> Env -> String -> EvalM a-bad loc env s = stacking loc env $ do-  ss <- map (locStr . srclocOf) <$> stacktrace-  liftF $ ExtOpError $ InterpreterError $ "Error at\n" ++ prettyStacktrace 0 ss ++ s--trace :: Value -> EvalM ()-trace v = do-  -- We take the second-to-top element of the stack, because any-  -- actual call to 'implicits.trace' is going to be in the trace-  -- function in the prelude, which is not interesting.-  top <- fromMaybe noLoc . maybeHead . drop 1 <$> stacktrace-  liftF $ ExtOpTrace top (prettyOneLine v) ()--typeCheckerEnv :: Env -> T.Env-typeCheckerEnv env =-  -- FIXME: some shadowing issues are probably not right here.-  let valMap (TermValue (Just t) _) = Just t-      valMap _ = Nothing-      vtable = M.mapMaybe valMap $ envTerm env-      nameMap k | k `M.member` vtable = Just ((T.Term, baseName k), qualName k)-                | otherwise = Nothing-  in mempty { T.envNameMap = M.fromList $ mapMaybe nameMap $ M.keys $ envTerm env-            , T.envVtable = vtable }--break :: EvalM ()-break = do-  -- We don't want the env of the function that is calling-  -- intrinsics.break, since that is just going to be the boring-  -- wrapper function (intrinsics are never called directly).-  -- This is why we go a step up the stack.-  backtrace <- asks $ drop 1 . fst-  case NE.nonEmpty backtrace of-    Nothing -> return ()-    Just backtrace' -> liftF $ ExtOpBreak BreakPoint backtrace' ()--fromArray :: Value -> (ValueShape, [Value])-fromArray (ValueArray shape as) = (shape, elems as)-fromArray v = error $ "Expected array value, but found: " ++ pretty v--toArray :: ValueShape -> [Value] -> Value-toArray shape vs = ValueArray shape (listArray (0, length vs - 1) vs)--toArray' :: ValueShape -> [Value] -> Value-toArray' rowshape vs = ValueArray shape (listArray (0, length vs - 1) vs)-  where shape = ShapeDim (genericLength vs) rowshape--apply :: SrcLoc -> Env -> Value -> Value -> EvalM Value-apply loc env (ValueFun f) v = stacking loc env (f v)-apply _ _ f _ = error $ "Cannot apply non-function: " ++ pretty f--apply2 :: SrcLoc -> Env -> Value -> Value -> Value -> EvalM Value-apply2 loc env f x y = stacking loc env $ do f' <- apply noLoc mempty f x-                                             apply noLoc mempty f' y--matchPattern :: Env -> Pattern -> Value -> EvalM Env-matchPattern env p v = do-  m <- runMaybeT $ patternMatch env p v-  case m of-    Nothing   -> error $ "matchPattern: missing case for " ++ pretty p ++ " and " ++ pretty v-    Just env' -> return env'--patternMatch :: Env -> Pattern -> Value -> MaybeT EvalM Env-patternMatch env (Id v (Info t) _) val =-  lift $ pure $-  valEnv (M.singleton v (Just $ T.BoundV [] $ toStruct t, val)) <> env-patternMatch env Wildcard{} _ =-  lift $ pure env-patternMatch env (TuplePattern ps _) (ValueRecord vs) =-  foldM (\env' (p,v) -> patternMatch env' p v) env $-  zip ps (map snd $ sortFields vs)-patternMatch env (RecordPattern ps _) (ValueRecord vs) =-  foldM (\env' (p,v) -> patternMatch env' p v) env $-  M.intersectionWith (,) (M.fromList ps) vs-patternMatch env (PatternParens p _) v = patternMatch env p v-patternMatch env (PatternAscription p _ _) v =-  patternMatch env p v-patternMatch env (PatternLit e _ _) v = do-  v' <- lift $ eval env e-  if v == v'-    then pure env-    else mzero-patternMatch env (PatternConstr n _ ps _) (ValueSum _ n' vs)-  | n == n' =-      foldM (\env' (p,v) -> patternMatch env' p v) env $ zip ps vs-patternMatch _ _ _ = mzero--data Indexing = IndexingFix Int32-              | IndexingSlice (Maybe Int32) (Maybe Int32) (Maybe Int32)--instance Pretty Indexing where-  ppr (IndexingFix i) = ppr i-  ppr (IndexingSlice i j (Just s)) =-    maybe mempty ppr i <> text ":" <>-    maybe mempty ppr j <> text ":" <>-    ppr s-  ppr (IndexingSlice i (Just j) s) =-    maybe mempty ppr i <> text ":" <>-    ppr j <>-    maybe mempty ((text ":" <>) . ppr) s-  ppr (IndexingSlice i Nothing Nothing) =-    maybe mempty ppr i <> text ":"--indexesFor :: Maybe Int32 -> Maybe Int32 -> Maybe Int32-           -> Int32 -> Maybe [Int]-indexesFor start end stride n-  | (start', end', stride') <- slice,-    end' == start' || signum' (end' - start') == signum' stride',-    stride' /= 0,-    is <- [start', start'+stride' .. end'-signum stride'],-    all inBounds is =-      Just $ map fromIntegral is-  | otherwise =-      Nothing-  where inBounds i = i >= 0 && i < n--        slice =-          case (start, end, stride) of-            (Just start', _, _) ->-              let end' = fromMaybe n end-              in (start', end', fromMaybe 1 stride)-            (Nothing, Just end', _) ->-              let start' = 0-              in (start', end', fromMaybe 1 stride)-            (Nothing, Nothing, Just stride') ->-              (if stride' > 0 then 0 else n-1,-               if stride' > 0 then n else -1,-               stride')-            (Nothing, Nothing, Nothing) ->-              (0, n, 1)---- | 'signum', but with 0 as 1.-signum' :: (Eq p, Num p) => p -> p-signum' 0 = 1-signum' x = signum x--indexShape :: [Indexing] -> ValueShape -> ValueShape-indexShape (IndexingFix{}:is) (ShapeDim _ shape) =-  indexShape is shape-indexShape (IndexingSlice start end stride:is) (ShapeDim d shape) =-  ShapeDim n $ indexShape is shape-  where n = maybe 0 genericLength $ indexesFor start end stride d-indexShape _ shape =-  shape--indexArray :: [Indexing] -> Value -> Maybe Value-indexArray (IndexingFix i:is) (ValueArray _ arr)-  | i >= 0, i < n =-      indexArray is $ arr ! fromIntegral i-  | otherwise =-      Nothing-  where n = arrayLength arr-indexArray (IndexingSlice start end stride:is) (ValueArray (ShapeDim _ rowshape) arr) = do-  js <- indexesFor start end stride $ arrayLength arr-  toArray' (indexShape is rowshape) <$> mapM (indexArray is . (arr!)) js-indexArray _ v = Just v--updateArray :: [Indexing] -> Value -> Value -> Maybe Value-updateArray (IndexingFix i:is) (ValueArray shape arr) v-  | i >= 0, i < n = do-      v' <- updateArray is (arr ! i') v-      Just $ ValueArray shape $ arr // [(i', v')]-  | otherwise =-      Nothing-  where n = arrayLength arr-        i' = fromIntegral i-updateArray (IndexingSlice start end stride:is) (ValueArray shape arr) (ValueArray _ v) = do-  arr_is <- indexesFor start end stride $ arrayLength arr-  guard $ length arr_is == arrayLength v-  let update arr' (i, v') = do-        x <- updateArray is (arr!i) v'-        return $ arr' // [(i, x)]-  fmap (ValueArray shape) $ foldM update arr $ zip arr_is $ elems v-updateArray _ _ v = Just v--evalDimIndex :: Env -> DimIndex -> EvalM Indexing-evalDimIndex env (DimFix x) =-  IndexingFix . asInt32 <$> eval env x-evalDimIndex env (DimSlice start end stride) =-  IndexingSlice <$> traverse (fmap asInt32 . eval env) start-                <*> traverse (fmap asInt32 . eval env) end-                <*> traverse (fmap asInt32 . eval env) stride--evalIndex :: SrcLoc -> Env -> [Indexing] -> Value -> EvalM Value-evalIndex loc env is arr = do-  let oob = bad loc env $ "Index [" <> intercalate ", " (map pretty is) <>-            "] out of bounds for array of shape " <>-            pretty (valueShape arr) <> "."-  maybe oob return $ indexArray is arr---- | Expand type based on information that was not available at--- type-checking time (the structure of abstract types).-evalType :: Env -> StructType -> StructType-evalType _ (Scalar (Prim pt)) = Scalar $ Prim pt-evalType env (Scalar (Record fs)) = Scalar $ Record $ fmap (evalType env) fs-evalType env (Scalar (Arrow () p t1 t2)) =-  Scalar $ Arrow () p (evalType env t1) (evalType env t2)-evalType env t@(Array _ u _ shape) =-  let et = stripArray (shapeRank shape) t-      et' = evalType env et-      shape' = fmap evalDim shape-  in arrayOf et' shape' u-  where evalDim (NamedDim qn)-          | Just (TermValue _ (ValuePrim (SignedValue (Int32Value x)))) <--              lookupVar qn env =-              ConstDim $ fromIntegral x-        evalDim d = d-evalType env t@(Scalar (TypeVar () _ tn args)) =-  case lookupType (qualNameFromTypeName tn) env of-    Just (T.TypeAbbr _ ps t') ->-      let (substs, types) = mconcat $ zipWith matchPtoA ps args-          onDim (NamedDim v) = fromMaybe (NamedDim v) $ M.lookup (qualLeaf v) substs-          onDim d = d-      in if null ps then first onDim t'-         else evalType (Env mempty types mempty <> env) $ first onDim t'-    Nothing -> t--  where matchPtoA (TypeParamDim p _) (TypeArgDim (NamedDim qv) _) =-          (M.singleton p $ NamedDim qv, mempty)-        matchPtoA (TypeParamDim p _) (TypeArgDim (ConstDim k) _) =-          (M.singleton p $ ConstDim k, mempty)-        matchPtoA (TypeParamType l p _) (TypeArgType t' _) =-          let t'' = evalType env t'-          in (mempty, M.singleton p $ T.TypeAbbr l [] t'')-        matchPtoA _ _ = mempty-evalType env (Scalar (Sum cs)) = Scalar $ Sum $ (fmap . fmap) (evalType env) cs--evalTermVar :: Env -> QualName VName -> StructType -> EvalM Value-evalTermVar env qv t =-  case lookupVar qv env of-    Just (TermPoly _ v) -> do size_env <- extSizeEnv-                              v $ evalType (size_env <> env) t-    Just (TermValue _ v) -> return v-    _ -> error $ "`" <> pretty qv <> "` is not bound to a value."--typeValueShape :: Env -> StructType -> EvalM ValueShape-typeValueShape env t = do-  size_env <- extSizeEnv-  let t' = evalType (size_env <> env) t-  case traverse dim $ typeShape mempty t' of-    Nothing -> error $ "typeValueShape: failed to fully evaluate type " ++ pretty t'-    Just shape -> return shape-  where dim (ConstDim x) = Just $ fromIntegral x-        dim _ = Nothing--evalFunction :: Env -> [VName] -> [Pattern] -> Exp -> StructType -> EvalM Value---- We treat zero-parameter lambdas as simply an expression to--- evaluate immediately.  Note that this is *not* the same as a lambda--- that takes an empty tuple '()' as argument!  Zero-parameter lambdas--- can never occur in a well-formed Futhark program, but they are--- convenient in the interpreter.-evalFunction env _ [] body rettype =-  -- Eta-expand the rest to make any sizes visible.-  etaExpand [] env rettype-  where etaExpand vs env' (Scalar (Arrow _ _ pt rt)) =-          return $ ValueFun $ \v -> do-          env'' <- matchPattern env' (Wildcard (Info $ fromStruct pt) noLoc) v-          etaExpand (v:vs) env'' rt-        etaExpand vs env' _ = do-          f <- eval env' body-          foldM (apply noLoc mempty) f $ reverse vs--evalFunction env missing_sizes (p:ps) body rettype =-  return $ ValueFun $ \v -> do-    env' <- matchPattern env p v-    -- Fix up the last sizes, if any.-    let env'' | null missing_sizes = env'-              | otherwise = env' <>-                            i32Env (resolveExistentials missing_sizes-                                    (patternStructType p) (valueShape v))-    evalFunction env'' missing_sizes ps body rettype--evalFunctionBinding :: Env-                    -> [TypeParam] -> [Pattern] -> StructType -> [VName] -> Exp-                    -> EvalM TermBinding-evalFunctionBinding env tparams ps ret retext fbody = do-  let ret' = evalType env ret-      arrow (xp, xt) yt = Scalar $ Arrow () xp xt yt-      ftype = foldr (arrow . patternParam) ret' ps--  -- Distinguish polymorphic and non-polymorphic bindings here.-  if null tparams-  then TermValue (Just $ T.BoundV [] ftype) <$>-       (returned env ret retext =<< evalFunction env [] ps fbody ret')-  else return $ TermPoly (Just $ T.BoundV [] ftype) $ \ftype' -> do-         let tparam_names = map typeParamName tparams-             env' = resolveTypeParams tparam_names ftype ftype' <> env--             -- In some cases (abstract lifted types) there may be-             -- missing sizes that were not fixed by the type-             -- instantiation.  These will have to be set by looking-             -- at the actual function arguments.-             missing_sizes =-               filter (`M.notMember` envTerm env') $-               map typeParamName (filter isSizeParam tparams)-         returned env ret retext =<< evalFunction env' missing_sizes ps fbody ret'--evalArg :: Env -> Exp -> Maybe VName -> EvalM Value-evalArg env e ext = do-  v <- eval env e-  case ext of Just ext' -> putExtSize ext' $ asInt32 v-              Nothing -> return ()-  return v--returned :: Env -> TypeBase (DimDecl VName) als -> [VName] -> Value -> EvalM Value-returned _ _ [] v = return v-returned env ret retext v = do-  mapM_ (uncurry putExtSize) $ M.toList $-    resolveExistentials retext (evalType env $ toStruct ret) $ valueShape v-  return v--eval :: Env -> Exp -> EvalM Value--eval _ (Literal v _) = return $ ValuePrim v--eval env (Parens e _ ) = eval env e--eval env (QualParens (qv, _) e loc) = do-  m <- evalModuleVar env qv-  case m of-    ModuleFun{} -> error $ "Local open of module function at " ++ locStr loc-    Module m' -> eval (m'<>env) e--eval env (TupLit vs _) = toTuple <$> mapM (eval env) vs--eval env (RecordLit fields _) =-  ValueRecord . M.fromList <$> mapM evalField fields-  where evalField (RecordFieldExplicit k e _) = do-          v <- eval env e-          return (k, v)-        evalField (RecordFieldImplicit k t loc) = do-          v <- eval env $ Var (qualName k) t loc-          return (baseName k, v)--eval _ (StringLit vs _) =-  return $ toArray' ShapeLeaf $-  map (ValuePrim . UnsignedValue . Int8Value . fromIntegral) vs--eval env (ArrayLit [] (Info t) _) = do-  t' <- typeValueShape env $ toStruct t-  return $ toArray t' []--eval env (ArrayLit (v:vs) _ _) = do-  v' <- eval env v-  vs' <- mapM (eval env) vs-  return $ toArray' (valueShape v') (v':vs')--eval env (Range start maybe_second end (Info t, Info retext) loc) = do-  start' <- asInteger <$> eval env start-  maybe_second' <- traverse (fmap asInteger . eval env) maybe_second-  end' <- traverse (fmap asInteger . eval env) end--  let (end_adj, step, ok) =-        case (end', maybe_second') of-          (DownToExclusive end'', Nothing) ->-            (end'' + 1, -1, start' >= end'')-          (DownToExclusive end'', Just second') ->-            (end'' + 1, second' - start', start' >= end'' && second' < start')--          (ToInclusive end'', Nothing) ->-            (end'', 1, start' <= end'')-          (ToInclusive end'', Just second')-            | second' > start' ->-                (end'', second' - start', start' <= end'')-            | otherwise ->-                (end'', second' - start', start' >= end'' && second' /= start')--          (UpToExclusive x, Nothing) ->-            (x-1, 1, start' <= x)-          (UpToExclusive x, Just second') ->-            (x-1, second' - start', start' <= x && second' > start')--  if ok-    then returned env t retext $-         toArray' ShapeLeaf $ map toInt [start',start'+step..end_adj]-    else bad loc env $ badRange start' maybe_second' end'--  where toInt =-          case stripArray 1 t of-            Scalar (Prim (Signed t')) ->-              ValuePrim . SignedValue . intValue t'-            Scalar (Prim (Unsigned t')) ->-              ValuePrim . UnsignedValue . intValue t'-            _ -> error $ "Nonsensical range type: " ++ show t--        badRange start' maybe_second' end' =-          "Range " ++ pretty start' ++-          (case maybe_second' of-             Nothing -> ""-             Just second' -> ".." ++ pretty second') ++-          (case end' of-             DownToExclusive x -> "..>" ++ pretty x-             ToInclusive x -> "..." ++ pretty x-             UpToExclusive x -> "..<"++ pretty x) ++-          " is invalid."--eval env (Var qv (Info t) _) = evalTermVar env qv (toStruct t)--eval env (Ascript e _ _ ) = eval env e--eval env (Coerce e td (Info ret, Info retext) loc) = do-  v <- returned env ret retext =<< eval env e-  let t = evalType env $ unInfo $ expandedType td-  case checkShape (structTypeShape (envShapes env) t) (valueShape v) of-    Just _ -> return v-    Nothing ->-      bad loc env $ "Value `" <> pretty v <> "` of shape `" ++ pretty (valueShape v) ++-      "` cannot match shape of type `" <>-      pretty (declaredType td) <> "` (`" <> pretty t <> "`)"--eval env (LetPat p e body (Info ret, Info retext) _) = do-  v <- eval env e-  env' <- matchPattern env p v-  returned env ret retext =<< eval env' body--eval env (LetFun f (tparams, ps, _, Info ret, fbody) body _ _) = do-  binding <- evalFunctionBinding env tparams ps ret [] fbody-  eval (env { envTerm = M.insert f binding $ envTerm env }) body--eval _ (IntLit v (Info t) _) =-  case t of-    Scalar (Prim (Signed it)) ->-      return $ ValuePrim $ SignedValue $ intValue it v-    Scalar (Prim (Unsigned it)) ->-      return $ ValuePrim $ UnsignedValue $ intValue it v-    Scalar (Prim (FloatType ft)) ->-      return $ ValuePrim $ FloatValue $ floatValue ft v-    _ -> error $ "eval: nonsensical type for integer literal: " ++ pretty t--eval _ (FloatLit v (Info t) _) =-  case t of-    Scalar (Prim (FloatType ft)) ->-      return $ ValuePrim $ FloatValue $ floatValue ft v-    _ -> error $ "eval: nonsensical type for float literal: " ++ pretty t--eval env (BinOp (op, _) op_t-          (x, Info (_, xext)) (y, Info (_, yext))-          (Info t) (Info retext) loc)-  | baseString (qualLeaf op) == "&&" = do-      x' <- asBool <$> eval env x-      if x'-        then eval env y-        else return $ ValuePrim $ BoolValue False-  | baseString (qualLeaf op) == "||" = do-      x' <- asBool <$> eval env x-      if x'-        then return $ ValuePrim $ BoolValue True-        else eval env y-  | otherwise = do-      op' <- eval env $ Var op op_t loc-      x' <- evalArg env x xext-      y' <- evalArg env y yext-      returned env t retext =<< apply2 loc env op' x' y'--eval env (If cond e1 e2 (Info ret, Info retext) _) = do-  cond' <- asBool <$> eval env cond-  returned env ret retext =<<-    if cond' then eval env e1 else eval env e2--eval env (Apply f x (Info (_, ext)) (Info t, Info retext) loc) = do-  -- It is important that 'x' is evaluated first in order to bring any-  -- sizes into scope that may be used in the type of 'f'.-  x' <- evalArg env x ext-  f' <- eval env f-  returned env t retext =<< apply loc env f' x'--eval env (Negate e _) = do-  ev <- eval env e-  ValuePrim <$> case ev of-    ValuePrim (SignedValue (Int8Value v)) -> return $ SignedValue $ Int8Value (-v)-    ValuePrim (SignedValue (Int16Value v)) -> return $ SignedValue $ Int16Value (-v)-    ValuePrim (SignedValue (Int32Value v)) -> return $ SignedValue $ Int32Value (-v)-    ValuePrim (SignedValue (Int64Value v)) -> return $ SignedValue $ Int64Value (-v)-    ValuePrim (UnsignedValue (Int8Value v)) -> return $ UnsignedValue $ Int8Value (-v)-    ValuePrim (UnsignedValue (Int16Value v)) -> return $ UnsignedValue $ Int16Value (-v)-    ValuePrim (UnsignedValue (Int32Value v)) -> return $ UnsignedValue $ Int32Value (-v)-    ValuePrim (UnsignedValue (Int64Value v)) -> return $ UnsignedValue $ Int64Value (-v)-    ValuePrim (FloatValue (Float32Value v)) -> return $ FloatValue $ Float32Value (-v)-    ValuePrim (FloatValue (Float64Value v)) -> return $ FloatValue $ Float64Value (-v)-    _ -> error $ "Cannot negate " ++ pretty ev--eval env (Index e is (Info t, Info retext) loc) = do-  is' <- mapM (evalDimIndex env) is-  arr <- eval env e-  returned env t retext =<< evalIndex loc env is' arr--eval env (Update src is v loc) =-  maybe oob return =<<-  updateArray <$> mapM (evalDimIndex env) is <*> eval env src <*> eval env v-  where oob = bad loc env "Bad update"--eval env (RecordUpdate src all_fs v _ _) =-  update <$> eval env src <*> pure all_fs <*> eval env v-  where update _ [] v' = v'-        update (ValueRecord src') (f:fs) v'-          | Just f_v <- M.lookup f src' =-              ValueRecord $ M.insert f (update f_v fs v') src'-        update _ _ _ = error "eval RecordUpdate: invalid value."--eval env (LetWith dest src is v body _ loc) = do-  let Ident src_vn (Info src_t) _ = src-  dest' <- maybe oob return =<<-    updateArray <$> mapM (evalDimIndex env) is <*>-    evalTermVar env (qualName src_vn) (toStruct src_t) <*> eval env v-  let t = T.BoundV [] $ toStruct $ unInfo $ identType dest-  eval (valEnv (M.singleton (identName dest) (Just t, dest')) <> env) body-  where oob = bad loc env "Bad update"---- We treat zero-parameter lambdas as simply an expression to--- evaluate immediately.  Note that this is *not* the same as a lambda--- that takes an empty tuple '()' as argument!  Zero-parameter lambdas--- can never occur in a well-formed Futhark program, but they are--- convenient in the interpreter.-eval env (Lambda ps body _ (Info (_, rt)) _) =-  evalFunction env [] ps body rt--eval env (OpSection qv (Info t) _) = evalTermVar env qv $ toStruct t--eval env (OpSectionLeft qv _ e (Info (_, argext), _) (Info t, Info retext) loc) = do-  v <- evalArg env e argext-  f <- evalTermVar env qv (toStruct t)-  returned env t retext =<< apply loc env f v--eval env (OpSectionRight qv _ e (Info _, Info (_, argext)) (Info t) loc) = do-  y <- evalArg env e argext-  return $ ValueFun $ \x -> do-    f <- evalTermVar env qv $ toStruct t-    apply2 loc env f x y--eval env (IndexSection is _ loc) = do-  is' <- mapM (evalDimIndex env) is-  return $ ValueFun $ evalIndex loc env is'--eval _ (ProjectSection ks _ _) =-  return $ ValueFun $ flip (foldM walk) ks-  where walk (ValueRecord fs) f-          | Just v' <- M.lookup f fs = return v'-        walk _ _ = error "Value does not have expected field."--eval env (DoLoop sparams pat init_e form body (Info (ret, retext)) _) = do-  init_v <- eval env init_e-  returned env ret retext =<<-    case form of For iv bound -> do-                   bound' <- asSigned <$> eval env bound-                   forLoop (identName iv) bound' (zero bound') init_v-                 ForIn in_pat in_e -> do-                   (_, in_vs) <- fromArray <$> eval env in_e-                   foldM (forInLoop in_pat) init_v in_vs-                 While cond ->-                   whileLoop cond init_v-  where withLoopParams v =-          let sparams' =-                resolveExistentials sparams-                (patternStructType pat) (valueShape v)-          in matchPattern (i32Env sparams' <> env) pat v--        inc = (`P.doAdd` Int64Value 1)-        zero = (`P.doMul` Int64Value 0)--        forLoop iv bound i v-          | i >= bound = return v-          | otherwise = do-              env' <- withLoopParams v-              forLoop iv bound (inc i) =<<-                eval (valEnv (M.singleton iv (Just $ T.BoundV [] $ Scalar $ Prim $ Signed Int32,-                                              ValuePrim (SignedValue i))) <> env') body--        whileLoop cond v = do-          env' <- withLoopParams v-          continue <- asBool <$> eval env' cond-          if continue-            then whileLoop cond =<< eval env' body-            else return v--        forInLoop in_pat v in_v = do-          env' <- withLoopParams v-          env'' <- matchPattern env' in_pat in_v-          eval env'' body--eval env (Project f e _ _) = do-  v <- eval env e-  case v of-    ValueRecord fs | Just v' <- M.lookup f fs -> return v'-    _ -> error "Value does not have expected field."--eval env (Assert what e (Info s) loc) = do-  cond <- asBool <$> eval env what-  unless cond $ bad loc env s-  eval env e--eval env (Constr c es (Info t) _) = do-  vs <- mapM (eval env) es-  shape <- typeValueShape env $ toStruct t-  return $ ValueSum shape c vs--eval env (Match e cs (Info ret, Info retext) _) = do-  v <- eval env e-  returned env ret retext =<< match v (NE.toList cs)-  where match _ [] =-          error "Pattern match failure."-        match v (c:cs') = do-          c' <- evalCase v env c-          case c' of-            Just v' -> return v'-            Nothing -> match v cs'--eval env (Attr _ e _) = eval env e--evalCase :: Value -> Env -> CaseBase Info VName-         -> EvalM (Maybe Value)-evalCase v env (CasePat p cExp _) = runMaybeT $ do-  env' <- patternMatch env p v-  lift $ eval env' cExp--substituteInModule :: M.Map VName VName -> Module -> Module-substituteInModule substs = onModule-  where-    rev_substs = reverseSubstitutions substs-    replace v = fromMaybe v $ M.lookup v rev_substs-    replaceQ v = maybe v qualName $ M.lookup (qualLeaf v) rev_substs-    replaceM f m = M.fromList $ do-      (k, v) <- M.toList m-      return (replace k, f v)-    onModule (Module (Env terms types _)) =-      Module $ Env (replaceM onTerm terms) (replaceM onType types) mempty-    onModule (ModuleFun f) =-      ModuleFun $ \m -> onModule <$> f (substituteInModule rev_substs m)-    onTerm (TermValue t v) = TermValue t v-    onTerm (TermPoly t v) = TermPoly t v-    onTerm (TermModule m) = TermModule $ onModule m-    onType (T.TypeAbbr l ps t) = T.TypeAbbr l ps $ first onDim t-    onDim (NamedDim v) = NamedDim $ replaceQ v-    onDim (ConstDim x) = ConstDim x-    onDim AnyDim = AnyDim--reverseSubstitutions :: M.Map VName VName -> M.Map VName VName-reverseSubstitutions = M.fromList . map (uncurry $ flip (,)) . M.toList--evalModuleVar :: Env -> QualName VName -> EvalM Module-evalModuleVar env qv =-  case lookupVar qv env of-    Just (TermModule m) -> return m-    _ -> error $ quote (pretty qv) <> " is not bound to a module."--evalModExp :: Env -> ModExp -> EvalM Module--evalModExp _ (ModImport _ (Info f) _) = do-  f' <- lookupImport f-  case f' of Nothing -> error $ "Unknown import " ++ show f-             Just m -> return $ Module m--evalModExp env (ModDecs ds _) = do-  Env terms types _ <- foldM evalDec env ds-  -- Remove everything that was present in the original Env.-  return $ Module $ Env (terms `M.difference` envTerm env)-                        (types `M.difference` envType env)-                        mempty--evalModExp env (ModVar qv _) =-  evalModuleVar env qv--evalModExp env (ModAscript me _ (Info substs) _) =-  substituteInModule substs <$> evalModExp env me--evalModExp env (ModParens me _) = evalModExp env me--evalModExp env (ModLambda p ret e loc) =-  return $ ModuleFun $ \am -> do-  let env' = env { envTerm = M.insert (modParamName p) (TermModule am) $ envTerm env }-  evalModExp env' $ case ret of-    Nothing -> e-    Just (se, rsubsts) -> ModAscript e se rsubsts loc--evalModExp env (ModApply f e (Info psubst) (Info rsubst) _) = do-  f' <- evalModExp env f-  case f' of-    ModuleFun f'' -> do-      e' <- evalModExp env e-      substituteInModule rsubst <$> f'' (substituteInModule psubst e')-    _ -> error "Expected ModuleFun."--evalDec :: Env -> Dec -> EvalM Env--evalDec env (ValDec (ValBind _ v _ (Info (ret, retext)) tparams ps fbody _ _ _)) = do-  binding <- evalFunctionBinding env tparams ps ret retext fbody-  return $ env { envTerm = M.insert v binding $ envTerm env }--evalDec env (OpenDec me _) = do-  me' <- evalModExp env me-  case me' of-    Module me'' -> return $ me'' <> env-    _ -> error "Expected Module"--evalDec env (ImportDec name name' loc) =-  evalDec env $ LocalDec (OpenDec (ModImport name name' loc) loc) loc--evalDec env (LocalDec d _) = evalDec env d-evalDec env SigDec{} = return env-evalDec env (TypeDec (TypeBind v l ps t _ _)) = do-  let abbr = T.TypeAbbr l ps $-             evalType env $ unInfo $ expandedType t-  return env { envType = M.insert v abbr $ envType env }-evalDec env (ModDec (ModBind v ps ret body _ loc)) = do-  mod <- evalModExp env $ wrapInLambda ps-  return $ modEnv (M.singleton v mod) <> env-  where wrapInLambda [] = case ret of-                            Just (se, substs) -> ModAscript body se substs loc-                            Nothing           -> body-        wrapInLambda [p] = ModLambda p ret body loc-        wrapInLambda (p:ps') = ModLambda p Nothing (wrapInLambda ps') loc---- | The interpreter context.  All evaluation takes place with respect--- to a context, and it can be extended with more definitions, which--- is how the REPL works.-data Ctx = Ctx { ctxEnv :: Env-               , ctxImports :: M.Map FilePath Env-               }--nanValue :: PrimValue -> Bool-nanValue (FloatValue v) =-  case v of Float32Value x -> isNaN x-            Float64Value x -> isNaN x-nanValue _ = False--breakOnNaN :: [PrimValue] -> PrimValue -> EvalM ()-breakOnNaN inputs result-  | not (any nanValue inputs) && nanValue result = do-      backtrace <- asks fst-      case NE.nonEmpty backtrace of-        Nothing -> return ()-        Just backtrace' -> liftF $ ExtOpBreak BreakNaN backtrace' ()-breakOnNaN _ _ =-  return ()---- | The initial environment contains definitions of the various intrinsic functions.-initialCtx :: Ctx-initialCtx =-  Ctx (Env (M.insert (VName (nameFromString "intrinsics") 0)-            (TermModule (Module $ Env terms types mempty)) terms)-        types mempty)-      mempty-  where-    terms = M.mapMaybeWithKey (const . def . baseString) intrinsics-    types = M.mapMaybeWithKey (const . tdef . baseString) intrinsics--    sintOp f = [ (getS, putS, P.doBinOp (f Int8))-               , (getS, putS, P.doBinOp (f Int16))-               , (getS, putS, P.doBinOp (f Int32))-               , (getS, putS, P.doBinOp (f Int64))]-    uintOp f = [ (getU, putU, P.doBinOp (f Int8))-               , (getU, putU, P.doBinOp (f Int16))-               , (getU, putU, P.doBinOp (f Int32))-               , (getU, putU, P.doBinOp (f Int64))]-    intOp f = sintOp f ++ uintOp f-    floatOp f = [ (getF, putF, P.doBinOp (f Float32))-                , (getF, putF, P.doBinOp (f Float64))]-    arithOp f g = Just $ bopDef $ intOp f ++ floatOp g--    flipCmps = map (\(f, g, h) -> (f, g, flip h))-    sintCmp f = [ (getS, Just . BoolValue, P.doCmpOp (f Int8))-                , (getS, Just . BoolValue, P.doCmpOp (f Int16))-                , (getS, Just . BoolValue, P.doCmpOp (f Int32))-                , (getS, Just . BoolValue, P.doCmpOp (f Int64))]-    uintCmp f = [ (getU, Just . BoolValue, P.doCmpOp (f Int8))-                , (getU, Just . BoolValue, P.doCmpOp (f Int16))-                , (getU, Just . BoolValue, P.doCmpOp (f Int32))-                , (getU, Just . BoolValue, P.doCmpOp (f Int64))]-    floatCmp f = [ (getF, Just . BoolValue, P.doCmpOp (f Float32))-                 , (getF, Just . BoolValue, P.doCmpOp (f Float64))]-    boolCmp f = [ (getB, Just . BoolValue, P.doCmpOp f) ]--    getV (SignedValue x) = Just $ P.IntValue x-    getV (UnsignedValue x) = Just $ P.IntValue x-    getV (FloatValue x) = Just $ P.FloatValue x-    getV (BoolValue x) = Just $ P.BoolValue x-    putV (P.IntValue x) = SignedValue x-    putV (P.FloatValue x) = FloatValue x-    putV (P.BoolValue x) = BoolValue x-    putV P.Checked = BoolValue True--    getS (SignedValue x) = Just $ P.IntValue x-    getS _               = Nothing-    putS (P.IntValue x) = Just $ SignedValue x-    putS _              = Nothing--    getU (UnsignedValue x) = Just $ P.IntValue x-    getU _                 = Nothing-    putU (P.IntValue x) = Just $ UnsignedValue x-    putU _              = Nothing--    getF (FloatValue x) = Just $ P.FloatValue x-    getF _              = Nothing-    putF (P.FloatValue x) = Just $ FloatValue x-    putF _                = Nothing--    getB (BoolValue x) = Just $ P.BoolValue x-    getB _             = Nothing-    putB (P.BoolValue x) = Just $ BoolValue x-    putB _               = Nothing--    fun1 f =-      TermValue Nothing $ ValueFun $ \x -> f x-    fun2 f =-      TermValue Nothing $ ValueFun $ \x ->-      return $ ValueFun $ \y -> f x y-    fun2t f =-      TermValue Nothing $ ValueFun $ \v ->-      case fromTuple v of Just [x,y] -> f x y-                          _ -> error $ "Expected pair; got: " ++ pretty v-    fun3t f =-      TermValue Nothing $ ValueFun $ \v ->-      case fromTuple v of Just [x,y,z] -> f x y z-                          _ -> error $ "Expected triple; got: " ++ pretty v--    fun6t f =-      TermValue Nothing $ ValueFun $ \v ->-      case fromTuple v of Just [x,y,z,a,b,c] -> f x y z a b c-                          _ -> error $ "Expected sextuple; got: " ++ pretty v--    bopDef fs = fun2 $ \x y ->-      case (x, y) of-        (ValuePrim x', ValuePrim y')-          | Just z <- msum $ map (`bopDef'` (x', y')) fs -> do-              breakOnNaN [x', y'] z-              return $ ValuePrim z-        _ ->-          bad noLoc mempty $ "Cannot apply operator to arguments " <>-          quote (pretty x) <> " and " <> quote (pretty y) <> "."-      where bopDef' (valf, retf, op) (x, y) = do-              x' <- valf x-              y' <- valf y-              retf =<< op x' y'--    unopDef fs = fun1 $ \x ->-      case x of-        (ValuePrim x')-          | Just r <- msum $ map (`unopDef'` x') fs -> do-              breakOnNaN [x'] r-              return $ ValuePrim r-        _ ->-          bad noLoc mempty $ "Cannot apply function to argument " <>-          quote (pretty x) <> "."-      where unopDef' (valf, retf, op) x = do-              x' <- valf x-              retf =<< op x'--    tbopDef f = fun1 $ \v ->-      case fromTuple v of-        Just [ValuePrim x, ValuePrim y]-          | Just x' <- getV x,-            Just y' <- getV y,-            Just z <- putV <$> f x' y' -> do-              breakOnNaN [x, y] z-              return $ ValuePrim z-        _ ->-          bad noLoc mempty $ "Cannot apply operator to argument " <>-          quote (pretty v) <> "."--    def "!" = Just $ unopDef [ (getS, putS, P.doUnOp $ P.Complement Int8)-                             , (getS, putS, P.doUnOp $ P.Complement Int16)-                             , (getS, putS, P.doUnOp $ P.Complement Int32)-                             , (getS, putS, P.doUnOp $ P.Complement Int64)-                             , (getU, putU, P.doUnOp $ P.Complement Int8)-                             , (getU, putU, P.doUnOp $ P.Complement Int16)-                             , (getU, putU, P.doUnOp $ P.Complement Int32)-                             , (getU, putU, P.doUnOp $ P.Complement Int64)-                             , (getB, putB, P.doUnOp P.Not) ]--    def "+" = arithOp (`P.Add` P.OverflowWrap) P.FAdd-    def "-" = arithOp (`P.Sub` P.OverflowWrap) P.FSub-    def "*" = arithOp (`P.Mul` P.OverflowWrap) P.FMul-    def "**" = arithOp P.Pow P.FPow-    def "/" = Just $ bopDef $-              sintOp (`P.SDiv` P.Unsafe) ++-              uintOp (`P.UDiv` P.Unsafe) ++-              floatOp P.FDiv-    def "%" = Just $ bopDef $-              sintOp (`P.SMod` P.Unsafe) ++-              uintOp (`P.UMod` P.Unsafe) ++-              floatOp P.FMod-    def "//" = Just $ bopDef $-               sintOp (`P.SQuot` P.Unsafe) ++-               uintOp (`P.UDiv` P.Unsafe)-    def "%%" = Just $ bopDef $-               sintOp (`P.SRem` P.Unsafe) ++-               uintOp (`P.UMod` P.Unsafe)-    def "^" = Just $ bopDef $ intOp P.Xor-    def "&" = Just $ bopDef $ intOp P.And-    def "|" = Just $ bopDef $ intOp P.Or-    def ">>" = Just $ bopDef $ sintOp P.AShr ++ uintOp P.LShr-    def "<<" = Just $ bopDef $ intOp P.Shl-    def ">>>" = Just $ bopDef $ sintOp P.LShr ++ uintOp P.LShr-    def "==" = Just $ fun2 $-               \xs ys -> return $ ValuePrim $ BoolValue $ xs == ys-    def "!=" = Just $ fun2 $-               \xs ys -> return $ ValuePrim $ BoolValue $ xs /= ys--    -- The short-circuiting is handled directly in 'eval'; these cases-    -- are only used when partially applying and such.-    def "&&" = Just $ fun2 $ \x y ->-      return $ ValuePrim $ BoolValue $ asBool x && asBool y-    def "||" = Just $ fun2 $ \x y ->-      return $ ValuePrim $ BoolValue $ asBool x || asBool y--    def "<" = Just $ bopDef $-              sintCmp P.CmpSlt ++ uintCmp P.CmpUlt ++-              floatCmp P.FCmpLt ++ boolCmp P.CmpLlt-    def ">" = Just $ bopDef $ flipCmps $-              sintCmp P.CmpSlt ++ uintCmp P.CmpUlt ++-              floatCmp P.FCmpLt ++ boolCmp P.CmpLlt-    def "<=" = Just $ bopDef $-               sintCmp P.CmpSle ++ uintCmp P.CmpUle ++-               floatCmp P.FCmpLe ++ boolCmp P.CmpLle-    def ">=" = Just $ bopDef $ flipCmps $-               sintCmp P.CmpSle ++ uintCmp P.CmpUle ++-               floatCmp P.FCmpLe ++ boolCmp P.CmpLle--    def s-      | Just bop <- find ((s==) . pretty) P.allBinOps =-          Just $ tbopDef $ P.doBinOp bop-      | Just unop <- find ((s==) . pretty) P.allCmpOps =-          Just $ tbopDef $ \x y -> P.BoolValue <$> P.doCmpOp unop x y-      | Just cop <- find ((s==) . pretty) P.allConvOps =-          Just $ unopDef [(getV, Just . putV, P.doConvOp cop)]-      | Just unop <- find ((s==) . pretty) P.allUnOps =-          Just $ unopDef [(getV, Just . putV, P.doUnOp unop)]--      | Just (pts, _, f) <- M.lookup s P.primFuns =-          case length pts of-            1 -> Just $ unopDef [(getV, Just . putV, f . pure)]-            _ -> Just $ fun1 $ \x -> do-              let getV' (ValuePrim v) = Just v-                  getV' _ = Nothing-              case mapM getV' =<< fromTuple x of-                Just vs-                  | Just res <- fmap putV . f =<< mapM getV vs -> do-                      breakOnNaN vs res-                      return $ ValuePrim res-                _ ->-                  error $ "Cannot apply " ++ pretty s ++ " to " ++ pretty x--      | "sign_" `isPrefixOf` s =-          Just $ fun1 $ \x ->-          case x of (ValuePrim (UnsignedValue x')) ->-                      return $ ValuePrim $ SignedValue x'-                    _ -> error $ "Cannot sign: " ++ pretty x-      | "unsign_" `isPrefixOf` s =-          Just $ fun1 $ \x ->-          case x of (ValuePrim (SignedValue x')) ->-                      return $ ValuePrim $ UnsignedValue x'-                    _ -> error $ "Cannot unsign: " ++ pretty x--    def s | "map_stream" `isPrefixOf` s =-              Just $ fun2t stream--    def s | "reduce_stream" `isPrefixOf` s =-              Just $ fun3t $ \_ f arg -> stream f arg--    def "map" = Just $ TermPoly Nothing $ \t -> return $ ValueFun $ \v ->-      case (fromTuple v, unfoldFunType t) of-        (Just [f, xs], ([_], ret_t))-          | Just rowshape <- typeRowShape ret_t ->-              toArray' rowshape <$> mapM (apply noLoc mempty f) (snd $ fromArray xs)-          | otherwise ->-              error $ "Bad return type: " ++ pretty ret_t-        _ ->-          error $ "Invalid arguments to map intrinsic:\n" ++-          unlines [pretty t, pretty v]-      where typeRowShape = sequenceA . structTypeShape mempty . stripArray 1--    def s | "reduce" `isPrefixOf` s = Just $ fun3t $ \f ne xs ->-      foldM (apply2 noLoc mempty f) ne $ snd $ fromArray xs--    def "scan" = Just $ fun3t $ \f ne xs -> do-      let next (out, acc) x = do-            x' <- apply2 noLoc mempty f acc x-            return (x':out, x')-      toArray' (valueShape ne) . reverse . fst <$>-        foldM next ([], ne) (snd $ fromArray xs)--    def "scatter" = Just $ fun3t $ \arr is vs ->-      case arr of-        ValueArray shape arr' ->-          return $ ValueArray shape $ foldl' update arr' $-          zip (map asInt $ snd $ fromArray is) (snd $ fromArray vs)-        _ ->-          error $ "scatter expects array, but got: " ++ pretty arr-      where update arr' (i, v) =-              if i >= 0 && i < arrayLength arr'-              then arr' // [(i, v)] else arr'--    def "hist" = Just $ fun6t $ \_ arr fun _ is vs ->-      case arr of-        ValueArray shape arr' ->-          ValueArray shape <$> foldM (update fun) arr'-          (zip (map asInt $ snd $ fromArray is) (snd $ fromArray vs))-        _ ->-          error $ "hist expects array, but got: " ++ pretty arr-      where update fun arr' (i, v) =-              if i >= 0 && i < arrayLength arr'-              then do-                v' <- apply2 noLoc mempty fun (arr' ! i) v-                return $ arr' // [(i, v')]-              else return arr'--    def "partition" = Just $ fun3t $ \k f xs -> do-      let (ShapeDim _ rowshape, xs') = fromArray xs--          next outs x = do-            i <- asInt <$> apply noLoc mempty f x-            return $ insertAt i x outs-          pack parts =-            toTuple [toArray' rowshape $ concat parts,-                     toArray' rowshape $-                     map (ValuePrim . SignedValue . Int32Value . genericLength) parts]--      pack . map reverse <$>-        foldM next (replicate (asInt k) []) xs'-      where insertAt 0 x (l:ls) = (x:l):ls-            insertAt i x (l:ls) = l:insertAt (i-1) x ls-            insertAt _ _ ls = ls--    def "unzip" = Just $ fun1 $ \x -> do-      let ShapeDim _ (ShapeRecord fs) = valueShape x-          Just [xs_shape, ys_shape] = areTupleFields fs-          listPair (xs, ys) =-            [toArray' xs_shape xs, toArray' ys_shape ys]--      return $ toTuple $ listPair $ unzip $ map (fromPair . fromTuple) $ snd $ fromArray x-      where fromPair (Just [x,y]) = (x,y)-            fromPair l = error $ "Not a pair: " ++ pretty l--    def "zip" = Just $ fun2t $ \xs ys -> do-      let ShapeDim _ xs_rowshape = valueShape xs-          ShapeDim _ ys_rowshape = valueShape ys-      return $ toArray' (ShapeRecord (tupleFields [xs_rowshape, ys_rowshape])) $-        map toTuple $ transpose [snd $ fromArray xs, snd $ fromArray ys]--    def "concat" = Just $ fun2t $ \xs ys -> do-      let (ShapeDim _ rowshape, xs') = fromArray xs-          (_, ys') = fromArray ys-      return $ toArray' rowshape $ xs' ++ ys'--    def "transpose" = Just $ fun1 $ \xs -> do-      let (ShapeDim n (ShapeDim m shape), xs') = fromArray xs-      return $ toArray (ShapeDim m (ShapeDim n shape)) $-        map (toArray (ShapeDim n shape)) $ transpose $ map (snd . fromArray) xs'--    def "rotate" = Just $ fun2t $ \i xs -> do-      let (shape, xs') = fromArray xs-      return $-        if asInt i > 0-        then let (bef, aft) = splitAt (asInt i) xs'-             in toArray shape $ aft ++ bef-        else let (bef, aft) = splitFromEnd (-asInt i) xs'-             in toArray shape $ aft ++ bef--    def "flatten" = Just $ fun1 $ \xs -> do-      let (ShapeDim n (ShapeDim m shape), xs') = fromArray xs-      return $ toArray (ShapeDim (n*m) shape) $ concatMap (snd . fromArray) xs'--    def "unflatten" = Just $ fun3t $ \n m xs -> do-      let (ShapeDim _ innershape, xs') = fromArray xs-          rowshape = ShapeDim (asInt32 m) innershape-          shape = ShapeDim (asInt32 n) rowshape-      return $ toArray shape $ map (toArray rowshape) $ chunk (asInt m) xs'--    def "opaque" = Just $ fun1 return--    def "trace" = Just $ fun1 $ \v -> trace v >> return v--    def "break" = Just $ fun1 $ \v -> do-      break-      return v--    def s | nameFromString s `M.member` namesToPrimTypes = Nothing--    def s = error $ "Missing intrinsic: " ++ s--    tdef s = do-      t <- nameFromString s `M.lookup` namesToPrimTypes-      return $ T.TypeAbbr Unlifted [] $ Scalar $ Prim t--    stream f arg@(ValueArray _ xs) =-      let n = ValuePrim $ SignedValue $ Int32Value $ arrayLength xs-      in apply2 noLoc mempty f n arg-    stream _ arg = error $ "Cannot stream: " ++ pretty arg---interpretExp :: Ctx -> Exp -> F ExtOp Value-interpretExp ctx e = runEvalM (ctxImports ctx) $ eval (ctxEnv ctx) e--interpretDec :: Ctx -> Dec -> F ExtOp Ctx-interpretDec ctx d = do-  env <- runEvalM (ctxImports ctx) $ evalDec (ctxEnv ctx) d-  return ctx { ctxEnv = env }--interpretImport :: Ctx -> (FilePath, Prog) -> F ExtOp Ctx-interpretImport ctx (fp, prog) = do-  env <- runEvalM (ctxImports ctx) $ foldM evalDec (ctxEnv ctx) $ progDecs prog-  return ctx { ctxImports = M.insert fp env $ ctxImports ctx }--checkEntryArgs :: VName -> [F.Value] -> StructType -> Either String ()-checkEntryArgs entry args entry_t-  | args_ts == param_ts =-      return ()-  | otherwise =-      Left $ pretty $ expected </>-      "Got input of types" </>-      indent 2 (stack (map ppr args_ts))-  where (param_ts, _) = unfoldFunType entry_t-        args_ts = map (valueStructType . valueType) args-        expected-          | null param_ts =-              "Entry point " <> pquote (pprName entry) <> " is not a function."-          | otherwise =-              "Entry point " <> pquote (pprName entry) <> " expects input of type(s)" </>-              indent 2 (stack (map ppr param_ts))---- | Execute the named function on the given arguments; may fail--- horribly if these are ill-typed.-interpretFunction :: Ctx -> VName -> [F.Value] -> Either String (F ExtOp Value)-interpretFunction ctx fname vs = do-  ft <- case lookupVar (qualName fname) $ ctxEnv ctx of-          Just (TermValue (Just (T.BoundV _ t)) _) ->-            Right $ updateType (map valueType vs) t-          Just (TermPoly (Just (T.BoundV _ t)) _) ->-            Right $ updateType (map valueType vs) t-          _ ->-            Left $ "Unknown function `" <> prettyName fname <> "`."--  vs' <- case mapM convertValue vs of-           Just vs' -> Right vs'-           Nothing -> Left "Invalid input: irregular array."--  checkEntryArgs fname vs ft--  Right $ runEvalM (ctxImports ctx) $ do-    f <- evalTermVar (ctxEnv ctx) (qualName fname) ft-    foldM (apply noLoc mempty) f vs'--  where updateType (vt:vts) (Scalar (Arrow als u _ rt)) =-          Scalar $ Arrow als u (valueStructType vt) $ updateType vts rt-        updateType _ t = t--        convertValue (F.PrimValue p) = Just $ ValuePrim p-        convertValue (F.ArrayValue arr t) = mkArray t =<< mapM convertValue (elems arr)+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}++-- | An interpreter operating on type-checked source Futhark terms.+-- Relatively slow.+module Language.Futhark.Interpreter+  ( Ctx (..),+    Env,+    InterpreterError,+    initialCtx,+    interpretExp,+    interpretDec,+    interpretImport,+    interpretFunction,+    ExtOp (..),+    BreakReason (..),+    StackFrame (..),+    typeCheckerEnv,+    Value (ValuePrim, ValueArray, ValueRecord),+    fromTuple,+    isEmptyArray,+    prettyEmptyArray,+  )+where++import Control.Monad.Except+import Control.Monad.Free.Church+import Control.Monad.Reader+import Control.Monad.State+import Control.Monad.Trans.Maybe+import Data.Array+import Data.Bifunctor (first)+import Data.List+  ( find,+    foldl',+    genericLength,+    intercalate,+    isPrefixOf,+    transpose,+  )+import qualified Data.List.NonEmpty as NE+import qualified Data.Map as M+import Data.Maybe+import Data.Monoid hiding (Sum)+import Futhark.IR.Primitive (floatValue, intValue)+import qualified Futhark.IR.Primitive as P+import Futhark.Util (chunk, maybeHead, splitFromEnd)+import Futhark.Util.Loc+import Futhark.Util.Pretty hiding (apply, bool)+import Language.Futhark hiding (Value, matchDims)+import qualified Language.Futhark as F+import qualified Language.Futhark.Semantic as T+import Prelude hiding (break, mod)++data StackFrame = StackFrame+  { stackFrameLoc :: Loc,+    stackFrameCtx :: Ctx+  }++instance Located StackFrame where+  locOf = stackFrameLoc++-- | What is the reason for this break point?+data BreakReason+  = -- | An explicit breakpoint in the program.+    BreakPoint+  | -- | A+    BreakNaN++data ExtOp a+  = ExtOpTrace Loc String a+  | ExtOpBreak BreakReason (NE.NonEmpty StackFrame) a+  | ExtOpError InterpreterError++instance Functor ExtOp where+  fmap f (ExtOpTrace w s x) = ExtOpTrace w s $ f x+  fmap f (ExtOpBreak why backtrace x) = ExtOpBreak why backtrace $ f x+  fmap _ (ExtOpError err) = ExtOpError err++type Stack = [StackFrame]++type Sizes = M.Map VName Int64++-- | The monad in which evaluation takes place.+newtype EvalM a+  = EvalM+      ( ReaderT+          (Stack, M.Map FilePath Env)+          (StateT Sizes (F ExtOp))+          a+      )+  deriving+    ( Monad,+      Applicative,+      Functor,+      MonadFree ExtOp,+      MonadReader (Stack, M.Map FilePath Env),+      MonadState Sizes+    )++runEvalM :: M.Map FilePath Env -> EvalM a -> F ExtOp a+runEvalM imports (EvalM m) = evalStateT (runReaderT m (mempty, imports)) mempty++stacking :: SrcLoc -> Env -> EvalM a -> EvalM a+stacking loc env = local $ \(ss, imports) ->+  if isNoLoc loc+    then (ss, imports)+    else+      let s = StackFrame (locOf loc) (Ctx env imports)+       in (s : ss, imports)+  where+    isNoLoc :: SrcLoc -> Bool+    isNoLoc = (== NoLoc) . locOf++stacktrace :: EvalM [Loc]+stacktrace = asks $ map stackFrameLoc . fst++lookupImport :: FilePath -> EvalM (Maybe Env)+lookupImport f = asks $ M.lookup f . snd++putExtSize :: VName -> Int64 -> EvalM ()+putExtSize v x = modify $ M.insert v x++getSizes :: EvalM Sizes+getSizes = get++extSizeEnv :: EvalM Env+extSizeEnv = i64Env <$> getSizes++prettyRecord :: Pretty a => M.Map Name a -> Doc+prettyRecord m+  | Just vs <- areTupleFields m =+    parens $ commasep $ map ppr vs+  | otherwise =+    braces $ commasep $ map field $ M.toList m+  where+    field (k, v) = ppr k <+> equals <+> ppr v++valueStructType :: ValueType -> StructType+valueStructType = first (ConstDim . fromIntegral)++-- | A shape is a tree to accomodate the case of records.  It is+-- parameterised over the representation of dimensions.+data Shape d+  = ShapeDim d (Shape d)+  | ShapeLeaf+  | ShapeRecord (M.Map Name (Shape d))+  | ShapeSum (M.Map Name [Shape d])+  deriving (Eq, Show, Functor, Foldable, Traversable)++type ValueShape = Shape Int64++instance Pretty d => Pretty (Shape d) where+  ppr ShapeLeaf = mempty+  ppr (ShapeDim d s) = brackets (ppr d) <> ppr s+  ppr (ShapeRecord m) = prettyRecord m+  ppr (ShapeSum cs) =+    mconcat (punctuate (text " | ") cs')+    where+      ppConstr (name, fs) = sep $ (text "#" <> ppr name) : map ppr fs+      cs' = map ppConstr $ M.toList cs++emptyShape :: ValueShape -> Bool+emptyShape (ShapeDim d s) = d == 0 || emptyShape s+emptyShape _ = False++typeShape :: M.Map VName (Shape d) -> TypeBase d () -> Shape d+typeShape shapes = go+  where+    go (Array _ _ et shape) =+      foldr ShapeDim (go (Scalar et)) $ shapeDims shape+    go (Scalar (Record fs)) =+      ShapeRecord $ M.map go fs+    go (Scalar (Sum cs)) =+      ShapeSum $ M.map (map go) cs+    go (Scalar (TypeVar _ _ (TypeName [] v) []))+      | Just shape <- M.lookup v shapes =+        shape+    go _ =+      ShapeLeaf++structTypeShape :: M.Map VName ValueShape -> StructType -> Shape (Maybe Int64)+structTypeShape shapes = fmap dim . typeShape shapes'+  where+    dim (ConstDim d) = Just $ fromIntegral d+    dim _ = Nothing+    shapes' = M.map (fmap $ ConstDim . fromIntegral) shapes++resolveTypeParams :: [VName] -> StructType -> StructType -> Env+resolveTypeParams names = match+  where+    match (Scalar (TypeVar _ _ tn _)) t+      | typeLeaf tn `elem` names =+        typeEnv $ M.singleton (typeLeaf tn) t+    match (Scalar (Record poly_fields)) (Scalar (Record fields)) =+      mconcat $+        M.elems $+          M.intersectionWith match poly_fields fields+    match (Scalar (Sum poly_fields)) (Scalar (Sum fields)) =+      mconcat $+        map mconcat $+          M.elems $+            M.intersectionWith (zipWith match) poly_fields fields+    match (Scalar (Arrow _ _ poly_t1 poly_t2)) (Scalar (Arrow _ _ t1 t2)) =+      match poly_t1 t1 <> match poly_t2 t2+    match poly_t t+      | d1 : _ <- shapeDims (arrayShape poly_t),+        d2 : _ <- shapeDims (arrayShape t) =+        matchDims d1 d2 <> match (stripArray 1 poly_t) (stripArray 1 t)+    match _ _ = mempty++    matchDims (NamedDim (QualName _ d1)) (ConstDim d2)+      | d1 `elem` names =+        i64Env $ M.singleton d1 $ fromIntegral d2+    matchDims _ _ = mempty++resolveExistentials :: [VName] -> StructType -> ValueShape -> M.Map VName Int64+resolveExistentials names = match+  where+    match (Scalar (Record poly_fields)) (ShapeRecord fields) =+      mconcat $+        M.elems $+          M.intersectionWith match poly_fields fields+    match (Scalar (Sum poly_fields)) (ShapeSum fields) =+      mconcat $+        map mconcat $+          M.elems $+            M.intersectionWith (zipWith match) poly_fields fields+    match poly_t (ShapeDim d2 rowshape)+      | d1 : _ <- shapeDims (arrayShape poly_t) =+        matchDims d1 d2 <> match (stripArray 1 poly_t) rowshape+    match _ _ = mempty++    matchDims (NamedDim (QualName _ d1)) d2+      | d1 `elem` names = M.singleton d1 d2+    matchDims _ _ = mempty++-- | A fully evaluated Futhark value.+data Value+  = ValuePrim !PrimValue+  | ValueArray ValueShape !(Array Int Value)+  | -- Stores the full shape.+    ValueRecord (M.Map Name Value)+  | ValueFun (Value -> EvalM Value)+  | ValueSum ValueShape Name [Value]++-- Stores the full shape.++instance Eq Value where+  ValuePrim x == ValuePrim y = x == y+  ValueArray _ x == ValueArray _ y = x == y+  ValueRecord x == ValueRecord y = x == y+  ValueSum _ n1 vs1 == ValueSum _ n2 vs2 = n1 == n2 && vs1 == vs2+  _ == _ = False++instance Pretty Value where+  ppr (ValuePrim v) = ppr v+  ppr (ValueArray _ a) =+    let elements = elems a -- [Value]+        (x : _) = elements+        separator = case x of+          ValueArray _ _ -> comma <> line+          _ -> comma <> space+     in brackets $ cat $ punctuate separator (map ppr elements)+  ppr (ValueRecord m) = prettyRecord m+  ppr ValueFun {} = text "#<fun>"+  ppr (ValueSum _ n vs) = text "#" <> sep (ppr n : map ppr vs)++valueShape :: Value -> ValueShape+valueShape (ValueArray shape _) = shape+valueShape (ValueRecord fs) = ShapeRecord $ M.map valueShape fs+valueShape (ValueSum shape _ _) = shape+valueShape _ = ShapeLeaf++checkShape :: Shape (Maybe Int64) -> ValueShape -> Maybe ValueShape+checkShape (ShapeDim Nothing shape1) (ShapeDim d2 shape2) =+  ShapeDim d2 <$> checkShape shape1 shape2+checkShape (ShapeDim (Just d1) shape1) (ShapeDim d2 shape2) = do+  guard $ d1 == d2+  ShapeDim d2 <$> checkShape shape1 shape2+checkShape (ShapeDim d1 shape1) ShapeLeaf =+  -- This case is for handling polymorphism, when a function doesn't+  -- know that the array it produced actually has more dimensions.+  ShapeDim (fromMaybe 0 d1) <$> checkShape shape1 ShapeLeaf+checkShape (ShapeRecord shapes1) (ShapeRecord shapes2) =+  ShapeRecord <$> sequence (M.intersectionWith checkShape shapes1 shapes2)+checkShape (ShapeRecord shapes1) ShapeLeaf =+  Just $ fromMaybe 0 <$> ShapeRecord shapes1+checkShape (ShapeSum shapes1) (ShapeSum shapes2) =+  ShapeSum <$> sequence (M.intersectionWith (zipWithM checkShape) shapes1 shapes2)+checkShape (ShapeSum shapes1) ShapeLeaf =+  Just $ fromMaybe 0 <$> ShapeSum shapes1+checkShape _ shape2 =+  Just shape2++-- | Does the value correspond to an empty array?+isEmptyArray :: Value -> Bool+isEmptyArray = emptyShape . valueShape++-- | String representation of an empty array with the provided element+-- type.  This is pretty ad-hoc - don't expect good results unless the+-- element type is a primitive.+prettyEmptyArray :: TypeBase () () -> Value -> String+prettyEmptyArray t v =+  "empty(" ++ dims (valueShape v) ++ pretty t' ++ ")"+  where+    t' = stripArray (arrayRank t) t+    dims (ShapeDim n rowshape) =+      "[" ++ pretty n ++ "]" ++ dims rowshape+    dims _ = ""++-- | Create an array value; failing if that would result in an+-- irregular array.+mkArray :: TypeBase Int64 () -> [Value] -> Maybe Value+mkArray t [] =+  return $ toArray (typeShape mempty t) []+mkArray _ (v : vs) = do+  let v_shape = valueShape v+  guard $ all ((== v_shape) . valueShape) vs+  return $ toArray' v_shape $ v : vs++arrayLength :: Integral int => Array Int Value -> int+arrayLength = fromIntegral . (+ 1) . snd . bounds++toTuple :: [Value] -> Value+toTuple = ValueRecord . M.fromList . zip tupleFieldNames++fromTuple :: Value -> Maybe [Value]+fromTuple (ValueRecord m) = areTupleFields m+fromTuple _ = Nothing++asInteger :: Value -> Integer+asInteger (ValuePrim (SignedValue v)) = P.valueIntegral v+asInteger (ValuePrim (UnsignedValue v)) =+  toInteger (P.valueIntegral (P.doZExt v Int64) :: Word64)+asInteger v = error $ "Unexpectedly not an integer: " ++ pretty v++asInt :: Value -> Int+asInt = fromIntegral . asInteger++asSigned :: Value -> IntValue+asSigned (ValuePrim (SignedValue v)) = v+asSigned v = error $ "Unexpected not a signed integer: " ++ pretty v++asInt64 :: Value -> Int64+asInt64 = fromIntegral . asInteger++asBool :: Value -> Bool+asBool (ValuePrim (BoolValue x)) = x+asBool v = error $ "Unexpectedly not a boolean: " ++ pretty v++lookupInEnv ::+  (Env -> M.Map VName x) ->+  QualName VName ->+  Env ->+  Maybe x+lookupInEnv onEnv qv env = f env $ qualQuals qv+  where+    f m (q : qs) =+      case M.lookup q $ envTerm m of+        Just (TermModule (Module mod)) -> f mod qs+        _ -> Nothing+    f m [] = M.lookup (qualLeaf qv) $ onEnv m++lookupVar :: QualName VName -> Env -> Maybe TermBinding+lookupVar = lookupInEnv envTerm++lookupType :: QualName VName -> Env -> Maybe T.TypeBinding+lookupType = lookupInEnv envType++-- | A TermValue with a 'Nothing' type annotation is an intrinsic.+data TermBinding+  = TermValue (Maybe T.BoundV) Value+  | -- | A polymorphic value that must be instantiated.+    TermPoly (Maybe T.BoundV) (StructType -> EvalM Value)+  | TermModule Module++data Module+  = Module Env+  | ModuleFun (Module -> EvalM Module)++-- | The actual type- and value environment.+data Env = Env+  { envTerm :: M.Map VName TermBinding,+    envType :: M.Map VName T.TypeBinding,+    -- | A mapping from type parameters to the shapes of+    -- the value to which they were initially bound.+    envShapes :: M.Map VName ValueShape+  }++instance Monoid Env where+  mempty = Env mempty mempty mempty++instance Semigroup Env where+  Env vm1 tm1 sm1 <> Env vm2 tm2 sm2 =+    Env (vm1 <> vm2) (tm1 <> tm2) (sm1 <> sm2)++-- | An error occurred during interpretation due to an error in the+-- user program.  Actual interpreter errors will be signaled with an+-- IO exception ('error').+newtype InterpreterError = InterpreterError String++valEnv :: M.Map VName (Maybe T.BoundV, Value) -> Env+valEnv m =+  Env+    { envTerm = M.map (uncurry TermValue) m,+      envType = mempty,+      envShapes = mempty+    }++modEnv :: M.Map VName Module -> Env+modEnv m =+  Env+    { envTerm = M.map TermModule m,+      envType = mempty,+      envShapes = mempty+    }++typeEnv :: M.Map VName StructType -> Env+typeEnv m =+  Env+    { envTerm = mempty,+      envType = M.map tbind m,+      envShapes = mempty+    }+  where+    tbind = T.TypeAbbr Unlifted []++i64Env :: M.Map VName Int64 -> Env+i64Env = valEnv . M.map f+  where+    f x =+      ( Just $ T.BoundV [] $ Scalar $ Prim $ Signed Int64,+        ValuePrim $ SignedValue $ Int64Value x+      )++instance Show InterpreterError where+  show (InterpreterError s) = s++bad :: SrcLoc -> Env -> String -> EvalM a+bad loc env s = stacking loc env $ do+  ss <- map (locStr . srclocOf) <$> stacktrace+  liftF $ ExtOpError $ InterpreterError $ "Error at\n" ++ prettyStacktrace 0 ss ++ s++trace :: Value -> EvalM ()+trace v = do+  -- We take the second-to-top element of the stack, because any+  -- actual call to 'implicits.trace' is going to be in the trace+  -- function in the prelude, which is not interesting.+  top <- fromMaybe noLoc . maybeHead . drop 1 <$> stacktrace+  liftF $ ExtOpTrace top (prettyOneLine v) ()++typeCheckerEnv :: Env -> T.Env+typeCheckerEnv env =+  -- FIXME: some shadowing issues are probably not right here.+  let valMap (TermValue (Just t) _) = Just t+      valMap _ = Nothing+      vtable = M.mapMaybe valMap $ envTerm env+      nameMap k+        | k `M.member` vtable = Just ((T.Term, baseName k), qualName k)+        | otherwise = Nothing+   in mempty+        { T.envNameMap = M.fromList $ mapMaybe nameMap $ M.keys $ envTerm env,+          T.envVtable = vtable+        }++break :: EvalM ()+break = do+  -- We don't want the env of the function that is calling+  -- intrinsics.break, since that is just going to be the boring+  -- wrapper function (intrinsics are never called directly).+  -- This is why we go a step up the stack.+  backtrace <- asks $ drop 1 . fst+  case NE.nonEmpty backtrace of+    Nothing -> return ()+    Just backtrace' -> liftF $ ExtOpBreak BreakPoint backtrace' ()++fromArray :: Value -> (ValueShape, [Value])+fromArray (ValueArray shape as) = (shape, elems as)+fromArray v = error $ "Expected array value, but found: " ++ pretty v++toArray :: ValueShape -> [Value] -> Value+toArray shape vs = ValueArray shape (listArray (0, length vs - 1) vs)++toArray' :: ValueShape -> [Value] -> Value+toArray' rowshape vs = ValueArray shape (listArray (0, length vs - 1) vs)+  where+    shape = ShapeDim (genericLength vs) rowshape++apply :: SrcLoc -> Env -> Value -> Value -> EvalM Value+apply loc env (ValueFun f) v = stacking loc env (f v)+apply _ _ f _ = error $ "Cannot apply non-function: " ++ pretty f++apply2 :: SrcLoc -> Env -> Value -> Value -> Value -> EvalM Value+apply2 loc env f x y = stacking loc env $ do+  f' <- apply noLoc mempty f x+  apply noLoc mempty f' y++matchPattern :: Env -> Pattern -> Value -> EvalM Env+matchPattern env p v = do+  m <- runMaybeT $ patternMatch env p v+  case m of+    Nothing -> error $ "matchPattern: missing case for " ++ pretty p ++ " and " ++ pretty v+    Just env' -> return env'++patternMatch :: Env -> Pattern -> Value -> MaybeT EvalM Env+patternMatch env (Id v (Info t) _) val =+  lift $+    pure $+      valEnv (M.singleton v (Just $ T.BoundV [] $ toStruct t, val)) <> env+patternMatch env Wildcard {} _ =+  lift $ pure env+patternMatch env (TuplePattern ps _) (ValueRecord vs) =+  foldM (\env' (p, v) -> patternMatch env' p v) env $+    zip ps (map snd $ sortFields vs)+patternMatch env (RecordPattern ps _) (ValueRecord vs) =+  foldM (\env' (p, v) -> patternMatch env' p v) env $+    M.intersectionWith (,) (M.fromList ps) vs+patternMatch env (PatternParens p _) v = patternMatch env p v+patternMatch env (PatternAscription p _ _) v =+  patternMatch env p v+patternMatch env (PatternLit e _ _) v = do+  v' <- lift $ eval env e+  if v == v'+    then pure env+    else mzero+patternMatch env (PatternConstr n _ ps _) (ValueSum _ n' vs)+  | n == n' =+    foldM (\env' (p, v) -> patternMatch env' p v) env $ zip ps vs+patternMatch _ _ _ = mzero++data Indexing+  = IndexingFix Int64+  | IndexingSlice (Maybe Int64) (Maybe Int64) (Maybe Int64)++instance Pretty Indexing where+  ppr (IndexingFix i) = ppr i+  ppr (IndexingSlice i j (Just s)) =+    maybe mempty ppr i <> text ":"+      <> maybe mempty ppr j+      <> text ":"+      <> ppr s+  ppr (IndexingSlice i (Just j) s) =+    maybe mempty ppr i <> text ":"+      <> ppr j+      <> maybe mempty ((text ":" <>) . ppr) s+  ppr (IndexingSlice i Nothing Nothing) =+    maybe mempty ppr i <> text ":"++indexesFor ::+  Maybe Int64 ->+  Maybe Int64 ->+  Maybe Int64 ->+  Int64 ->+  Maybe [Int]+indexesFor start end stride n+  | (start', end', stride') <- slice,+    end' == start' || signum' (end' - start') == signum' stride',+    stride' /= 0,+    is <- [start', start' + stride' .. end' - signum stride'],+    all inBounds is =+    Just $ map fromIntegral is+  | otherwise =+    Nothing+  where+    inBounds i = i >= 0 && i < n++    slice =+      case (start, end, stride) of+        (Just start', _, _) ->+          let end' = fromMaybe n end+           in (start', end', fromMaybe 1 stride)+        (Nothing, Just end', _) ->+          let start' = 0+           in (start', end', fromMaybe 1 stride)+        (Nothing, Nothing, Just stride') ->+          ( if stride' > 0 then 0 else n -1,+            if stride' > 0 then n else -1,+            stride'+          )+        (Nothing, Nothing, Nothing) ->+          (0, n, 1)++-- | 'signum', but with 0 as 1.+signum' :: (Eq p, Num p) => p -> p+signum' 0 = 1+signum' x = signum x++indexShape :: [Indexing] -> ValueShape -> ValueShape+indexShape (IndexingFix {} : is) (ShapeDim _ shape) =+  indexShape is shape+indexShape (IndexingSlice start end stride : is) (ShapeDim d shape) =+  ShapeDim n $ indexShape is shape+  where+    n = maybe 0 genericLength $ indexesFor start end stride d+indexShape _ shape =+  shape++indexArray :: [Indexing] -> Value -> Maybe Value+indexArray (IndexingFix i : is) (ValueArray _ arr)+  | i >= 0,+    i < n =+    indexArray is $ arr ! fromIntegral i+  | otherwise =+    Nothing+  where+    n = arrayLength arr+indexArray (IndexingSlice start end stride : is) (ValueArray (ShapeDim _ rowshape) arr) = do+  js <- indexesFor start end stride $ arrayLength arr+  toArray' (indexShape is rowshape) <$> mapM (indexArray is . (arr !)) js+indexArray _ v = Just v++updateArray :: [Indexing] -> Value -> Value -> Maybe Value+updateArray (IndexingFix i : is) (ValueArray shape arr) v+  | i >= 0,+    i < n = do+    v' <- updateArray is (arr ! i') v+    Just $ ValueArray shape $ arr // [(i', v')]+  | otherwise =+    Nothing+  where+    n = arrayLength arr+    i' = fromIntegral i+updateArray (IndexingSlice start end stride : is) (ValueArray shape arr) (ValueArray _ v) = do+  arr_is <- indexesFor start end stride $ arrayLength arr+  guard $ length arr_is == arrayLength v+  let update arr' (i, v') = do+        x <- updateArray is (arr ! i) v'+        return $ arr' // [(i, x)]+  fmap (ValueArray shape) $ foldM update arr $ zip arr_is $ elems v+updateArray _ _ v = Just v++evalDimIndex :: Env -> DimIndex -> EvalM Indexing+evalDimIndex env (DimFix x) =+  IndexingFix . asInt64 <$> eval env x+evalDimIndex env (DimSlice start end stride) =+  IndexingSlice <$> traverse (fmap asInt64 . eval env) start+    <*> traverse (fmap asInt64 . eval env) end+    <*> traverse (fmap asInt64 . eval env) stride++evalIndex :: SrcLoc -> Env -> [Indexing] -> Value -> EvalM Value+evalIndex loc env is arr = do+  let oob =+        bad loc env $+          "Index [" <> intercalate ", " (map pretty is)+            <> "] out of bounds for array of shape "+            <> pretty (valueShape arr)+            <> "."+  maybe oob return $ indexArray is arr++-- | Expand type based on information that was not available at+-- type-checking time (the structure of abstract types).+evalType :: Env -> StructType -> StructType+evalType _ (Scalar (Prim pt)) = Scalar $ Prim pt+evalType env (Scalar (Record fs)) = Scalar $ Record $ fmap (evalType env) fs+evalType env (Scalar (Arrow () p t1 t2)) =+  Scalar $ Arrow () p (evalType env t1) (evalType env t2)+evalType env t@(Array _ u _ shape) =+  let et = stripArray (shapeRank shape) t+      et' = evalType env et+      shape' = fmap evalDim shape+   in arrayOf et' shape' u+  where+    evalDim (NamedDim qn)+      | Just (TermValue _ (ValuePrim (SignedValue (Int64Value x)))) <-+          lookupVar qn env =+        ConstDim $ fromIntegral x+    evalDim d = d+evalType env t@(Scalar (TypeVar () _ tn args)) =+  case lookupType (qualNameFromTypeName tn) env of+    Just (T.TypeAbbr _ ps t') ->+      let (substs, types) = mconcat $ zipWith matchPtoA ps args+          onDim (NamedDim v) = fromMaybe (NamedDim v) $ M.lookup (qualLeaf v) substs+          onDim d = d+       in if null ps+            then first onDim t'+            else evalType (Env mempty types mempty <> env) $ first onDim t'+    Nothing -> t+  where+    matchPtoA (TypeParamDim p _) (TypeArgDim (NamedDim qv) _) =+      (M.singleton p $ NamedDim qv, mempty)+    matchPtoA (TypeParamDim p _) (TypeArgDim (ConstDim k) _) =+      (M.singleton p $ ConstDim k, mempty)+    matchPtoA (TypeParamType l p _) (TypeArgType t' _) =+      let t'' = evalType env t'+       in (mempty, M.singleton p $ T.TypeAbbr l [] t'')+    matchPtoA _ _ = mempty+evalType env (Scalar (Sum cs)) = Scalar $ Sum $ (fmap . fmap) (evalType env) cs++evalTermVar :: Env -> QualName VName -> StructType -> EvalM Value+evalTermVar env qv t =+  case lookupVar qv env of+    Just (TermPoly _ v) -> do+      size_env <- extSizeEnv+      v $ evalType (size_env <> env) t+    Just (TermValue _ v) -> return v+    _ -> error $ "`" <> pretty qv <> "` is not bound to a value."++typeValueShape :: Env -> StructType -> EvalM ValueShape+typeValueShape env t = do+  size_env <- extSizeEnv+  let t' = evalType (size_env <> env) t+  case traverse dim $ typeShape mempty t' of+    Nothing -> error $ "typeValueShape: failed to fully evaluate type " ++ pretty t'+    Just shape -> return shape+  where+    dim (ConstDim x) = Just $ fromIntegral x+    dim _ = Nothing++evalFunction :: Env -> [VName] -> [Pattern] -> Exp -> StructType -> EvalM Value+-- We treat zero-parameter lambdas as simply an expression to+-- evaluate immediately.  Note that this is *not* the same as a lambda+-- that takes an empty tuple '()' as argument!  Zero-parameter lambdas+-- can never occur in a well-formed Futhark program, but they are+-- convenient in the interpreter.+evalFunction env _ [] body rettype =+  -- Eta-expand the rest to make any sizes visible.+  etaExpand [] env rettype+  where+    etaExpand vs env' (Scalar (Arrow _ _ pt rt)) =+      return $+        ValueFun $ \v -> do+          env'' <- matchPattern env' (Wildcard (Info $ fromStruct pt) noLoc) v+          etaExpand (v : vs) env'' rt+    etaExpand vs env' _ = do+      f <- eval env' body+      foldM (apply noLoc mempty) f $ reverse vs+evalFunction env missing_sizes (p : ps) body rettype =+  return $+    ValueFun $ \v -> do+      env' <- matchPattern env p v+      -- Fix up the last sizes, if any.+      let env''+            | null missing_sizes = env'+            | otherwise =+              env'+                <> i64Env+                  ( resolveExistentials+                      missing_sizes+                      (patternStructType p)+                      (valueShape v)+                  )+      evalFunction env'' missing_sizes ps body rettype++evalFunctionBinding ::+  Env ->+  [TypeParam] ->+  [Pattern] ->+  StructType ->+  [VName] ->+  Exp ->+  EvalM TermBinding+evalFunctionBinding env tparams ps ret retext fbody = do+  let ret' = evalType env ret+      arrow (xp, xt) yt = Scalar $ Arrow () xp xt yt+      ftype = foldr (arrow . patternParam) ret' ps++  -- Distinguish polymorphic and non-polymorphic bindings here.+  if null tparams+    then+      TermValue (Just $ T.BoundV [] ftype)+        <$> (returned env ret retext =<< evalFunction env [] ps fbody ret')+    else return $+      TermPoly (Just $ T.BoundV [] ftype) $ \ftype' -> do+        let tparam_names = map typeParamName tparams+            env' = resolveTypeParams tparam_names ftype ftype' <> env++            -- In some cases (abstract lifted types) there may be+            -- missing sizes that were not fixed by the type+            -- instantiation.  These will have to be set by looking+            -- at the actual function arguments.+            missing_sizes =+              filter (`M.notMember` envTerm env') $+                map typeParamName (filter isSizeParam tparams)+        returned env ret retext =<< evalFunction env' missing_sizes ps fbody ret'++evalArg :: Env -> Exp -> Maybe VName -> EvalM Value+evalArg env e ext = do+  v <- eval env e+  case ext of+    Just ext' -> putExtSize ext' $ asInt64 v+    Nothing -> return ()+  return v++returned :: Env -> TypeBase (DimDecl VName) als -> [VName] -> Value -> EvalM Value+returned _ _ [] v = return v+returned env ret retext v = do+  mapM_ (uncurry putExtSize) $+    M.toList $+      resolveExistentials retext (evalType env $ toStruct ret) $ valueShape v+  return v++eval :: Env -> Exp -> EvalM Value+eval _ (Literal v _) = return $ ValuePrim v+eval env (Parens e _) = eval env e+eval env (QualParens (qv, _) e loc) = do+  m <- evalModuleVar env qv+  case m of+    ModuleFun {} -> error $ "Local open of module function at " ++ locStr loc+    Module m' -> eval (m' <> env) e+eval env (TupLit vs _) = toTuple <$> mapM (eval env) vs+eval env (RecordLit fields _) =+  ValueRecord . M.fromList <$> mapM evalField fields+  where+    evalField (RecordFieldExplicit k e _) = do+      v <- eval env e+      return (k, v)+    evalField (RecordFieldImplicit k t loc) = do+      v <- eval env $ Var (qualName k) t loc+      return (baseName k, v)+eval _ (StringLit vs _) =+  return $+    toArray' ShapeLeaf $+      map (ValuePrim . UnsignedValue . Int8Value . fromIntegral) vs+eval env (ArrayLit [] (Info t) _) = do+  t' <- typeValueShape env $ toStruct t+  return $ toArray t' []+eval env (ArrayLit (v : vs) _ _) = do+  v' <- eval env v+  vs' <- mapM (eval env) vs+  return $ toArray' (valueShape v') (v' : vs')+eval env (Range start maybe_second end (Info t, Info retext) loc) = do+  start' <- asInteger <$> eval env start+  maybe_second' <- traverse (fmap asInteger . eval env) maybe_second+  end' <- traverse (fmap asInteger . eval env) end++  let (end_adj, step, ok) =+        case (end', maybe_second') of+          (DownToExclusive end'', Nothing) ->+            (end'' + 1, -1, start' >= end'')+          (DownToExclusive end'', Just second') ->+            (end'' + 1, second' - start', start' >= end'' && second' < start')+          (ToInclusive end'', Nothing) ->+            (end'', 1, start' <= end'')+          (ToInclusive end'', Just second')+            | second' > start' ->+              (end'', second' - start', start' <= end'')+            | otherwise ->+              (end'', second' - start', start' >= end'' && second' /= start')+          (UpToExclusive x, Nothing) ->+            (x -1, 1, start' <= x)+          (UpToExclusive x, Just second') ->+            (x -1, second' - start', start' <= x && second' > start')++  if ok+    then+      returned env t retext $+        toArray' ShapeLeaf $ map toInt [start', start' + step .. end_adj]+    else bad loc env $ badRange start' maybe_second' end'+  where+    toInt =+      case stripArray 1 t of+        Scalar (Prim (Signed t')) ->+          ValuePrim . SignedValue . intValue t'+        Scalar (Prim (Unsigned t')) ->+          ValuePrim . UnsignedValue . intValue t'+        _ -> error $ "Nonsensical range type: " ++ show t++    badRange start' maybe_second' end' =+      "Range " ++ pretty start'+        ++ ( case maybe_second' of+               Nothing -> ""+               Just second' -> ".." ++ pretty second'+           )+        ++ ( case end' of+               DownToExclusive x -> "..>" ++ pretty x+               ToInclusive x -> "..." ++ pretty x+               UpToExclusive x -> "..<" ++ pretty x+           )+        ++ " is invalid."+eval env (Var qv (Info t) _) = evalTermVar env qv (toStruct t)+eval env (Ascript e _ _) = eval env e+eval env (Coerce e td (Info ret, Info retext) loc) = do+  v <- returned env ret retext =<< eval env e+  let t = evalType env $ unInfo $ expandedType td+  case checkShape (structTypeShape (envShapes env) t) (valueShape v) of+    Just _ -> return v+    Nothing ->+      bad loc env $+        "Value `" <> pretty v <> "` of shape `" ++ pretty (valueShape v)+          ++ "` cannot match shape of type `"+          <> pretty (declaredType td)+          <> "` (`"+          <> pretty t+          <> "`)"+eval env (LetPat p e body (Info ret, Info retext) _) = do+  v <- eval env e+  env' <- matchPattern env p v+  returned env ret retext =<< eval env' body+eval env (LetFun f (tparams, ps, _, Info ret, fbody) body _ _) = do+  binding <- evalFunctionBinding env tparams ps ret [] fbody+  eval (env {envTerm = M.insert f binding $ envTerm env}) body+eval _ (IntLit v (Info t) _) =+  case t of+    Scalar (Prim (Signed it)) ->+      return $ ValuePrim $ SignedValue $ intValue it v+    Scalar (Prim (Unsigned it)) ->+      return $ ValuePrim $ UnsignedValue $ intValue it v+    Scalar (Prim (FloatType ft)) ->+      return $ ValuePrim $ FloatValue $ floatValue ft v+    _ -> error $ "eval: nonsensical type for integer literal: " ++ pretty t+eval _ (FloatLit v (Info t) _) =+  case t of+    Scalar (Prim (FloatType ft)) ->+      return $ ValuePrim $ FloatValue $ floatValue ft v+    _ -> error $ "eval: nonsensical type for float literal: " ++ pretty t+eval+  env+  ( BinOp+      (op, _)+      op_t+      (x, Info (_, xext))+      (y, Info (_, yext))+      (Info t)+      (Info retext)+      loc+    )+    | baseString (qualLeaf op) == "&&" = do+      x' <- asBool <$> eval env x+      if x'+        then eval env y+        else return $ ValuePrim $ BoolValue False+    | baseString (qualLeaf op) == "||" = do+      x' <- asBool <$> eval env x+      if x'+        then return $ ValuePrim $ BoolValue True+        else eval env y+    | otherwise = do+      op' <- eval env $ Var op op_t loc+      x' <- evalArg env x xext+      y' <- evalArg env y yext+      returned env t retext =<< apply2 loc env op' x' y'+eval env (If cond e1 e2 (Info ret, Info retext) _) = do+  cond' <- asBool <$> eval env cond+  returned env ret retext+    =<< if cond' then eval env e1 else eval env e2+eval env (Apply f x (Info (_, ext)) (Info t, Info retext) loc) = do+  -- It is important that 'x' is evaluated first in order to bring any+  -- sizes into scope that may be used in the type of 'f'.+  x' <- evalArg env x ext+  f' <- eval env f+  returned env t retext =<< apply loc env f' x'+eval env (Negate e _) = do+  ev <- eval env e+  ValuePrim <$> case ev of+    ValuePrim (SignedValue (Int8Value v)) -> return $ SignedValue $ Int8Value (- v)+    ValuePrim (SignedValue (Int16Value v)) -> return $ SignedValue $ Int16Value (- v)+    ValuePrim (SignedValue (Int32Value v)) -> return $ SignedValue $ Int32Value (- v)+    ValuePrim (SignedValue (Int64Value v)) -> return $ SignedValue $ Int64Value (- v)+    ValuePrim (UnsignedValue (Int8Value v)) -> return $ UnsignedValue $ Int8Value (- v)+    ValuePrim (UnsignedValue (Int16Value v)) -> return $ UnsignedValue $ Int16Value (- v)+    ValuePrim (UnsignedValue (Int32Value v)) -> return $ UnsignedValue $ Int32Value (- v)+    ValuePrim (UnsignedValue (Int64Value v)) -> return $ UnsignedValue $ Int64Value (- v)+    ValuePrim (FloatValue (Float32Value v)) -> return $ FloatValue $ Float32Value (- v)+    ValuePrim (FloatValue (Float64Value v)) -> return $ FloatValue $ Float64Value (- v)+    _ -> error $ "Cannot negate " ++ pretty ev+eval env (Index e is (Info t, Info retext) loc) = do+  is' <- mapM (evalDimIndex env) is+  arr <- eval env e+  returned env t retext =<< evalIndex loc env is' arr+eval env (Update src is v loc) =+  maybe oob return+    =<< updateArray <$> mapM (evalDimIndex env) is <*> eval env src <*> eval env v+  where+    oob = bad loc env "Bad update"+eval env (RecordUpdate src all_fs v _ _) =+  update <$> eval env src <*> pure all_fs <*> eval env v+  where+    update _ [] v' = v'+    update (ValueRecord src') (f : fs) v'+      | Just f_v <- M.lookup f src' =+        ValueRecord $ M.insert f (update f_v fs v') src'+    update _ _ _ = error "eval RecordUpdate: invalid value."+eval env (LetWith dest src is v body _ loc) = do+  let Ident src_vn (Info src_t) _ = src+  dest' <-+    maybe oob return+      =<< updateArray <$> mapM (evalDimIndex env) is+      <*> evalTermVar env (qualName src_vn) (toStruct src_t)+      <*> eval env v+  let t = T.BoundV [] $ toStruct $ unInfo $ identType dest+  eval (valEnv (M.singleton (identName dest) (Just t, dest')) <> env) body+  where+    oob = bad loc env "Bad update"++-- We treat zero-parameter lambdas as simply an expression to+-- evaluate immediately.  Note that this is *not* the same as a lambda+-- that takes an empty tuple '()' as argument!  Zero-parameter lambdas+-- can never occur in a well-formed Futhark program, but they are+-- convenient in the interpreter.+eval env (Lambda ps body _ (Info (_, rt)) _) =+  evalFunction env [] ps body rt+eval env (OpSection qv (Info t) _) = evalTermVar env qv $ toStruct t+eval env (OpSectionLeft qv _ e (Info (_, argext), _) (Info t, Info retext) loc) = do+  v <- evalArg env e argext+  f <- evalTermVar env qv (toStruct t)+  returned env t retext =<< apply loc env f v+eval env (OpSectionRight qv _ e (Info _, Info (_, argext)) (Info t) loc) = do+  y <- evalArg env e argext+  return $+    ValueFun $ \x -> do+      f <- evalTermVar env qv $ toStruct t+      apply2 loc env f x y+eval env (IndexSection is _ loc) = do+  is' <- mapM (evalDimIndex env) is+  return $ ValueFun $ evalIndex loc env is'+eval _ (ProjectSection ks _ _) =+  return $ ValueFun $ flip (foldM walk) ks+  where+    walk (ValueRecord fs) f+      | Just v' <- M.lookup f fs = return v'+    walk _ _ = error "Value does not have expected field."+eval env (DoLoop sparams pat init_e form body (Info (ret, retext)) _) = do+  init_v <- eval env init_e+  returned env ret retext+    =<< case form of+      For iv bound -> do+        bound' <- asSigned <$> eval env bound+        forLoop (identName iv) bound' (zero bound') init_v+      ForIn in_pat in_e -> do+        (_, in_vs) <- fromArray <$> eval env in_e+        foldM (forInLoop in_pat) init_v in_vs+      While cond ->+        whileLoop cond init_v+  where+    withLoopParams v =+      let sparams' =+            resolveExistentials+              sparams+              (patternStructType pat)+              (valueShape v)+       in matchPattern (i64Env sparams' <> env) pat v++    inc = (`P.doAdd` Int64Value 1)+    zero = (`P.doMul` Int64Value 0)++    forLoop iv bound i v+      | i >= bound = return v+      | otherwise = do+        env' <- withLoopParams v+        forLoop iv bound (inc i)+          =<< eval+            ( valEnv+                ( M.singleton+                    iv+                    ( Just $ T.BoundV [] $ Scalar $ Prim $ Signed Int64,+                      ValuePrim (SignedValue i)+                    )+                )+                <> env'+            )+            body++    whileLoop cond v = do+      env' <- withLoopParams v+      continue <- asBool <$> eval env' cond+      if continue+        then whileLoop cond =<< eval env' body+        else return v++    forInLoop in_pat v in_v = do+      env' <- withLoopParams v+      env'' <- matchPattern env' in_pat in_v+      eval env'' body+eval env (Project f e _ _) = do+  v <- eval env e+  case v of+    ValueRecord fs | Just v' <- M.lookup f fs -> return v'+    _ -> error "Value does not have expected field."+eval env (Assert what e (Info s) loc) = do+  cond <- asBool <$> eval env what+  unless cond $ bad loc env s+  eval env e+eval env (Constr c es (Info t) _) = do+  vs <- mapM (eval env) es+  shape <- typeValueShape env $ toStruct t+  return $ ValueSum shape c vs+eval env (Match e cs (Info ret, Info retext) _) = do+  v <- eval env e+  returned env ret retext =<< match v (NE.toList cs)+  where+    match _ [] =+      error "Pattern match failure."+    match v (c : cs') = do+      c' <- evalCase v env c+      case c' of+        Just v' -> return v'+        Nothing -> match v cs'+eval env (Attr _ e _) = eval env e++evalCase ::+  Value ->+  Env ->+  CaseBase Info VName ->+  EvalM (Maybe Value)+evalCase v env (CasePat p cExp _) = runMaybeT $ do+  env' <- patternMatch env p v+  lift $ eval env' cExp++substituteInModule :: M.Map VName VName -> Module -> Module+substituteInModule substs = onModule+  where+    rev_substs = reverseSubstitutions substs+    replace v = fromMaybe v $ M.lookup v rev_substs+    replaceQ v = maybe v qualName $ M.lookup (qualLeaf v) rev_substs+    replaceM f m = M.fromList $ do+      (k, v) <- M.toList m+      return (replace k, f v)+    onModule (Module (Env terms types _)) =+      Module $ Env (replaceM onTerm terms) (replaceM onType types) mempty+    onModule (ModuleFun f) =+      ModuleFun $ \m -> onModule <$> f (substituteInModule rev_substs m)+    onTerm (TermValue t v) = TermValue t v+    onTerm (TermPoly t v) = TermPoly t v+    onTerm (TermModule m) = TermModule $ onModule m+    onType (T.TypeAbbr l ps t) = T.TypeAbbr l ps $ first onDim t+    onDim (NamedDim v) = NamedDim $ replaceQ v+    onDim (ConstDim x) = ConstDim x+    onDim AnyDim = AnyDim++reverseSubstitutions :: M.Map VName VName -> M.Map VName VName+reverseSubstitutions = M.fromList . map (uncurry $ flip (,)) . M.toList++evalModuleVar :: Env -> QualName VName -> EvalM Module+evalModuleVar env qv =+  case lookupVar qv env of+    Just (TermModule m) -> return m+    _ -> error $ quote (pretty qv) <> " is not bound to a module."++evalModExp :: Env -> ModExp -> EvalM Module+evalModExp _ (ModImport _ (Info f) _) = do+  f' <- lookupImport f+  case f' of+    Nothing -> error $ "Unknown import " ++ show f+    Just m -> return $ Module m+evalModExp env (ModDecs ds _) = do+  Env terms types _ <- foldM evalDec env ds+  -- Remove everything that was present in the original Env.+  return $+    Module $+      Env+        (terms `M.difference` envTerm env)+        (types `M.difference` envType env)+        mempty+evalModExp env (ModVar qv _) =+  evalModuleVar env qv+evalModExp env (ModAscript me _ (Info substs) _) =+  substituteInModule substs <$> evalModExp env me+evalModExp env (ModParens me _) = evalModExp env me+evalModExp env (ModLambda p ret e loc) =+  return $+    ModuleFun $ \am -> do+      let env' = env {envTerm = M.insert (modParamName p) (TermModule am) $ envTerm env}+      evalModExp env' $ case ret of+        Nothing -> e+        Just (se, rsubsts) -> ModAscript e se rsubsts loc+evalModExp env (ModApply f e (Info psubst) (Info rsubst) _) = do+  f' <- evalModExp env f+  case f' of+    ModuleFun f'' -> do+      e' <- evalModExp env e+      substituteInModule rsubst <$> f'' (substituteInModule psubst e')+    _ -> error "Expected ModuleFun."++evalDec :: Env -> Dec -> EvalM Env+evalDec env (ValDec (ValBind _ v _ (Info (ret, retext)) tparams ps fbody _ _ _)) = do+  binding <- evalFunctionBinding env tparams ps ret retext fbody+  return $ env {envTerm = M.insert v binding $ envTerm env}+evalDec env (OpenDec me _) = do+  me' <- evalModExp env me+  case me' of+    Module me'' -> return $ me'' <> env+    _ -> error "Expected Module"+evalDec env (ImportDec name name' loc) =+  evalDec env $ LocalDec (OpenDec (ModImport name name' loc) loc) loc+evalDec env (LocalDec d _) = evalDec env d+evalDec env SigDec {} = return env+evalDec env (TypeDec (TypeBind v l ps t _ _)) = do+  let abbr =+        T.TypeAbbr l ps $+          evalType env $ unInfo $ expandedType t+  return env {envType = M.insert v abbr $ envType env}+evalDec env (ModDec (ModBind v ps ret body _ loc)) = do+  mod <- evalModExp env $ wrapInLambda ps+  return $ modEnv (M.singleton v mod) <> env+  where+    wrapInLambda [] = case ret of+      Just (se, substs) -> ModAscript body se substs loc+      Nothing -> body+    wrapInLambda [p] = ModLambda p ret body loc+    wrapInLambda (p : ps') = ModLambda p Nothing (wrapInLambda ps') loc++-- | The interpreter context.  All evaluation takes place with respect+-- to a context, and it can be extended with more definitions, which+-- is how the REPL works.+data Ctx = Ctx+  { ctxEnv :: Env,+    ctxImports :: M.Map FilePath Env+  }++nanValue :: PrimValue -> Bool+nanValue (FloatValue v) =+  case v of+    Float32Value x -> isNaN x+    Float64Value x -> isNaN x+nanValue _ = False++breakOnNaN :: [PrimValue] -> PrimValue -> EvalM ()+breakOnNaN inputs result+  | not (any nanValue inputs) && nanValue result = do+    backtrace <- asks fst+    case NE.nonEmpty backtrace of+      Nothing -> return ()+      Just backtrace' -> liftF $ ExtOpBreak BreakNaN backtrace' ()+breakOnNaN _ _ =+  return ()++-- | The initial environment contains definitions of the various intrinsic functions.+initialCtx :: Ctx+initialCtx =+  Ctx+    ( Env+        ( M.insert+            (VName (nameFromString "intrinsics") 0)+            (TermModule (Module $ Env terms types mempty))+            terms+        )+        types+        mempty+    )+    mempty+  where+    terms = M.mapMaybeWithKey (const . def . baseString) intrinsics+    types = M.mapMaybeWithKey (const . tdef . baseString) intrinsics++    sintOp f =+      [ (getS, putS, P.doBinOp (f Int8)),+        (getS, putS, P.doBinOp (f Int16)),+        (getS, putS, P.doBinOp (f Int32)),+        (getS, putS, P.doBinOp (f Int64))+      ]+    uintOp f =+      [ (getU, putU, P.doBinOp (f Int8)),+        (getU, putU, P.doBinOp (f Int16)),+        (getU, putU, P.doBinOp (f Int32)),+        (getU, putU, P.doBinOp (f Int64))+      ]+    intOp f = sintOp f ++ uintOp f+    floatOp f =+      [ (getF, putF, P.doBinOp (f Float32)),+        (getF, putF, P.doBinOp (f Float64))+      ]+    arithOp f g = Just $ bopDef $ intOp f ++ floatOp g++    flipCmps = map (\(f, g, h) -> (f, g, flip h))+    sintCmp f =+      [ (getS, Just . BoolValue, P.doCmpOp (f Int8)),+        (getS, Just . BoolValue, P.doCmpOp (f Int16)),+        (getS, Just . BoolValue, P.doCmpOp (f Int32)),+        (getS, Just . BoolValue, P.doCmpOp (f Int64))+      ]+    uintCmp f =+      [ (getU, Just . BoolValue, P.doCmpOp (f Int8)),+        (getU, Just . BoolValue, P.doCmpOp (f Int16)),+        (getU, Just . BoolValue, P.doCmpOp (f Int32)),+        (getU, Just . BoolValue, P.doCmpOp (f Int64))+      ]+    floatCmp f =+      [ (getF, Just . BoolValue, P.doCmpOp (f Float32)),+        (getF, Just . BoolValue, P.doCmpOp (f Float64))+      ]+    boolCmp f = [(getB, Just . BoolValue, P.doCmpOp f)]++    getV (SignedValue x) = Just $ P.IntValue x+    getV (UnsignedValue x) = Just $ P.IntValue x+    getV (FloatValue x) = Just $ P.FloatValue x+    getV (BoolValue x) = Just $ P.BoolValue x+    putV (P.IntValue x) = SignedValue x+    putV (P.FloatValue x) = FloatValue x+    putV (P.BoolValue x) = BoolValue x+    putV P.Checked = BoolValue True++    getS (SignedValue x) = Just $ P.IntValue x+    getS _ = Nothing+    putS (P.IntValue x) = Just $ SignedValue x+    putS _ = Nothing++    getU (UnsignedValue x) = Just $ P.IntValue x+    getU _ = Nothing+    putU (P.IntValue x) = Just $ UnsignedValue x+    putU _ = Nothing++    getF (FloatValue x) = Just $ P.FloatValue x+    getF _ = Nothing+    putF (P.FloatValue x) = Just $ FloatValue x+    putF _ = Nothing++    getB (BoolValue x) = Just $ P.BoolValue x+    getB _ = Nothing+    putB (P.BoolValue x) = Just $ BoolValue x+    putB _ = Nothing++    fun1 f =+      TermValue Nothing $ ValueFun $ \x -> f x+    fun2 f =+      TermValue Nothing $+        ValueFun $ \x ->+          return $ ValueFun $ \y -> f x y+    fun2t f =+      TermValue Nothing $+        ValueFun $ \v ->+          case fromTuple v of+            Just [x, y] -> f x y+            _ -> error $ "Expected pair; got: " ++ pretty v+    fun3t f =+      TermValue Nothing $+        ValueFun $ \v ->+          case fromTuple v of+            Just [x, y, z] -> f x y z+            _ -> error $ "Expected triple; got: " ++ pretty v++    fun6t f =+      TermValue Nothing $+        ValueFun $ \v ->+          case fromTuple v of+            Just [x, y, z, a, b, c] -> f x y z a b c+            _ -> error $ "Expected sextuple; got: " ++ pretty v++    bopDef fs = fun2 $ \x y ->+      case (x, y) of+        (ValuePrim x', ValuePrim y')+          | Just z <- msum $ map (`bopDef'` (x', y')) fs -> do+            breakOnNaN [x', y'] z+            return $ ValuePrim z+        _ ->+          bad noLoc mempty $+            "Cannot apply operator to arguments "+              <> quote (pretty x)+              <> " and "+              <> quote (pretty y)+              <> "."+      where+        bopDef' (valf, retf, op) (x, y) = do+          x' <- valf x+          y' <- valf y+          retf =<< op x' y'++    unopDef fs = fun1 $ \x ->+      case x of+        (ValuePrim x')+          | Just r <- msum $ map (`unopDef'` x') fs -> do+            breakOnNaN [x'] r+            return $ ValuePrim r+        _ ->+          bad noLoc mempty $+            "Cannot apply function to argument "+              <> quote (pretty x)+              <> "."+      where+        unopDef' (valf, retf, op) x = do+          x' <- valf x+          retf =<< op x'++    tbopDef f = fun1 $ \v ->+      case fromTuple v of+        Just [ValuePrim x, ValuePrim y]+          | Just x' <- getV x,+            Just y' <- getV y,+            Just z <- putV <$> f x' y' -> do+            breakOnNaN [x, y] z+            return $ ValuePrim z+        _ ->+          bad noLoc mempty $+            "Cannot apply operator to argument "+              <> quote (pretty v)+              <> "."++    def "!" =+      Just $+        unopDef+          [ (getS, putS, P.doUnOp $ P.Complement Int8),+            (getS, putS, P.doUnOp $ P.Complement Int16),+            (getS, putS, P.doUnOp $ P.Complement Int32),+            (getS, putS, P.doUnOp $ P.Complement Int64),+            (getU, putU, P.doUnOp $ P.Complement Int8),+            (getU, putU, P.doUnOp $ P.Complement Int16),+            (getU, putU, P.doUnOp $ P.Complement Int32),+            (getU, putU, P.doUnOp $ P.Complement Int64),+            (getB, putB, P.doUnOp P.Not)+          ]+    def "+" = arithOp (`P.Add` P.OverflowWrap) P.FAdd+    def "-" = arithOp (`P.Sub` P.OverflowWrap) P.FSub+    def "*" = arithOp (`P.Mul` P.OverflowWrap) P.FMul+    def "**" = arithOp P.Pow P.FPow+    def "/" =+      Just $+        bopDef $+          sintOp (`P.SDiv` P.Unsafe)+            ++ uintOp (`P.UDiv` P.Unsafe)+            ++ floatOp P.FDiv+    def "%" =+      Just $+        bopDef $+          sintOp (`P.SMod` P.Unsafe)+            ++ uintOp (`P.UMod` P.Unsafe)+            ++ floatOp P.FMod+    def "//" =+      Just $+        bopDef $+          sintOp (`P.SQuot` P.Unsafe)+            ++ uintOp (`P.UDiv` P.Unsafe)+    def "%%" =+      Just $+        bopDef $+          sintOp (`P.SRem` P.Unsafe)+            ++ uintOp (`P.UMod` P.Unsafe)+    def "^" = Just $ bopDef $ intOp P.Xor+    def "&" = Just $ bopDef $ intOp P.And+    def "|" = Just $ bopDef $ intOp P.Or+    def ">>" = Just $ bopDef $ sintOp P.AShr ++ uintOp P.LShr+    def "<<" = Just $ bopDef $ intOp P.Shl+    def ">>>" = Just $ bopDef $ sintOp P.LShr ++ uintOp P.LShr+    def "==" = Just $+      fun2 $+        \xs ys -> return $ ValuePrim $ BoolValue $ xs == ys+    def "!=" = Just $+      fun2 $+        \xs ys -> return $ ValuePrim $ BoolValue $ xs /= ys+    -- The short-circuiting is handled directly in 'eval'; these cases+    -- are only used when partially applying and such.+    def "&&" = Just $+      fun2 $ \x y ->+        return $ ValuePrim $ BoolValue $ asBool x && asBool y+    def "||" = Just $+      fun2 $ \x y ->+        return $ ValuePrim $ BoolValue $ asBool x || asBool y+    def "<" =+      Just $+        bopDef $+          sintCmp P.CmpSlt ++ uintCmp P.CmpUlt+            ++ floatCmp P.FCmpLt+            ++ boolCmp P.CmpLlt+    def ">" =+      Just $+        bopDef $+          flipCmps $+            sintCmp P.CmpSlt ++ uintCmp P.CmpUlt+              ++ floatCmp P.FCmpLt+              ++ boolCmp P.CmpLlt+    def "<=" =+      Just $+        bopDef $+          sintCmp P.CmpSle ++ uintCmp P.CmpUle+            ++ floatCmp P.FCmpLe+            ++ boolCmp P.CmpLle+    def ">=" =+      Just $+        bopDef $+          flipCmps $+            sintCmp P.CmpSle ++ uintCmp P.CmpUle+              ++ floatCmp P.FCmpLe+              ++ boolCmp P.CmpLle+    def s+      | Just bop <- find ((s ==) . pretty) P.allBinOps =+        Just $ tbopDef $ P.doBinOp bop+      | Just unop <- find ((s ==) . pretty) P.allCmpOps =+        Just $ tbopDef $ \x y -> P.BoolValue <$> P.doCmpOp unop x y+      | Just cop <- find ((s ==) . pretty) P.allConvOps =+        Just $ unopDef [(getV, Just . putV, P.doConvOp cop)]+      | Just unop <- find ((s ==) . pretty) P.allUnOps =+        Just $ unopDef [(getV, Just . putV, P.doUnOp unop)]+      | Just (pts, _, f) <- M.lookup s P.primFuns =+        case length pts of+          1 -> Just $ unopDef [(getV, Just . putV, f . pure)]+          _ -> Just $+            fun1 $ \x -> do+              let getV' (ValuePrim v) = Just v+                  getV' _ = Nothing+              case mapM getV' =<< fromTuple x of+                Just vs+                  | Just res <- fmap putV . f =<< mapM getV vs -> do+                    breakOnNaN vs res+                    return $ ValuePrim res+                _ ->+                  error $ "Cannot apply " ++ pretty s ++ " to " ++ pretty x+      | "sign_" `isPrefixOf` s =+        Just $+          fun1 $ \x ->+            case x of+              (ValuePrim (UnsignedValue x')) ->+                return $ ValuePrim $ SignedValue x'+              _ -> error $ "Cannot sign: " ++ pretty x+      | "unsign_" `isPrefixOf` s =+        Just $+          fun1 $ \x ->+            case x of+              (ValuePrim (SignedValue x')) ->+                return $ ValuePrim $ UnsignedValue x'+              _ -> error $ "Cannot unsign: " ++ pretty x+    def s+      | "map_stream" `isPrefixOf` s =+        Just $ fun2t stream+    def s | "reduce_stream" `isPrefixOf` s =+      Just $ fun3t $ \_ f arg -> stream f arg+    def "map" = Just $+      TermPoly Nothing $ \t -> return $+        ValueFun $ \v ->+          case (fromTuple v, unfoldFunType t) of+            (Just [f, xs], ([_], ret_t))+              | Just rowshape <- typeRowShape ret_t ->+                toArray' rowshape <$> mapM (apply noLoc mempty f) (snd $ fromArray xs)+              | otherwise ->+                error $ "Bad return type: " ++ pretty ret_t+            _ ->+              error $+                "Invalid arguments to map intrinsic:\n"+                  ++ unlines [pretty t, pretty v]+      where+        typeRowShape = sequenceA . structTypeShape mempty . stripArray 1+    def s | "reduce" `isPrefixOf` s = Just $+      fun3t $ \f ne xs ->+        foldM (apply2 noLoc mempty f) ne $ snd $ fromArray xs+    def "scan" = Just $+      fun3t $ \f ne xs -> do+        let next (out, acc) x = do+              x' <- apply2 noLoc mempty f acc x+              return (x' : out, x')+        toArray' (valueShape ne) . reverse . fst+          <$> foldM next ([], ne) (snd $ fromArray xs)+    def "scatter" = Just $+      fun3t $ \arr is vs ->+        case arr of+          ValueArray shape arr' ->+            return $+              ValueArray shape $+                foldl' update arr' $+                  zip (map asInt $ snd $ fromArray is) (snd $ fromArray vs)+          _ ->+            error $ "scatter expects array, but got: " ++ pretty arr+      where+        update arr' (i, v) =+          if i >= 0 && i < arrayLength arr'+            then arr' // [(i, v)]+            else arr'+    def "hist" = Just $+      fun6t $ \_ arr fun _ is vs ->+        case arr of+          ValueArray shape arr' ->+            ValueArray shape+              <$> foldM+                (update fun)+                arr'+                (zip (map asInt $ snd $ fromArray is) (snd $ fromArray vs))+          _ ->+            error $ "hist expects array, but got: " ++ pretty arr+      where+        update fun arr' (i, v) =+          if i >= 0 && i < arrayLength arr'+            then do+              v' <- apply2 noLoc mempty fun (arr' ! i) v+              return $ arr' // [(i, v')]+            else return arr'+    def "partition" = Just $+      fun3t $ \k f xs -> do+        let (ShapeDim _ rowshape, xs') = fromArray xs++            next outs x = do+              i <- asInt <$> apply noLoc mempty f x+              return $ insertAt i x outs+            pack parts =+              toTuple+                [ toArray' rowshape $ concat parts,+                  toArray' rowshape $+                    map (ValuePrim . SignedValue . Int64Value . genericLength) parts+                ]++        pack . map reverse+          <$> foldM next (replicate (asInt k) []) xs'+      where+        insertAt 0 x (l : ls) = (x : l) : ls+        insertAt i x (l : ls) = l : insertAt (i -1) x ls+        insertAt _ _ ls = ls+    def "unzip" = Just $+      fun1 $ \x -> do+        let ShapeDim _ (ShapeRecord fs) = valueShape x+            Just [xs_shape, ys_shape] = areTupleFields fs+            listPair (xs, ys) =+              [toArray' xs_shape xs, toArray' ys_shape ys]++        return $ toTuple $ listPair $ unzip $ map (fromPair . fromTuple) $ snd $ fromArray x+      where+        fromPair (Just [x, y]) = (x, y)+        fromPair l = error $ "Not a pair: " ++ pretty l+    def "zip" = Just $+      fun2t $ \xs ys -> do+        let ShapeDim _ xs_rowshape = valueShape xs+            ShapeDim _ ys_rowshape = valueShape ys+        return $+          toArray' (ShapeRecord (tupleFields [xs_rowshape, ys_rowshape])) $+            map toTuple $ transpose [snd $ fromArray xs, snd $ fromArray ys]+    def "concat" = Just $+      fun2t $ \xs ys -> do+        let (ShapeDim _ rowshape, xs') = fromArray xs+            (_, ys') = fromArray ys+        return $ toArray' rowshape $ xs' ++ ys'+    def "transpose" = Just $+      fun1 $ \xs -> do+        let (ShapeDim n (ShapeDim m shape), xs') = fromArray xs+        return $+          toArray (ShapeDim m (ShapeDim n shape)) $+            map (toArray (ShapeDim n shape)) $ transpose $ map (snd . fromArray) xs'+    def "rotate" = Just $+      fun2t $ \i xs -> do+        let (shape, xs') = fromArray xs+        return $+          if asInt i > 0+            then+              let (bef, aft) = splitAt (asInt i) xs'+               in toArray shape $ aft ++ bef+            else+              let (bef, aft) = splitFromEnd (- asInt i) xs'+               in toArray shape $ aft ++ bef+    def "flatten" = Just $+      fun1 $ \xs -> do+        let (ShapeDim n (ShapeDim m shape), xs') = fromArray xs+        return $ toArray (ShapeDim (n * m) shape) $ concatMap (snd . fromArray) xs'+    def "unflatten" = Just $+      fun3t $ \n m xs -> do+        let (ShapeDim _ innershape, xs') = fromArray xs+            rowshape = ShapeDim (asInt64 m) innershape+            shape = ShapeDim (asInt64 n) rowshape+        return $ toArray shape $ map (toArray rowshape) $ chunk (asInt m) xs'+    def "opaque" = Just $ fun1 return+    def "trace" = Just $ fun1 $ \v -> trace v >> return v+    def "break" = Just $+      fun1 $ \v -> do+        break+        return v+    def s | nameFromString s `M.member` namesToPrimTypes = Nothing+    def s = error $ "Missing intrinsic: " ++ s++    tdef s = do+      t <- nameFromString s `M.lookup` namesToPrimTypes+      return $ T.TypeAbbr Unlifted [] $ Scalar $ Prim t++    stream f arg@(ValueArray _ xs) =+      let n = ValuePrim $ SignedValue $ Int64Value $ arrayLength xs+       in apply2 noLoc mempty f n arg+    stream _ arg = error $ "Cannot stream: " ++ pretty arg++interpretExp :: Ctx -> Exp -> F ExtOp Value+interpretExp ctx e = runEvalM (ctxImports ctx) $ eval (ctxEnv ctx) e++interpretDec :: Ctx -> Dec -> F ExtOp Ctx+interpretDec ctx d = do+  env <- runEvalM (ctxImports ctx) $ evalDec (ctxEnv ctx) d+  return ctx {ctxEnv = env}++interpretImport :: Ctx -> (FilePath, Prog) -> F ExtOp Ctx+interpretImport ctx (fp, prog) = do+  env <- runEvalM (ctxImports ctx) $ foldM evalDec (ctxEnv ctx) $ progDecs prog+  return ctx {ctxImports = M.insert fp env $ ctxImports ctx}++checkEntryArgs :: VName -> [F.Value] -> StructType -> Either String ()+checkEntryArgs entry args entry_t+  | args_ts == param_ts =+    return ()+  | otherwise =+    Left $+      pretty $+        expected+          </> "Got input of types"+          </> indent 2 (stack (map ppr args_ts))+  where+    (param_ts, _) = unfoldFunType entry_t+    args_ts = map (valueStructType . valueType) args+    expected+      | null param_ts =+        "Entry point " <> pquote (pprName entry) <> " is not a function."+      | otherwise =+        "Entry point " <> pquote (pprName entry) <> " expects input of type(s)"+          </> indent 2 (stack (map ppr param_ts))++-- | Execute the named function on the given arguments; may fail+-- horribly if these are ill-typed.+interpretFunction :: Ctx -> VName -> [F.Value] -> Either String (F ExtOp Value)+interpretFunction ctx fname vs = do+  ft <- case lookupVar (qualName fname) $ ctxEnv ctx of+    Just (TermValue (Just (T.BoundV _ t)) _) ->+      Right $ updateType (map valueType vs) t+    Just (TermPoly (Just (T.BoundV _ t)) _) ->+      Right $ updateType (map valueType vs) t+    _ ->+      Left $ "Unknown function `" <> prettyName fname <> "`."++  vs' <- case mapM convertValue vs of+    Just vs' -> Right vs'+    Nothing -> Left "Invalid input: irregular array."++  checkEntryArgs fname vs ft++  Right $+    runEvalM (ctxImports ctx) $ do+      f <- evalTermVar (ctxEnv ctx) (qualName fname) ft+      foldM (apply noLoc mempty) f vs'+  where+    updateType (vt : vts) (Scalar (Arrow als u _ rt)) =+      Scalar $ Arrow als u (valueStructType vt) $ updateType vts rt+    updateType _ t = t++    convertValue (F.PrimValue p) = Just $ ValuePrim p+    convertValue (F.ArrayValue arr t) = mkArray t =<< mapM convertValue (elems arr)
src/Language/Futhark/Parser.hs view
@@ -1,64 +1,72 @@ {-# LANGUAGE Safe #-}+ -- | Interface to the Futhark parser. module Language.Futhark.Parser-  ( parseFuthark-  , parseExp-  , parseModExp-  , parseType--  , parseValue-  , parseValues--  , parseDecOrExpIncrM--  , ParseError (..)--  , scanTokensText-  , L(..)-  , Token(..)+  ( parseFuthark,+    parseExp,+    parseModExp,+    parseType,+    parseValue,+    parseValues,+    parseDecOrExpIncrM,+    ParseError (..),+    scanTokensText,+    L (..),+    Token (..),   )-  where+where  import qualified Data.Text as T--import Language.Futhark.Syntax-import Language.Futhark.Prop-import Language.Futhark.Parser.Parser import Language.Futhark.Parser.Lexer+import Language.Futhark.Parser.Parser+import Language.Futhark.Prop+import Language.Futhark.Syntax  -- | Parse an entire Futhark program from the given 'T.Text', using -- the 'FilePath' as the source name for error messages.-parseFuthark :: FilePath -> T.Text-             -> Either ParseError UncheckedProg+parseFuthark ::+  FilePath ->+  T.Text ->+  Either ParseError UncheckedProg parseFuthark = parse prog  -- | Parse an Futhark expression from the given 'String', using the -- 'FilePath' as the source name for error messages.-parseExp :: FilePath -> T.Text-         -> Either ParseError UncheckedExp+parseExp ::+  FilePath ->+  T.Text ->+  Either ParseError UncheckedExp parseExp = parse expression  -- | Parse a Futhark module expression from the given 'String', using the -- 'FilePath' as the source name for error messages.-parseModExp :: FilePath -> T.Text-            -> Either ParseError (ModExpBase NoInfo Name)+parseModExp ::+  FilePath ->+  T.Text ->+  Either ParseError (ModExpBase NoInfo Name) parseModExp = parse modExpression  -- | Parse an Futhark type from the given 'String', using the -- 'FilePath' as the source name for error messages.-parseType :: FilePath -> T.Text-          -> Either ParseError UncheckedTypeExp+parseType ::+  FilePath ->+  T.Text ->+  Either ParseError UncheckedTypeExp parseType = parse futharkType  -- | Parse any Futhark value from the given 'String', using the 'FilePath' -- as the source name for error messages.-parseValue :: FilePath -> T.Text-           -> Either ParseError Value+parseValue ::+  FilePath ->+  T.Text ->+  Either ParseError Value parseValue = parse anyValue  -- | Parse several Futhark values (separated by anything) from the given -- 'String', using the 'FilePath' as the source name for error -- messages.-parseValues :: FilePath -> T.Text-            -> Either ParseError [Value]+parseValues ::+  FilePath ->+  T.Text ->+  Either ParseError [Value] parseValues = parse anyValues
src/Language/Futhark/Parser/Parser.y view
@@ -974,7 +974,7 @@            | '[' ']'              {% emptyArrayError $1 } -Dim :: { Int32 }+Dim :: { Int64 } Dim : intlit { let L _ (INTLIT num) = $1 in fromInteger num }  ValueType :: { ValueType }
src/Language/Futhark/Prelude.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE Trustworthy #-} {-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE Trustworthy #-}+ -- | The Futhark Prelude Library embedded embedded as strings read -- during compilation of the Futhark compiler.  The advantage is that -- the prelude can be accessed without reading it from disk, thus@@ -11,12 +12,12 @@ import Data.FileEmbed import qualified Data.Text as T import qualified Data.Text.Encoding as T-import qualified System.FilePath.Posix as Posix- import Futhark.Util (toPOSIX)+import qualified System.FilePath.Posix as Posix  -- | Prelude embedded as 'T.Text' values, one for every file. prelude :: [(Posix.FilePath, T.Text)] prelude = map fixup prelude_bs-  where prelude_bs = $(embedDir "prelude")-        fixup (path, s) = ("/prelude" Posix.</> toPOSIX path, T.decodeUtf8 s)+  where+    prelude_bs = $(embedDir "prelude")+    fixup (path, s) = ("/prelude" Posix.</> toPOSIX path, T.decodeUtf8 s)
src/Language/Futhark/Pretty.hs view
@@ -1,38 +1,36 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+ -- | Futhark prettyprinter.  This module defines 'Pretty' instances -- for the AST defined in "Language.Futhark.Syntax". module Language.Futhark.Pretty-  ( pretty-  , prettyTuple-  , leadingOperator-  , IsName(..)-  , prettyName-  , Annot(..)+  ( pretty,+    prettyTuple,+    leadingOperator,+    IsName (..),+    prettyName,+    Annot (..),   ) where -import           Control.Monad-import           Codec.Binary.UTF8.String (decode)-import           Data.Array-import           Data.Functor-import qualified Data.Map.Strict       as M-import           Data.List (intersperse)-import qualified Data.List.NonEmpty    as NE-import           Data.Maybe-import           Data.Monoid           hiding (Sum)-import           Data.Ord-import           Data.Word--import           Prelude--import           Futhark.Util.Pretty-import           Futhark.Util--import           Language.Futhark.Syntax-import           Language.Futhark.Prop+import Codec.Binary.UTF8.String (decode)+import Control.Monad+import Data.Array+import Data.Functor+import Data.List (intersperse)+import qualified Data.List.NonEmpty as NE+import qualified Data.Map.Strict as M+import Data.Maybe+import Data.Monoid hiding (Sum)+import Data.Ord+import Data.Word+import Futhark.Util+import Futhark.Util.Pretty+import Language.Futhark.Prop+import Language.Futhark.Syntax+import Prelude  commastack :: [Doc] -> Doc commastack = align . stack . punctuate comma@@ -52,9 +50,10 @@ -- VNames are printed as either the name with an internal tag, or just -- the base name. instance IsName VName where-  pprName | isEnvVarSet "FUTHARK_COMPILER_DEBUGGING" False =-            \(VName vn i) -> ppr vn <> text "_" <> text (show i)-          | otherwise = ppr . baseName+  pprName+    | isEnvVarSet "FUTHARK_COMPILER_DEBUGGING" False =+      \(VName vn i) -> ppr vn <> text "_" <> text (show i)+    | otherwise = ppr . baseName  instance IsName Name where   pprName = ppr@@ -83,30 +82,30 @@   ppr (PrimValue bv) = ppr bv   ppr (ArrayValue a t)     | [] <- elems a = text "empty" <> parens (ppr t)-    | Array{} <- t  = brackets $ commastack $ map ppr $ elems a-    | otherwise     = brackets $ commasep $ map ppr $ elems a+    | Array {} <- t = brackets $ commastack $ map ppr $ elems a+    | otherwise = brackets $ commasep $ map ppr $ elems a  instance Pretty PrimValue where   ppr (UnsignedValue (Int8Value v)) =-    text (show (fromIntegral v::Word8)) <> text "u8"+    text (show (fromIntegral v :: Word8)) <> text "u8"   ppr (UnsignedValue (Int16Value v)) =-    text (show (fromIntegral v::Word16)) <> text "u16"+    text (show (fromIntegral v :: Word16)) <> text "u16"   ppr (UnsignedValue (Int32Value v)) =-    text (show (fromIntegral v::Word32)) <> text "u32"+    text (show (fromIntegral v :: Word32)) <> text "u32"   ppr (UnsignedValue (Int64Value v)) =-    text (show (fromIntegral v::Word64)) <> text "u64"+    text (show (fromIntegral v :: Word64)) <> text "u64"   ppr (SignedValue v) = ppr v   ppr (BoolValue True) = text "true"   ppr (BoolValue False) = text "false"   ppr (FloatValue v) = ppr v  instance IsName vn => Pretty (DimDecl vn) where-  ppr AnyDim       = mempty+  ppr AnyDim = mempty   ppr (NamedDim v) = ppr v   ppr (ConstDim n) = ppr n  instance IsName vn => Pretty (DimExp vn) where-  ppr DimExpAny         = mempty+  ppr DimExpAny = mempty   ppr (DimExpNamed v _) = ppr v   ppr (DimExpConst n _) = ppr n @@ -116,7 +115,7 @@ instance Pretty (ShapeDecl ()) where   ppr (ShapeDecl ds) = mconcat $ replicate (length ds) $ text "[]" -instance Pretty (ShapeDecl Int32) where+instance Pretty (ShapeDecl Int64) where   ppr (ShapeDecl ds) = mconcat (map (brackets . ppr) ds)  instance Pretty (ShapeDecl Bool) where@@ -127,27 +126,29 @@   pprPrec _ (Prim et) = ppr et   pprPrec p (TypeVar _ u et targs) =     parensIf (not (null targs) && p > 3) $-    ppr u <> ppr (qualNameFromTypeName et) <+> spread (map (pprPrec 3) targs)+      ppr u <> ppr (qualNameFromTypeName et) <+> spread (map (pprPrec 3) targs)   pprPrec _ (Record fs)     | Just ts <- areTupleFields fs =-        oneLine (parens $ commasep $ map ppr ts)+      oneLine (parens $ commasep $ map ppr ts)         <|> parens (align $ mconcat $ punctuate (text "," <> line) $ map ppr ts)     | otherwise =-        oneLine (braces $ commasep fs')+      oneLine (braces $ commasep fs')         <|> braces (align $ mconcat $ punctuate (text "," <> line) fs')-    where ppField (name, t) = text (nameToString name) <> colon <+> align (ppr t)-          fs' = map ppField $ M.toList fs+    where+      ppField (name, t) = text (nameToString name) <> colon <+> align (ppr t)+      fs' = map ppField $ M.toList fs   pprPrec p (Arrow _ (Named v) t1 t2) =     parensIf (p > 1) $-    parens (pprName v <> colon <+> align (ppr t1)) <+/> text "->" <+> pprPrec 1 t2+      parens (pprName v <> colon <+> align (ppr t1)) <+/> text "->" <+> pprPrec 1 t2   pprPrec p (Arrow _ Unnamed t1 t2) =     parensIf (p > 1) $ pprPrec 2 t1 <+/> text "->" <+> pprPrec 1 t2   pprPrec p (Sum cs) =     parensIf (p > 0) $-    oneLine (mconcat $ punctuate (text " | ") cs')-    <|> align (mconcat $ punctuate (text " |" <> line) cs')-    where ppConstr (name, fs) = sep $ (text "#" <> ppr name) : map (pprPrec 1) fs-          cs' = map ppConstr $ M.toList cs+      oneLine (mconcat $ punctuate (text " | ") cs')+        <|> align (mconcat $ punctuate (text " |" <> line) cs')+    where+      ppConstr (name, fs) = sep $ (text "#" <> ppr name) : map (pprPrec 1) fs+      cs' = map ppConstr $ M.toList cs  instance Pretty (ShapeDecl dim) => Pretty (TypeBase dim as) where   ppr = pprPrec 0@@ -164,15 +165,18 @@   ppr (TEArray at d _) = brackets (ppr d) <> ppr at   ppr (TETuple ts _) = parens $ commasep $ map ppr ts   ppr (TERecord fs _) = braces $ commasep $ map ppField fs-    where ppField (name, t) = text (nameToString name) <> colon <+> ppr t+    where+      ppField (name, t) = text (nameToString name) <> colon <+> ppr t   ppr (TEVar name _) = ppr name   ppr (TEApply t arg _) = ppr t <+> ppr arg   ppr (TEArrow (Just v) t1 t2 _) = parens v' <+> text "->" <+> ppr t2-    where v' = pprName v <> colon <+> ppr t1+    where+      v' = pprName v <> colon <+> ppr t1   ppr (TEArrow Nothing t1 t2 _) = ppr t1 <+> text "->" <+> ppr t2   ppr (TESum cs _) =     align $ cat $ punctuate (text " |" <> softline) $ map ppConstr cs-    where ppConstr (name, fs) = text "#" <> ppr name <+> sep (map ppr fs)+    where+      ppConstr (name, fs) = text "#" <> ppr name <+> sep (map ppr fs)  instance (Eq vn, IsName vn) => Pretty (TypeArgExp vn) where   ppr (TypeArgExpDim d _) = brackets $ ppr d@@ -189,36 +193,38 @@   ppr = pprName . identName  hasArrayLit :: ExpBase ty vn -> Bool-hasArrayLit ArrayLit{}     = True+hasArrayLit ArrayLit {} = True hasArrayLit (TupLit es2 _) = any hasArrayLit es2-hasArrayLit _              = False+hasArrayLit _ = False  instance (Eq vn, IsName vn, Annot f) => Pretty (DimIndexBase f vn) where-  ppr (DimFix e)       = ppr e+  ppr (DimFix e) = ppr e   ppr (DimSlice i j (Just s)) =-    maybe mempty ppr i <> text ":" <>-    maybe mempty ppr j <> text ":" <>-    ppr s+    maybe mempty ppr i <> text ":"+      <> maybe mempty ppr j+      <> text ":"+      <> ppr s   ppr (DimSlice i (Just j) s) =-    maybe mempty ppr i <> text ":" <>-    ppr j <>-    maybe mempty ((text ":" <>) . ppr) s+    maybe mempty ppr i <> text ":"+      <> ppr j+      <> maybe mempty ((text ":" <>) . ppr) s   ppr (DimSlice i Nothing Nothing) =     maybe mempty ppr i <> text ":"  letBody :: (Eq vn, IsName vn, Annot f) => ExpBase f vn -> Doc-letBody body@LetPat{} = ppr body-letBody body@LetFun{} = ppr body-letBody body          = text "in" <+> align (ppr body)+letBody body@LetPat {} = ppr body+letBody body@LetFun {} = ppr body+letBody body = text "in" <+> align (ppr body)  instance (Eq vn, IsName vn, Annot f) => Pretty (ExpBase f vn) where   ppr = pprPrec (-1)   pprPrec _ (Var name t _) = ppr name <> inst-    where inst = case unAnnot t of-                   Just t'-                     | isEnvVarSet "FUTHARK_COMPILER_DEBUGGING" False ->-                         text "@" <> parens (align $ ppr t')-                   _ -> mempty+    where+      inst = case unAnnot t of+        Just t'+          | isEnvVarSet "FUTHARK_COMPILER_DEBUGGING" False ->+            text "@" <> parens (align $ ppr t')+        _ -> mempty   pprPrec _ (Parens e _) = align $ parens $ ppr e   pprPrec _ (QualParens (v, _) e _) = ppr v <> text "." <> align (parens $ ppr e)   pprPrec p (Ascript e t _) =@@ -230,84 +236,98 @@   pprPrec _ (FloatLit v _ _) = ppr v   pprPrec _ (TupLit es _)     | any hasArrayLit es = parens $ commastack $ map ppr es-    | otherwise          = parens $ commasep $ map ppr es+    | otherwise = parens $ commasep $ map ppr es   pprPrec _ (RecordLit fs _)     | any fieldArray fs = braces $ commastack $ map ppr fs-    | otherwise                     = braces $ commasep $ map ppr fs-    where fieldArray (RecordFieldExplicit _ e _) = hasArrayLit e-          fieldArray RecordFieldImplicit{} = False+    | otherwise = braces $ commasep $ map ppr fs+    where+      fieldArray (RecordFieldExplicit _ e _) = hasArrayLit e+      fieldArray RecordFieldImplicit {} = False   pprPrec _ (ArrayLit es info _) =     brackets (commasep $ map ppr es) <> info'-    where info' = case unAnnot info of-                    Just t-                      | isEnvVarSet "FUTHARK_COMPILER_DEBUGGING" False ->-                          text "@" <> parens (align $ ppr t)-                    _ -> mempty+    where+      info' = case unAnnot info of+        Just t+          | isEnvVarSet "FUTHARK_COMPILER_DEBUGGING" False ->+            text "@" <> parens (align $ ppr t)+        _ -> mempty   pprPrec _ (StringLit s _) =     text $ show $ decode s   pprPrec p (Range start maybe_step end _ _) =-    parensIf (p /= -1) $ ppr start <>-    maybe mempty ((text ".." <>) . ppr) maybe_step <>-    case end of-      DownToExclusive end' -> text "..>" <> ppr end'-      ToInclusive     end' -> text "..." <> ppr end'-      UpToExclusive   end' -> text "..<" <> ppr end'-  pprPrec p (BinOp (bop,_) _ (x,_) (y,_) _ _ _) = prettyBinOp p bop x y+    parensIf (p /= -1) $+      ppr start+        <> maybe mempty ((text ".." <>) . ppr) maybe_step+        <> case end of+          DownToExclusive end' -> text "..>" <> ppr end'+          ToInclusive end' -> text "..." <> ppr end'+          UpToExclusive end' -> text "..<" <> ppr end'+  pprPrec p (BinOp (bop, _) _ (x, _) (y, _) _ _ _) = prettyBinOp p bop x y   pprPrec _ (Project k e _ _) = ppr e <> text "." <> ppr k-  pprPrec _ (If c t f _ _) = text "if" <+> ppr c </>-                             text "then" <+> align (ppr t) </>-                             text "else" <+> align (ppr f)+  pprPrec _ (If c t f _ _) =+    text "if" <+> ppr c+      </> text "then" <+> align (ppr t)+      </> text "else" <+> align (ppr f)   pprPrec p (Apply f arg _ _ _) =     parensIf (p >= 10) $ pprPrec 0 f <+/> pprPrec 10 arg   pprPrec _ (Negate e _) = text "-" <> ppr e   pprPrec p (LetPat pat e body _ _) =-    parensIf (p /= -1) $ align $-    text "let" <+> align (ppr pat) <+>-    (if linebreak-     then equals </> indent 2 (ppr e)-     else equals <+> align (ppr e)) </>-    letBody body-    where linebreak = case e of-                        DoLoop{}    -> True-                        LetPat{}    -> True-                        LetWith{}   -> True-                        If{}        -> True-                        Match{}     -> True-                        Attr{}      -> True-                        ArrayLit{}  -> False-                        _           -> hasArrayLit e+    parensIf (p /= -1) $+      align $+        text "let" <+> align (ppr pat)+          <+> ( if linebreak+                  then equals </> indent 2 (ppr e)+                  else equals <+> align (ppr e)+              )+          </> letBody body+    where+      linebreak = case e of+        DoLoop {} -> True+        LetPat {} -> True+        LetWith {} -> True+        If {} -> True+        Match {} -> True+        Attr {} -> True+        ArrayLit {} -> False+        _ -> hasArrayLit e   pprPrec _ (LetFun fname (tparams, params, retdecl, rettype, e) body _ _) =-    text "let" <+> pprName fname <+> spread (map ppr tparams ++ map ppr params) <>-    retdecl' <+> equals </> indent 2 (ppr e) </>-    letBody body-    where retdecl' = case (ppr <$> unAnnot rettype) `mplus` (ppr <$> retdecl) of-                       Just rettype' -> colon <+> align rettype'-                       Nothing       -> mempty+    text "let" <+> pprName fname <+> spread (map ppr tparams ++ map ppr params)+      <> retdecl' <+> equals+      </> indent 2 (ppr e)+      </> letBody body+    where+      retdecl' = case (ppr <$> unAnnot rettype) `mplus` (ppr <$> retdecl) of+        Just rettype' -> colon <+> align rettype'+        Nothing -> mempty   pprPrec _ (LetWith dest src idxs ve body _ _)     | dest == src =-      text "let" <+> ppr dest <> list (map ppr idxs) <+>-      equals <+> align (ppr ve) </>-      letBody body+      text "let" <+> ppr dest <> list (map ppr idxs)+        <+> equals+        <+> align (ppr ve)+        </> letBody body     | otherwise =-      text "let" <+> ppr dest <+> equals <+> ppr src <+>-      text "with" <+> brackets (commasep (map ppr idxs)) <+>-      text "=" <+> align (ppr ve) </>-      letBody body+      text "let" <+> ppr dest <+> equals <+> ppr src+        <+> text "with"+        <+> brackets (commasep (map ppr idxs))+        <+> text "="+        <+> align (ppr ve)+        </> letBody body   pprPrec _ (Update src idxs ve _) =-    ppr src <+> text "with" <+>-    brackets (commasep (map ppr idxs)) <+>-    text "=" <+> align (ppr ve)+    ppr src <+> text "with"+      <+> brackets (commasep (map ppr idxs))+      <+> text "="+      <+> align (ppr ve)   pprPrec _ (RecordUpdate src fs ve _ _) =-    ppr src <+> text "with" <+>-    mconcat (intersperse (text ".") (map ppr fs)) <+>-    text "=" <+> align (ppr ve)+    ppr src <+> text "with"+      <+> mconcat (intersperse (text ".") (map ppr fs))+      <+> text "="+      <+> align (ppr ve)   pprPrec _ (Index e idxs _ _) =     pprPrec 9 e <> brackets (commasep (map ppr idxs))   pprPrec _ (Assert e1 e2 _ _) = text "assert" <+> pprPrec 10 e1 <+> pprPrec 10 e2   pprPrec p (Lambda params body rettype _ _) =     parensIf (p /= -1) $-    text "\\" <> spread (map ppr params) <> ppAscription rettype <+>-    text "->" </> indent 2 (ppr body)+      text "\\" <> spread (map ppr params) <> ppAscription rettype+        <+> text "->" </> indent 2 (ppr body)   pprPrec _ (OpSection binop _ _) =     parens $ ppr binop   pprPrec _ (OpSectionLeft binop _ x _ _ _) =@@ -316,14 +336,19 @@     parens $ ppr binop <+> ppr x   pprPrec _ (ProjectSection fields _ _) =     parens $ mconcat $ map p fields-    where p name = text "." <> ppr name+    where+      p name = text "." <> ppr name   pprPrec _ (IndexSection idxs _ _) =     parens $ text "." <> brackets (commasep (map ppr idxs))   pprPrec _ (DoLoop sizeparams pat initexp form loopbody _ _) =-    text "loop" <+>-    align (spread (map (brackets . pprName) sizeparams) <+/>-           ppr pat <+> equals <+/> ppr initexp <+/> ppr form <+> text "do") </>-    indent 2 (ppr loopbody)+    text "loop"+      <+> align+        ( spread (map (brackets . pprName) sizeparams)+            <+/> ppr pat <+> equals+            <+/> ppr initexp+            <+/> ppr form <+> text "do"+        )+      </> indent 2 (ppr loopbody)   pprPrec _ (Constr n cs _ _) = text "#" <> ppr n <+> sep (map ppr cs)   pprPrec _ (Match e cs _ _) = text "match" <+> ppr e </> (stack . map ppr) (NE.toList cs)   pprPrec _ (Attr attr e _) =@@ -350,33 +375,34 @@  instance (Eq vn, IsName vn, Annot f) => Pretty (PatternBase f vn) where   ppr (PatternAscription p t _) = ppr p <> colon <+> align (ppr t)-  ppr (PatternParens p _)       = parens $ ppr p-  ppr (Id v t _)                = case unAnnot t of-                                    Just t' -> parens $ pprName v <> colon <+> align (ppr t')-                                    Nothing -> pprName v-  ppr (TuplePattern pats _)     = parens $ commasep $ map ppr pats-  ppr (RecordPattern fs _)      = braces $ commasep $ map ppField fs-    where ppField (name, t) = text (nameToString name) <> equals <> ppr t-  ppr (Wildcard t _)            = case unAnnot t of-                                    Just t' -> parens $ text "_" <> colon <+> ppr t'-                                    Nothing -> text "_"-  ppr (PatternLit e _ _)        = ppr e-  ppr (PatternConstr n _ ps _)  = text "#" <> ppr n <+> sep (map ppr ps)+  ppr (PatternParens p _) = parens $ ppr p+  ppr (Id v t _) = case unAnnot t of+    Just t' -> parens $ pprName v <> colon <+> align (ppr t')+    Nothing -> pprName v+  ppr (TuplePattern pats _) = parens $ commasep $ map ppr pats+  ppr (RecordPattern fs _) = braces $ commasep $ map ppField fs+    where+      ppField (name, t) = text (nameToString name) <> equals <> ppr t+  ppr (Wildcard t _) = case unAnnot t of+    Just t' -> parens $ text "_" <> colon <+> ppr t'+    Nothing -> text "_"+  ppr (PatternLit e _ _) = ppr e+  ppr (PatternConstr n _ ps _) = text "#" <> ppr n <+> sep (map ppr ps)  ppAscription :: Pretty t => Maybe t -> Doc-ppAscription Nothing  = mempty+ppAscription Nothing = mempty ppAscription (Just t) = colon <> align (ppr t)  instance (Eq vn, IsName vn, Annot f) => Pretty (ProgBase f vn) where   ppr = stack . punctuate line . map ppr . progDecs  instance (Eq vn, IsName vn, Annot f) => Pretty (DecBase f vn) where-  ppr (ValDec dec)      = ppr dec-  ppr (TypeDec dec)     = ppr dec-  ppr (SigDec sig)      = ppr sig-  ppr (ModDec sd)       = ppr sd-  ppr (OpenDec x _)     = text "open" <+> ppr x-  ppr (LocalDec dec _)  = text "local" <+> ppr dec+  ppr (ValDec dec) = ppr dec+  ppr (TypeDec dec) = ppr dec+  ppr (SigDec sig) = ppr sig+  ppr (ModDec sd) = ppr sd+  ppr (OpenDec x _) = text "open" <+> ppr x+  ppr (LocalDec dec _) = text "local" <+> ppr dec   ppr (ImportDec x _ _) = text "import" <+> ppr x  instance (Eq vn, IsName vn, Annot f) => Pretty (ModExpBase f vn) where@@ -387,10 +413,12 @@   ppr (ModApply f a _ _ _) = parens $ ppr f <+> parens (ppr a)   ppr (ModAscript me se _ _) = ppr me <> colon <+> ppr se   ppr (ModLambda param maybe_sig body _) =-    text "\\" <> ppr param <> maybe_sig' <+>-    text "->" </> indent 2 (ppr body)-    where maybe_sig' = case maybe_sig of Nothing       -> mempty-                                         Just (sig, _) -> colon <+> ppr sig+    text "\\" <> ppr param <> maybe_sig'+      <+> text "->" </> indent 2 (ppr body)+    where+      maybe_sig' = case maybe_sig of+        Nothing -> mempty+        Just (sig, _) -> colon <+> ppr sig  instance Pretty Liftedness where   ppr Unlifted = text ""@@ -399,8 +427,10 @@  instance (Eq vn, IsName vn, Annot f) => Pretty (TypeBindBase f vn) where   ppr (TypeBind name l params usertype _ _) =-    text "type" <> ppr l <+> pprName name <+>-    spread (map ppr params) <+> equals <+> ppr usertype+    text "type" <> ppr l <+> pprName name+      <+> spread (map ppr params)+      <+> equals+      <+> ppr usertype  instance (Eq vn, IsName vn) => Pretty (TypeParamBase vn) where   ppr (TypeParamDim name _) = brackets $ pprName name@@ -408,15 +438,20 @@  instance (Eq vn, IsName vn, Annot f) => Pretty (ValBindBase f vn) where   ppr (ValBind entry name retdecl rettype tparams args body _ attrs _) =-    mconcat (map ((<> line) . ppr) attrs) <>-    text fun <+> pprName name <+>-    align (sep (map ppr tparams ++ map ppr args)) <> retdecl' <> text " =" </>-    indent 2 (ppr body)-    where fun | isJust entry = "entry"-              | otherwise    = "let"-          retdecl' = case (ppr . fst <$> unAnnot rettype) `mplus` (ppr <$> retdecl) of-                       Just rettype' -> colon <+> align rettype'-                       Nothing       -> mempty+    mconcat (map ((<> line) . ppr) attrs)+      <> text fun+      <+> pprName name+      <+> align (sep (map ppr tparams ++ map ppr args))+      <> retdecl'+      <> text " ="+      </> indent 2 (ppr body)+    where+      fun+        | isJust entry = "entry"+        | otherwise = "let"+      retdecl' = case (ppr . fst <$> unAnnot rettype) `mplus` (ppr <$> retdecl) of+        Just rettype' -> colon <+> align rettype'+        Nothing -> mempty  instance (Eq vn, IsName vn, Annot f) => Pretty (SpecBase f vn) where   ppr (TypeAbbrSpec tpsig) = ppr tpsig@@ -451,44 +486,54 @@ instance (Eq vn, IsName vn, Annot f) => Pretty (ModBindBase f vn) where   ppr (ModBind name ps sig e _ _) =     text "module" <+> pprName name <+> spread (map ppr ps) <+> sig' <> text " =" <+> ppr e-    where sig' = case sig of Nothing    -> mempty-                             Just (s,_) -> colon <+> ppr s <> text " "+    where+      sig' = case sig of+        Nothing -> mempty+        Just (s, _) -> colon <+> ppr s <> text " " -prettyBinOp :: (Eq vn, IsName vn, Annot f) =>-               Int -> QualName vn -> ExpBase f vn -> ExpBase f vn -> Doc-prettyBinOp p bop x y = parensIf (p > symPrecedence) $-                        pprPrec symPrecedence x <+/>-                        bop' <+>-                        pprPrec symRPrecedence y-  where bop' = case leading of Backtick -> text "`" <> ppr bop <> text "`"-                               _        -> ppr bop-        leading = leadingOperator $ nameFromString $ pretty $ pprName $ qualLeaf bop-        symPrecedence = precedence leading-        symRPrecedence = rprecedence leading-        precedence PipeRight = -1-        precedence PipeLeft  = -1-        precedence LogAnd   = 0-        precedence LogOr    = 0-        precedence Band     = 1-        precedence Bor      = 1-        precedence Xor      = 1-        precedence Equal    = 2-        precedence NotEqual = 2-        precedence Less     = 2-        precedence Leq      = 2-        precedence Greater  = 2-        precedence Geq      = 2-        precedence ShiftL   = 3-        precedence ShiftR   = 3-        precedence Plus     = 4-        precedence Minus    = 4-        precedence Times    = 5-        precedence Divide   = 5-        precedence Mod      = 5-        precedence Quot     = 5-        precedence Rem      = 5-        precedence Pow      = 6-        precedence Backtick = 9-        rprecedence Minus  = 10-        rprecedence Divide = 10-        rprecedence op     = precedence op+prettyBinOp ::+  (Eq vn, IsName vn, Annot f) =>+  Int ->+  QualName vn ->+  ExpBase f vn ->+  ExpBase f vn ->+  Doc+prettyBinOp p bop x y =+  parensIf (p > symPrecedence) $+    pprPrec symPrecedence x+      <+/> bop'+      <+> pprPrec symRPrecedence y+  where+    bop' = case leading of+      Backtick -> text "`" <> ppr bop <> text "`"+      _ -> ppr bop+    leading = leadingOperator $ nameFromString $ pretty $ pprName $ qualLeaf bop+    symPrecedence = precedence leading+    symRPrecedence = rprecedence leading+    precedence PipeRight = -1+    precedence PipeLeft = -1+    precedence LogAnd = 0+    precedence LogOr = 0+    precedence Band = 1+    precedence Bor = 1+    precedence Xor = 1+    precedence Equal = 2+    precedence NotEqual = 2+    precedence Less = 2+    precedence Leq = 2+    precedence Greater = 2+    precedence Geq = 2+    precedence ShiftL = 3+    precedence ShiftR = 3+    precedence Plus = 4+    precedence Minus = 4+    precedence Times = 5+    precedence Divide = 5+    precedence Mod = 5+    precedence Quot = 5+    precedence Rem = 5+    precedence Pow = 6+    precedence Backtick = 9+    rprecedence Minus = 10+    rprecedence Divide = 10+    rprecedence op = precedence op
src/Language/Futhark/Prop.hs view
@@ -1,1033 +1,1128 @@-{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}--- | This module provides various simple ways to query and manipulate--- fundamental Futhark terms, such as types and values.  The intent is to--- keep "Futhark.Language.Syntax" simple, and put whatever embellishments--- we need here.-module Language.Futhark.Prop-  (-  -- * Various-    Intrinsic(..)-  , intrinsics-  , maxIntrinsicTag-  , namesToPrimTypes-  , qualName-  , qualify-  , typeName-  , valueType-  , primValueType-  , leadingOperator-  , progImports-  , decImports-  , progModuleTypes-  , identifierReference-  , prettyStacktrace--  -- * Queries on expressions-  , typeOf--  -- * Queries on patterns and params-  , patternIdents-  , patternNames-  , patternMap-  , patternType-  , patternStructType-  , patternParam-  , patternOrderZero-  , patternDimNames--  -- * Queries on types-  , uniqueness-  , unique-  , aliases-  , diet-  , arrayRank-  , arrayShape-  , nestedDims-  , orderZero-  , unfoldFunType-  , foldFunType-  , typeVars-  , typeDimNames-  , primByteSize--  -- * Operations on types-  , rank-  , peelArray-  , stripArray-  , arrayOf-  , toStructural-  , toStruct-  , fromStruct-  , setAliases-  , addAliases-  , setUniqueness-  , noSizes-  , addSizes-  , anySizes-  , traverseDims-  , DimPos(..)-  , mustBeExplicit-  , mustBeExplicitInType-  , tupleRecord-  , isTupleRecord-  , areTupleFields-  , tupleFields-  , tupleFieldNames-  , sortFields-  , sortConstrs-  , isTypeParam-  , isSizeParam-  , combineTypeShapes-  , matchDims-  , unscopeType-  , onRecordField--  -- | Values of these types are produces by the parser.  They use-  -- unadorned names and have no type information, apart from that-  -- which is syntactically required.-  , NoInfo(..)-  , UncheckedType-  , UncheckedTypeExp-  , UncheckedIdent-  , UncheckedTypeDecl-  , UncheckedDimIndex-  , UncheckedExp-  , UncheckedModExp-  , UncheckedSigExp-  , UncheckedTypeParam-  , UncheckedPattern-  , UncheckedValBind-  , UncheckedDec-  , UncheckedSpec-  , UncheckedProg-  , UncheckedCase-  )-  where--import           Control.Monad.State-import           Data.Char-import           Data.Foldable-import qualified Data.Map.Strict       as M-import qualified Data.Set              as S-import           Data.List (sortOn, genericLength, isPrefixOf, nub)-import           Data.Maybe-import           Data.Ord-import           Data.Bifunctor-import           Data.Bifoldable-import           Data.Bitraversable (bitraverse)--import           Futhark.Util (maxinum)-import           Futhark.Util.Pretty--import           Language.Futhark.Syntax-import qualified Futhark.IR.Primitive as Primitive---- | Return the dimensionality of a type.  For non-arrays, this is--- zero.  For a one-dimensional array it is one, for a two-dimensional--- it is two, and so forth.-arrayRank :: TypeBase dim as -> Int-arrayRank = shapeRank . arrayShape---- | Return the shape of a type - for non-arrays, this is 'mempty'.-arrayShape :: TypeBase dim as -> ShapeDecl dim-arrayShape (Array _ _ _ ds) = ds-arrayShape _ = mempty---- | Return any shape declarations in the type, with duplicates--- removed.-nestedDims :: TypeBase (DimDecl VName) as -> [DimDecl VName]-nestedDims t =-  case t of Array _ _ a ds ->-              nub $ nestedDims (Scalar a) <> shapeDims ds-            Scalar (Record fs) ->-              nub $ foldMap nestedDims fs-            Scalar Prim{} ->-              mempty-            Scalar (Sum cs) ->-              nub $ foldMap (foldMap nestedDims) cs-            Scalar (Arrow _ v t1 t2) ->-              filter (notV v) $ nestedDims t1 <> nestedDims t2-            Scalar (TypeVar _ _ _ targs) ->-              concatMap typeArgDims targs--  where typeArgDims (TypeArgDim d _) = [d]-        typeArgDims (TypeArgType at _) = nestedDims at--        notV Unnamed  = const True-        notV (Named v) = (/=NamedDim (qualName v))---- | Change the shape of a type to be just the rank.-noSizes :: TypeBase (DimDecl vn) as -> TypeBase () as-noSizes = first $ const ()---- | Add size annotations that are all 'AnyDim'.-addSizes :: TypeBase () as -> TypeBase (DimDecl vn) as-addSizes = first $ const AnyDim---- | Change all size annotations to be 'AnyDim'.-anySizes :: TypeBase (DimDecl vn) as -> TypeBase (DimDecl vn) as-anySizes = first $ const AnyDim---- | Where does this dimension occur?-data DimPos-  = PosImmediate-    -- ^ Immediately in the argument to 'traverseDims'.-  | PosParam-    -- ^ In a function parameter type.-  | PosReturn-    -- ^ In a function return type.-  deriving (Eq, Ord, Show)---- | Perform a traversal (possibly including replacement) on sizes--- that are parameters in a function type, but also including the type--- immediately passed to the function.  Also passes along a set of the--- parameter names inside the type that have come in scope at the--- occurrence of the dimension.-traverseDims :: forall f fdim tdim als.-                Applicative f =>-                (S.Set VName -> DimPos -> fdim -> f tdim)-             -> TypeBase fdim als-             -> f (TypeBase tdim als)-traverseDims f = go mempty PosImmediate-  where go :: forall als'.-              S.Set VName -> DimPos -> TypeBase fdim als'-           -> f (TypeBase tdim als')-        go bound b t@Array{} =-          bitraverse (f bound b) pure t-        go bound b (Scalar (Record fields)) =-          Scalar . Record <$> traverse (go bound b) fields-        go bound b (Scalar (TypeVar as u tn targs)) =-          Scalar <$> (TypeVar as u tn <$> traverse (onTypeArg bound b) targs)-        go bound b (Scalar (Sum cs)) =-          Scalar . Sum <$> traverse (traverse (go bound b)) cs-        go _ _ (Scalar (Prim t)) =-          pure $ Scalar $ Prim t-        go bound _ (Scalar (Arrow als p t1 t2)) =-          Scalar <$> (Arrow als p <$> go bound' PosParam t1 <*> go bound' PosReturn t2)-          where bound' = case p of Named p' -> S.insert p' bound-                                   Unnamed -> bound--        onTypeArg bound b (TypeArgDim d loc) =-          TypeArgDim <$> f bound b d <*> pure loc-        onTypeArg bound b (TypeArgType t loc) =-          TypeArgType <$> go bound b t <*> pure loc--mustBeExplicitAux :: StructType -> M.Map VName Bool-mustBeExplicitAux t =-  execState (traverseDims onDim t) mempty-  where onDim bound _ (NamedDim d)-          | qualLeaf d `S.member` bound =-              modify $ \s -> M.insertWith (&&) (qualLeaf d) False s-        onDim _ PosImmediate (NamedDim d) =-          modify $ \s -> M.insertWith (&&) (qualLeaf d) False s-        onDim _ _ (NamedDim d) =-          modify $ M.insertWith (&&) (qualLeaf d) True-        onDim _ _ _ =-          return ()---- | Figure out which of the sizes in a parameter type must be passed--- explicitly, because their first use is as something else than just--- an array dimension.  'mustBeExplicit' is like this function, but--- first decomposes into parameter types.-mustBeExplicitInType :: StructType -> S.Set VName-mustBeExplicitInType t =-  S.fromList $ M.keys $ M.filter id $ mustBeExplicitAux t---- | Figure out which of the sizes in a binding type must be passed--- explicitly, because their first use is as something else than just--- an array dimension.-mustBeExplicit :: StructType -> S.Set VName-mustBeExplicit bind_t =-  let (ts, ret) = unfoldFunType bind_t-      alsoRet = M.unionWith (&&) $-                M.fromList $ zip (S.toList $ typeDimNames ret) $ repeat True-  in S.fromList $ M.keys $ M.filter id $ alsoRet $ foldl' onType mempty ts-  where onType uses t = uses <> mustBeExplicitAux t -- Left-biased union.---- | Return the uniqueness of a type.-uniqueness :: TypeBase shape as -> Uniqueness-uniqueness (Array _ u _ _) = u-uniqueness (Scalar (TypeVar _ u _ _)) = u-uniqueness (Scalar (Sum ts)) = foldMap (foldMap uniqueness) $ M.elems ts-uniqueness (Scalar (Record fs)) = foldMap uniqueness $ M.elems fs-uniqueness _ = Nonunique---- | @unique t@ is 'True' if the type of the argument is unique.-unique :: TypeBase shape as -> Bool-unique = (==Unique) . uniqueness---- | Return the set of all variables mentioned in the aliasing of a--- type.-aliases :: Monoid as => TypeBase shape as -> as-aliases = bifoldMap (const mempty) id---- | @diet t@ returns a description of how a function parameter of--- type @t@ might consume its argument.-diet :: TypeBase shape as -> Diet-diet (Scalar (Record ets))              = RecordDiet $ fmap diet ets-diet (Scalar (Prim _))                  = Observe-diet (Scalar (Arrow _ _ t1 t2))         = FuncDiet (diet t1) (diet t2)-diet (Array _ Unique _ _)               = Consume-diet (Array _ Nonunique _ _)            = Observe-diet (Scalar (TypeVar _ Unique _ _))    = Consume-diet (Scalar (TypeVar _ Nonunique _ _)) = Observe-diet (Scalar Sum{})                     = Observe---- | Convert any type to one that has rank information, no alias--- information, and no embedded names.-toStructural :: TypeBase dim as-             -> TypeBase () ()-toStructural = flip setAliases () . first (const ())---- | Remove aliasing information from a type.-toStruct :: TypeBase dim as-         -> TypeBase dim ()-toStruct t = t `setAliases` ()---- | Replace no aliasing with an empty alias set.-fromStruct :: TypeBase dim as-           -> TypeBase dim Aliasing-fromStruct t = t `setAliases` S.empty---- | @peelArray n t@ returns the type resulting from peeling the first--- @n@ array dimensions from @t@.  Returns @Nothing@ if @t@ has less--- than @n@ dimensions.-peelArray :: Int -> TypeBase dim as -> Maybe (TypeBase dim as)-peelArray n (Array als u t shape)-  | shapeRank shape == n =-      Just $ Scalar t `addAliases` const als-  | otherwise =-      Array als u t <$> stripDims n shape-peelArray _ _ = Nothing---- | @arrayOf t s u@ constructs an array type.  The convenience--- compared to using the 'Array' constructor directly is that @t@ can--- itself be an array.  If @t@ is an @n@-dimensional array, and @s@ is--- a list of length @n@, the resulting type is of an @n+m@ dimensions.--- The uniqueness of the new array will be @u@, no matter the--- uniqueness of @t@.-arrayOf :: Monoid as =>-           TypeBase dim as-        -> ShapeDecl dim-        -> Uniqueness-        -> TypeBase dim as-arrayOf t = arrayOfWithAliases (t `setUniqueness` Nonunique) mempty--arrayOfWithAliases :: Monoid as =>-                      TypeBase dim as-                   -> as-                   -> ShapeDecl dim-                   -> Uniqueness-                   -> TypeBase dim as-arrayOfWithAliases (Array as1 _ et shape1) as2 shape2 u =-  Array (as1<>as2) u et (shape2 <> shape1)-arrayOfWithAliases (Scalar t) as shape u =-  Array as u (second (const ()) t) shape---- | @stripArray n t@ removes the @n@ outermost layers of the array.--- Essentially, it is the type of indexing an array of type @t@ with--- @n@ indexes.-stripArray :: Int -> TypeBase dim as -> TypeBase dim as-stripArray n (Array als u et shape)-  | Just shape' <- stripDims n shape =-      Array als u et shape'-  | otherwise =-      Scalar et `setUniqueness` u `setAliases` als-stripArray _ t = t---- | Create a record type corresponding to a tuple with the given--- element types.-tupleRecord :: [TypeBase dim as] -> TypeBase dim as-tupleRecord = Scalar . Record . M.fromList . zip tupleFieldNames---- | Does this type corespond to a tuple?  If so, return the elements--- of that tuple.-isTupleRecord :: TypeBase dim as -> Maybe [TypeBase dim as]-isTupleRecord (Scalar (Record fs)) = areTupleFields fs-isTupleRecord _ = Nothing---- | Does this record map correspond to a tuple?-areTupleFields :: M.Map Name a -> Maybe [a]-areTupleFields fs =-  let fs' = sortFields fs-  in if and $ zipWith (==) (map fst fs') tupleFieldNames-     then Just $ map snd fs'-     else Nothing---- | Construct a record map corresponding to a tuple.-tupleFields :: [a] -> M.Map Name a-tupleFields as = M.fromList $ zip tupleFieldNames as---- | Increasing field names for a tuple (starts at 0).-tupleFieldNames :: [Name]-tupleFieldNames = map (nameFromString . show) [(0::Int)..]---- | Sort fields by their name; taking care to sort numeric fields by--- their numeric value.  This ensures that tuples and tuple-like--- records match.-sortFields :: M.Map Name a -> [(Name,a)]-sortFields l = map snd $ sortOn fst $ zip (map (fieldish . fst) l') l'-  where l' = M.toList l-        fieldish s = case reads $ nameToString s of-          [(x, "")] -> Left (x::Int)-          _         -> Right s---- | Sort the constructors of a sum type in some well-defined (but not--- otherwise significant) manner.-sortConstrs :: M.Map Name a -> [(Name, a)]-sortConstrs cs = sortOn fst $ M.toList cs---- | Is this a 'TypeParamType'?-isTypeParam :: TypeParamBase vn -> Bool-isTypeParam TypeParamType{} = True-isTypeParam TypeParamDim{}  = False---- | Is this a 'TypeParamDim'?-isSizeParam :: TypeParamBase vn -> Bool-isSizeParam = not . isTypeParam---- | Combine the shape information of types as much as possible. The first--- argument is the orignal type and the second is the type of the transformed--- expression. This is necessary since the original type may contain additional--- information (e.g., shape restrictions) from the user given annotation.-combineTypeShapes :: (Monoid as, ArrayDim dim) =>-                     TypeBase dim as -> TypeBase dim as -> TypeBase dim as-combineTypeShapes (Scalar (Record ts1)) (Scalar (Record ts2))-  | M.keys ts1 == M.keys ts2 =-      Scalar $ Record $ M.map (uncurry combineTypeShapes) (M.intersectionWith (,) ts1 ts2)-combineTypeShapes (Scalar (Arrow als1 p1 a1 b1)) (Scalar (Arrow als2 _p2 a2 b2)) =-  Scalar $ Arrow (als1<>als2) p1 (combineTypeShapes a1 a2) (combineTypeShapes b1 b2)-combineTypeShapes (Scalar (TypeVar als1 u1 v targs1)) (Scalar (TypeVar als2 _ _ targs2)) =-  Scalar $ TypeVar (als1<>als2) u1 v $ zipWith f targs1 targs2-  where f (TypeArgType t1 loc) (TypeArgType t2 _) =-          TypeArgType (combineTypeShapes t1 t2) loc-        f targ _ = targ-combineTypeShapes (Array als1 u1 et1 shape1) (Array als2 _u2 et2 _shape2) =-  arrayOfWithAliases (combineTypeShapes (Scalar et1) (Scalar et2)-                       `setAliases` mempty)-  (als1<>als2) shape1 u1-combineTypeShapes _ new_tp = new_tp---- | Match the dimensions of otherwise assumed-equal types.-matchDims :: (Monoid as, Monad m) =>-             (d1 -> d2 -> m d1)-          -> TypeBase d1 as -> TypeBase d2 as-          -> m (TypeBase d1 as)-matchDims onDims t1 t2 =-  case (t1, t2) of-    (Array als1 u1 et1 shape1, Array als2 u2 et2 shape2) ->-      flip setAliases (als1<>als2) <$>-      (arrayOf <$>-       matchDims onDims (Scalar et1) (Scalar et2) <*>-       onShapes shape1 shape2 <*> pure (min u1 u2))-    (Scalar (Record f1), Scalar (Record f2)) ->-      Scalar . Record <$>-      traverse (uncurry (matchDims onDims)) (M.intersectionWith (,) f1 f2)-    (Scalar (Sum cs1), Scalar (Sum cs2)) ->-      Scalar . Sum <$>-      traverse (traverse (uncurry (matchDims onDims)))-      (M.intersectionWith zip cs1 cs2)-    (Scalar (Arrow als1 p1 a1 b1), Scalar (Arrow als2 _p2 a2 b2)) ->-      Scalar <$>-      (Arrow (als1 <> als2) p1 <$> matchDims onDims a1 a2 <*> matchDims onDims b1 b2)-    (Scalar (TypeVar als1 u v targs1),-     Scalar (TypeVar als2 _ _ targs2)) ->-      Scalar . TypeVar (als1 <> als2) u v <$> zipWithM matchTypeArg targs1 targs2-    _ -> return t1--  where matchTypeArg ta@TypeArgType{} _ = return ta-        matchTypeArg a _ = return a--        onShapes shape1 shape2 =-          ShapeDecl <$> zipWithM onDims (shapeDims shape1) (shapeDims shape2)----- | Set the uniqueness attribute of a type.  If the type is a record--- or sum type, the uniqueness of its components will be modified.-setUniqueness :: TypeBase dim as -> Uniqueness -> TypeBase dim as-setUniqueness (Array als _ et shape) u =-  Array als u et shape-setUniqueness (Scalar (TypeVar als _ t targs)) u =-  Scalar $ TypeVar als u t targs-setUniqueness (Scalar (Record ets)) u =-  Scalar $ Record $ fmap (`setUniqueness` u) ets-setUniqueness (Scalar (Sum ets)) u =-  Scalar $ Sum $ fmap (map (`setUniqueness` u)) ets-setUniqueness t _ = t---- | @t \`setAliases\` als@ returns @t@, but with @als@ substituted for--- any already present aliasing.-setAliases :: TypeBase dim asf -> ast -> TypeBase dim ast-setAliases t = addAliases t . const---- | @t \`addAliases\` f@ returns @t@, but with any already present--- aliasing replaced by @f@ applied to that aliasing.-addAliases :: TypeBase dim asf -> (asf -> ast)-           -> TypeBase dim ast-addAliases = flip second--intValueType :: IntValue -> IntType-intValueType Int8Value{}  = Int8-intValueType Int16Value{} = Int16-intValueType Int32Value{} = Int32-intValueType Int64Value{} = Int64--floatValueType :: FloatValue -> FloatType-floatValueType Float32Value{} = Float32-floatValueType Float64Value{} = Float64---- | The type of a basic value.-primValueType :: PrimValue -> PrimType-primValueType (SignedValue v)   = Signed $ intValueType v-primValueType (UnsignedValue v) = Unsigned $ intValueType v-primValueType (FloatValue v)    = FloatType $ floatValueType v-primValueType BoolValue{}       = Bool---- | The type of the value.-valueType :: Value -> ValueType-valueType (PrimValue bv) = Scalar $ Prim $ primValueType bv-valueType (ArrayValue _ t) = t---- | The size of values of this type, in bytes.-primByteSize :: Num a => PrimType -> a-primByteSize (Signed it) = Primitive.intByteSize it-primByteSize (Unsigned it) = Primitive.intByteSize it-primByteSize (FloatType ft) = Primitive.floatByteSize ft-primByteSize Bool = 1---- | Construct a 'ShapeDecl' with the given number of 'AnyDim'--- dimensions.-rank :: Int -> ShapeDecl (DimDecl VName)-rank n = ShapeDecl $ replicate n AnyDim---- | The type is leaving a scope, so clean up any aliases that--- reference the bound variables, and turn any dimensions that name--- them into AnyDim instead.-unscopeType :: S.Set VName -> PatternType -> PatternType-unscopeType bound_here t = first onDim $ t `addAliases` S.map unbind-  where unbind (AliasBound v) | v `S.member` bound_here = AliasFree v-        unbind a = a-        onDim (NamedDim qn) | qualLeaf qn `S.member` bound_here = AnyDim-        onDim d = d---- | Perform some operation on a given record field.  Returns--- 'Nothing' if that field does not exist.-onRecordField :: (TypeBase dim als -> TypeBase dim als)-              -> [Name]-              -> TypeBase dim als -> Maybe (TypeBase dim als)-onRecordField f [] t = Just $ f t-onRecordField f (k:ks) (Scalar (Record m)) = do-  t <- onRecordField f ks =<< M.lookup k m-  Just $ Scalar $ Record $ M.insert k t m-onRecordField _ _ _ = Nothing---- | The type of an Futhark term.  The aliasing will refer to itself, if--- the term is a non-tuple-typed variable.-typeOf :: ExpBase Info VName -> PatternType-typeOf (Literal val _) = Scalar $ Prim $ primValueType val-typeOf (IntLit _ (Info t) _) = t-typeOf (FloatLit _ (Info t) _) = t-typeOf (Parens e _) = typeOf e-typeOf (QualParens _ e _) = typeOf e-typeOf (TupLit es _) = tupleRecord $ map typeOf es-typeOf (RecordLit fs _) =-  -- Reverse, because M.unions is biased to the left.-  Scalar $ Record $ M.unions $ reverse $ map record fs-  where record (RecordFieldExplicit name e _) = M.singleton name $ typeOf e-        record (RecordFieldImplicit name (Info t) _) =-          M.singleton (baseName name) $ t-          `addAliases` S.insert (AliasBound name)-typeOf (ArrayLit _ (Info t) _) = t-typeOf (StringLit vs _) =-  Array mempty Unique (Prim (Unsigned Int8))-  (ShapeDecl [ConstDim $ genericLength vs])-typeOf (Range _ _ _ (Info t, _) _) = t-typeOf (BinOp _ _ _ _ (Info t) _ _) = t-typeOf (Project _ _ (Info t) _) = t-typeOf (If _ _ _ (Info t, _) _) = t-typeOf (Var _ (Info t) _) = t-typeOf (Ascript e _ _) = typeOf e-typeOf (Coerce _ _ (Info t, _) _) = t-typeOf (Apply _ _ _ (Info t, _) _) = t-typeOf (Negate e _) = typeOf e-typeOf (LetPat _ _ _ (Info t, _) _) = t-typeOf (LetFun _ _ _ (Info t) _) = t-typeOf (LetWith _ _ _ _ _ (Info t) _) = t-typeOf (Index _ _ (Info t, _) _) = t-typeOf (Update e _ _ _) = typeOf e `setAliases` mempty-typeOf (RecordUpdate _ _ _ (Info t) _) = t-typeOf (Assert _ e _ _) = typeOf e-typeOf (DoLoop _ _ _ _ _ (Info (t, _)) _) = t-typeOf (Lambda params _ _ (Info (als, t)) _) =-  unscopeType bound_here $ foldr (arrow . patternParam) t params `setAliases` als-  where bound_here = S.map identName (mconcat $ map patternIdents params) `S.difference`-                     S.fromList (mapMaybe (named . patternParam) params)-        arrow (px, tx) y = Scalar $ Arrow () px tx y-        named (Named x, _) = Just x-        named (Unnamed, _) = Nothing-typeOf (OpSection _ (Info t) _) =-  t-typeOf (OpSectionLeft _ _ _ (_, Info pt2) (Info ret, _) _)  =-  foldFunType [fromStruct pt2] ret-typeOf (OpSectionRight _ _ _ (Info pt1, _) (Info ret) _) =-  foldFunType [fromStruct pt1] ret-typeOf (ProjectSection _ (Info t) _) = t-typeOf (IndexSection _ (Info t) _) = t-typeOf (Constr _ _ (Info t) _)  = t-typeOf (Match _ cs (Info t, _) _) =-  unscopeType (foldMap unscopeSet cs) t-  where unscopeSet (CasePat p _ _) = S.map identName $ patternIdents p-typeOf (Attr _ e _) = typeOf e---- | @foldFunType ts ret@ creates a function type ('Arrow') that takes--- @ts@ as parameters and returns @ret@.-foldFunType :: Monoid as => [TypeBase dim as] -> TypeBase dim as -> TypeBase dim as-foldFunType ps ret = foldr arrow ret ps-  where arrow t1 t2 = Scalar $ Arrow mempty Unnamed t1 t2---- | Extract the parameter types and return type from a type.--- If the type is not an arrow type, the list of parameter types is empty.-unfoldFunType :: TypeBase dim as -> ([TypeBase dim as], TypeBase dim as)-unfoldFunType (Scalar (Arrow _ _ t1 t2)) =-  let (ps, r) = unfoldFunType t2-  in (t1 : ps, r)-unfoldFunType t = ([], t)---- | The type names mentioned in a type.-typeVars :: Monoid as => TypeBase dim as -> S.Set VName-typeVars t =-  case t of-    Scalar Prim{} -> mempty-    Scalar (TypeVar _ _ tn targs) ->-      mconcat $ typeVarFree tn : map typeArgFree targs-    Scalar (Arrow _ _ t1 t2) -> typeVars t1 <> typeVars t2-    Scalar (Record fields) -> foldMap typeVars fields-    Scalar (Sum cs) -> mconcat $ (foldMap . fmap) typeVars cs-    Array _ _ rt _ -> typeVars $ Scalar rt-  where typeVarFree = S.singleton . typeLeaf-        typeArgFree (TypeArgType ta _) = typeVars ta-        typeArgFree TypeArgDim{} = mempty---- | @orderZero t@ is 'True' if the argument type has order 0, i.e., it is not--- a function type, does not contain a function type as a subcomponent, and may--- not be instantiated with a function type.-orderZero :: TypeBase dim as -> Bool-orderZero Array{}     = True-orderZero (Scalar (Prim _)) = True-orderZero (Scalar (Record fs)) = all orderZero $ M.elems fs-orderZero (Scalar TypeVar{}) = True-orderZero (Scalar Arrow{}) = False-orderZero (Scalar (Sum cs)) = all (all orderZero) cs---- | Extract all the shape names that occur in a given pattern.-patternDimNames :: PatternBase Info VName -> S.Set VName-patternDimNames (TuplePattern ps _)    = foldMap patternDimNames ps-patternDimNames (RecordPattern fs _)   = foldMap (patternDimNames . snd) fs-patternDimNames (PatternParens p _)    = patternDimNames p-patternDimNames (Id _ (Info tp) _)     = typeDimNames tp-patternDimNames (Wildcard (Info tp) _) = typeDimNames tp-patternDimNames (PatternAscription p (TypeDecl _ (Info t)) _) =-  patternDimNames p <> typeDimNames t-patternDimNames (PatternLit _ (Info tp) _) = typeDimNames tp-patternDimNames (PatternConstr _ _ ps _) = foldMap patternDimNames ps---- | Extract all the shape names that occur in a given type.-typeDimNames :: TypeBase (DimDecl VName) als -> S.Set VName-typeDimNames = foldMap dimName . nestedDims-  where dimName :: DimDecl VName -> S.Set VName-        dimName (NamedDim qn) = S.singleton $ qualLeaf qn-        dimName _             = mempty---- | @patternOrderZero pat@ is 'True' if all of the types in the given pattern--- have order 0.-patternOrderZero :: PatternBase Info vn -> Bool-patternOrderZero pat = case pat of-  TuplePattern ps _       -> all patternOrderZero ps-  RecordPattern fs _      -> all (patternOrderZero . snd) fs-  PatternParens p _       -> patternOrderZero p-  Id _ (Info t) _         -> orderZero t-  Wildcard (Info t) _     -> orderZero t-  PatternAscription p _ _ -> patternOrderZero p-  PatternLit _ (Info t) _ -> orderZero t-  PatternConstr _ _ ps _  -> all patternOrderZero ps---- | The set of identifiers bound in a pattern.-patternIdents :: (Functor f, Ord vn) => PatternBase f vn -> S.Set (IdentBase f vn)-patternIdents (Id v t loc)              = S.singleton $ Ident v t loc-patternIdents (PatternParens p _)       = patternIdents p-patternIdents (TuplePattern pats _)     = mconcat $ map patternIdents pats-patternIdents (RecordPattern fs _)      = mconcat $ map (patternIdents . snd) fs-patternIdents Wildcard{}                = mempty-patternIdents (PatternAscription p _ _) = patternIdents p-patternIdents PatternLit{}              = mempty-patternIdents (PatternConstr _ _ ps _ ) = mconcat $ map patternIdents ps---- | The set of names bound in a pattern.-patternNames :: (Functor f, Ord vn) => PatternBase f vn -> S.Set vn-patternNames (Id v _ _)                = S.singleton v-patternNames (PatternParens p _)       = patternNames p-patternNames (TuplePattern pats _)     = mconcat $ map patternNames pats-patternNames (RecordPattern fs _)      = mconcat $ map (patternNames . snd) fs-patternNames Wildcard{}                = mempty-patternNames (PatternAscription p _ _) = patternNames p-patternNames PatternLit{}              = mempty-patternNames (PatternConstr _ _ ps _ ) = mconcat $ map patternNames ps---- | A mapping from names bound in a map to their identifier.-patternMap :: (Functor f) => PatternBase f VName -> M.Map VName (IdentBase f VName)-patternMap pat =-  M.fromList $ zip (map identName idents) idents-  where idents = S.toList $ patternIdents pat---- | The type of values bound by the pattern.-patternType :: PatternBase Info VName -> PatternType-patternType (Wildcard (Info t) _)          = t-patternType (PatternParens p _)            = patternType p-patternType (Id _ (Info t) _)              = t-patternType (TuplePattern pats _)          = tupleRecord $ map patternType pats-patternType (RecordPattern fs _)           = Scalar $ Record $ patternType <$> M.fromList fs-patternType (PatternAscription p _ _)      = patternType p-patternType (PatternLit _ (Info t) _)      = t-patternType (PatternConstr _ (Info t) _ _) = t---- | The type matched by the pattern, including shape declarations if present.-patternStructType :: PatternBase Info VName -> StructType-patternStructType = toStruct . patternType---- | When viewed as a function parameter, does this pattern correspond--- to a named parameter of some type?-patternParam :: PatternBase Info VName -> (PName, StructType)-patternParam (PatternParens p _) =-  patternParam p-patternParam (PatternAscription (Id v _ _) td _) =-  (Named v, unInfo $ expandedType td)-patternParam (Id v (Info t) _) =-  (Named v, toStruct t)-patternParam p =-  (Unnamed, patternStructType p)---- | Names of primitive types to types.  This is only valid if no--- shadowing is going on, but useful for tools.-namesToPrimTypes :: M.Map Name PrimType-namesToPrimTypes = M.fromList-                   [ (nameFromString $ pretty t, t) |-                     t <- Bool :-                          map Signed [minBound..maxBound] ++-                          map Unsigned [minBound..maxBound] ++-                          map FloatType [minBound..maxBound] ]---- | The nature of something predefined.  These can either be--- monomorphic or overloaded.  An overloaded builtin is a list valid--- types it can be instantiated with, to the parameter and result--- type, with 'Nothing' representing the overloaded parameter type.-data Intrinsic = IntrinsicMonoFun [PrimType] PrimType-               | IntrinsicOverloadedFun [PrimType] [Maybe PrimType] (Maybe PrimType)-               | IntrinsicPolyFun [TypeParamBase VName] [StructType] StructType-               | IntrinsicType PrimType-               | IntrinsicEquality -- Special cased.---- | A map of all built-ins.-intrinsics :: M.Map VName Intrinsic-intrinsics = M.fromList $ zipWith namify [10..] $--             map primFun (M.toList Primitive.primFuns) ++--             [("opaque", IntrinsicPolyFun [tp_a] [Scalar t_a] $ Scalar t_a)] ++--             map unOpFun Primitive.allUnOps ++--             map binOpFun Primitive.allBinOps ++--             map cmpOpFun Primitive.allCmpOps ++--             map convOpFun Primitive.allConvOps ++--             map signFun Primitive.allIntTypes ++--             map unsignFun Primitive.allIntTypes ++--             map intrinsicType (map Signed [minBound..maxBound] ++-                                map Unsigned [minBound..maxBound] ++-                                map FloatType [minBound..maxBound] ++-                                [Bool]) ++--             -- This overrides the ! from Primitive.-             [ ("!", IntrinsicOverloadedFun-                     (map Signed [minBound..maxBound] ++-                      map Unsigned [minBound..maxBound] ++-                     [Bool])-                     [Nothing] Nothing) ] ++--             -- The reason for the loop formulation is to ensure that we-             -- get a missing case warning if we forget a case.-             mapMaybe mkIntrinsicBinOp [minBound..maxBound] ++--             [("flatten", IntrinsicPolyFun [tp_a]-                          [Array () Nonunique t_a (rank 2)] $-                          Array () Nonunique t_a (rank 1)),-              ("unflatten", IntrinsicPolyFun [tp_a]-                            [Scalar $ Prim $ Signed Int32,-                             Scalar $ Prim $ Signed Int32,-                             Array () Nonunique t_a (rank 1)] $-                            Array () Nonunique t_a (rank 2)),--              ("concat", IntrinsicPolyFun [tp_a]-                         [arr_a, arr_a] uarr_a),-              ("rotate", IntrinsicPolyFun [tp_a]-                         [Scalar $ Prim $ Signed Int32, arr_a] arr_a),-              ("transpose", IntrinsicPolyFun [tp_a] [arr_2d_a] arr_2d_a),--               ("scatter", IntrinsicPolyFun [tp_a]-                          [Array () Unique t_a (rank 1),-                           Array () Nonunique (Prim $ Signed Int32) (rank 1),-                           Array () Nonunique t_a (rank 1)] $-                          Array () Unique t_a (rank 1)),--              ("zip", IntrinsicPolyFun [tp_a, tp_b] [arr_a, arr_b] arr_a_b),-              ("unzip", IntrinsicPolyFun [tp_a, tp_b] [arr_a_b] t_arr_a_arr_b),--              ("hist", IntrinsicPolyFun [tp_a]-                       [Scalar $ Prim $ Signed Int32,-                        uarr_a,-                        Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a),-                        Scalar t_a,-                        Array () Nonunique (Prim $ Signed Int32) (rank 1),-                        arr_a]-                       uarr_a),--              ("map", IntrinsicPolyFun [tp_a, tp_b] [Scalar t_a `arr` Scalar t_b, arr_a] uarr_b),--              ("reduce", IntrinsicPolyFun [tp_a]-                         [Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a), Scalar t_a, arr_a] $-                         Scalar t_a),--              ("reduce_comm", IntrinsicPolyFun [tp_a]-                              [Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a), Scalar t_a, arr_a] $-                              Scalar t_a),--              ("scan", IntrinsicPolyFun [tp_a]-                       [Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a), Scalar t_a, arr_a] uarr_a),--              ("partition",-               IntrinsicPolyFun [tp_a]-               [Scalar (Prim $ Signed Int32),-                Scalar t_a `arr` Scalar (Prim $ Signed Int32), arr_a] $-               tupleRecord [uarr_a, Array () Unique (Prim $ Signed Int32) (rank 1)]),--              ("map_stream",-               IntrinsicPolyFun [tp_a, tp_b]-                [Scalar (Prim $ Signed Int32) `karr` (arr_ka `arr` arr_kb), arr_a]-                uarr_b),--              ("map_stream_per",-               IntrinsicPolyFun [tp_a, tp_b]-                [Scalar (Prim $ Signed Int32) `karr` (arr_ka `arr` arr_kb), arr_a]-                uarr_b),--              ("reduce_stream",-               IntrinsicPolyFun [tp_a, tp_b]-                [Scalar t_b `arr` (Scalar t_b `arr` Scalar t_b),-                 Scalar (Prim $ Signed Int32) `karr` (arr_ka `arr` Scalar t_b),-                 arr_a] $-                Scalar t_b),--              ("reduce_stream_per",-               IntrinsicPolyFun [tp_a, tp_b]-                [Scalar t_b `arr` (Scalar t_b `arr` Scalar t_b),-                 Scalar (Prim $ Signed Int32) `karr` (arr_ka `arr` Scalar t_b),-                 arr_a] $-                Scalar t_b),--              ("trace", IntrinsicPolyFun [tp_a] [Scalar t_a] $ Scalar t_a),-              ("break", IntrinsicPolyFun [tp_a] [Scalar t_a] $ Scalar t_a)]--  where tv_a = VName (nameFromString "a") 0-        t_a = TypeVar () Nonunique (typeName tv_a) []-        arr_a = Array () Nonunique t_a (rank 1)-        arr_2d_a = Array () Nonunique t_a (rank 2)-        uarr_a = Array () Unique t_a (rank 1)-        tp_a = TypeParamType Unlifted tv_a mempty--        tv_b = VName (nameFromString "b") 1-        t_b = TypeVar () Nonunique (typeName tv_b) []-        arr_b = Array () Nonunique t_b (rank 1)-        uarr_b = Array () Unique t_b (rank 1)-        tp_b = TypeParamType Unlifted tv_b mempty--        arr_a_b = Array () Nonunique-                  (Record (M.fromList $ zip tupleFieldNames [Scalar t_a, Scalar t_b]))-                  (rank 1)-        t_arr_a_arr_b = Scalar $ Record $ M.fromList $ zip tupleFieldNames [arr_a, arr_b]--        arr x y = Scalar $ Arrow mempty Unnamed x y--        kv = VName (nameFromString "k") 2-        arr_ka = Array () Nonunique t_a (ShapeDecl [NamedDim $ qualName kv])-        arr_kb = Array () Nonunique t_b (ShapeDecl [NamedDim $ qualName kv])-        karr x y = Scalar $ Arrow mempty (Named kv) x y--        namify i (k,v) = (VName (nameFromString k) i, v)--        primFun (name, (ts,t, _)) =-          (name, IntrinsicMonoFun (map unPrim ts) $ unPrim t)--        unOpFun bop = (pretty bop, IntrinsicMonoFun [t] t)-          where t = unPrim $ Primitive.unOpType bop--        binOpFun bop = (pretty bop, IntrinsicMonoFun [t, t] t)-          where t = unPrim $ Primitive.binOpType bop--        cmpOpFun bop = (pretty bop, IntrinsicMonoFun [t, t] Bool)-          where t = unPrim $ Primitive.cmpOpType bop--        convOpFun cop = (pretty cop, IntrinsicMonoFun [unPrim ft] $ unPrim tt)-          where (ft, tt) = Primitive.convOpType cop--        signFun t = ("sign_" ++ pretty t, IntrinsicMonoFun [Unsigned t] $ Signed t)--        unsignFun t = ("unsign_" ++ pretty t, IntrinsicMonoFun [Signed t] $ Unsigned t)--        unPrim (Primitive.IntType t) = Signed t-        unPrim (Primitive.FloatType t) = FloatType t-        unPrim Primitive.Bool = Bool-        unPrim Primitive.Cert = Bool--        intrinsicType t = (pretty t, IntrinsicType t)--        anyIntType = map Signed [minBound..maxBound] ++-                     map Unsigned [minBound..maxBound]-        anyNumberType = anyIntType ++-                        map FloatType [minBound..maxBound]-        anyPrimType = Bool : anyNumberType--        mkIntrinsicBinOp :: BinOp -> Maybe (String, Intrinsic)-        mkIntrinsicBinOp op = do op' <- intrinsicBinOp op-                                 return (pretty op, op')--        binOp ts = Just $ IntrinsicOverloadedFun ts [Nothing, Nothing] Nothing-        ordering = Just $ IntrinsicOverloadedFun anyPrimType [Nothing, Nothing] (Just Bool)--        intrinsicBinOp Plus     = binOp anyNumberType-        intrinsicBinOp Minus    = binOp anyNumberType-        intrinsicBinOp Pow      = binOp anyNumberType-        intrinsicBinOp Times    = binOp anyNumberType-        intrinsicBinOp Divide   = binOp anyNumberType-        intrinsicBinOp Mod      = binOp anyNumberType-        intrinsicBinOp Quot     = binOp anyIntType-        intrinsicBinOp Rem      = binOp anyIntType-        intrinsicBinOp ShiftR   = binOp anyIntType-        intrinsicBinOp ShiftL   = binOp anyIntType-        intrinsicBinOp Band     = binOp anyIntType-        intrinsicBinOp Xor      = binOp anyIntType-        intrinsicBinOp Bor      = binOp anyIntType-        intrinsicBinOp LogAnd   = binOp [Bool]-        intrinsicBinOp LogOr    = binOp [Bool]-        intrinsicBinOp Equal    = Just IntrinsicEquality-        intrinsicBinOp NotEqual = Just IntrinsicEquality-        intrinsicBinOp Less     = ordering-        intrinsicBinOp Leq      = ordering-        intrinsicBinOp Greater  = ordering-        intrinsicBinOp Geq      = ordering-        intrinsicBinOp _        = Nothing---- | The largest tag used by an intrinsic - this can be used to--- determine whether a 'VName' refers to an intrinsic or a user-defined name.-maxIntrinsicTag :: Int-maxIntrinsicTag = maxinum $ map baseTag $ M.keys intrinsics---- | Create a name with no qualifiers from a name.-qualName :: v -> QualName v-qualName = QualName []---- | Add another qualifier (at the head) to a qualified name.-qualify :: v -> QualName v -> QualName v-qualify k (QualName ks v) = QualName (k:ks) v---- | Create a type name name with no qualifiers from a 'VName'.-typeName :: VName -> TypeName-typeName = typeNameFromQualName . qualName---- | The modules imported by a Futhark program.-progImports :: ProgBase f vn -> [(String,SrcLoc)]-progImports = concatMap decImports . progDecs---- | The modules imported by a single declaration.-decImports :: DecBase f vn -> [(String,SrcLoc)]-decImports (OpenDec x _) = modExpImports x-decImports (ModDec md) = modExpImports $ modExp md-decImports SigDec{} = []-decImports TypeDec{} = []-decImports ValDec{} = []-decImports (LocalDec d _) = decImports d-decImports (ImportDec x _ loc) = [(x, loc)]--modExpImports :: ModExpBase f vn -> [(String,SrcLoc)]-modExpImports ModVar{}              = []-modExpImports (ModParens p _)       = modExpImports p-modExpImports (ModImport f _ loc)   = [(f,loc)]-modExpImports (ModDecs ds _)        = concatMap decImports ds-modExpImports (ModApply _ me _ _ _) = modExpImports me-modExpImports (ModAscript me _ _ _) = modExpImports me-modExpImports ModLambda{}           = []---- | The set of module types used in any exported (non-local)--- declaration.-progModuleTypes :: Ord vn => ProgBase f vn -> S.Set vn-progModuleTypes = mconcat . map onDec . progDecs-  where onDec (OpenDec x _) = onModExp x-        onDec (ModDec md) =-          maybe mempty (onSigExp . fst) (modSignature md) <> onModExp (modExp md)-        onDec SigDec{} = mempty-        onDec TypeDec{} = mempty-        onDec ValDec{} = mempty-        onDec LocalDec{} = mempty-        onDec ImportDec{} = mempty--        onModExp ModVar{} = mempty-        onModExp (ModParens p _) = onModExp p-        onModExp ModImport {} = mempty-        onModExp (ModDecs ds _) = mconcat $ map onDec ds-        onModExp (ModApply me1 me2 _ _ _) = onModExp me1 <> onModExp me2-        onModExp (ModAscript me se _ _) = onModExp me <> onSigExp se-        onModExp (ModLambda p r me _) =-          onModParam p <> maybe mempty (onSigExp . fst) r <> onModExp me--        onModParam = onSigExp . modParamType--        onSigExp (SigVar v _ _) = S.singleton $ qualLeaf v-        onSigExp (SigParens e _) = onSigExp e-        onSigExp SigSpecs{} = mempty-        onSigExp (SigWith e _ _) = onSigExp e-        onSigExp (SigArrow _ e1 e2 _) = onSigExp e1 <> onSigExp e2---- | Extract a leading @((name, namespace, file), remainder)@ from a--- documentation comment string.  These are formatted as--- \`name\`\@namespace[\@file].  Let us hope that this pattern does not occur--- anywhere else.-identifierReference :: String -> Maybe ((String, String, Maybe FilePath), String)-identifierReference ('`' : s)-  | (identifier, '`' : '@' : s') <- break (=='`') s,-    (namespace, s'') <- span isAlpha s',-    not $ null namespace =-      case s'' of-        '@' : '"' : s'''-          | (file, '"' : s'''') <- span (/= '"') s''' ->-            Just ((identifier, namespace, Just file), s'''')-        _ -> Just ((identifier, namespace, Nothing), s'')--identifierReference _ = Nothing---- | Given an operator name, return the operator that determines its--- syntactical properties.-leadingOperator :: Name -> BinOp-leadingOperator s = maybe Backtick snd $ find ((`isPrefixOf` s') . fst) $-                    sortOn (Down . length . fst) $-                    zip (map pretty operators) operators-  where s' = nameToString s-        operators :: [BinOp]-        operators = [minBound..maxBound::BinOp]+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | This module provides various simple ways to query and manipulate+-- fundamental Futhark terms, such as types and values.  The intent is to+-- keep "Futhark.Language.Syntax" simple, and put whatever embellishments+-- we need here.+module Language.Futhark.Prop+  ( -- * Various+    Intrinsic (..),+    intrinsics,+    maxIntrinsicTag,+    namesToPrimTypes,+    qualName,+    qualify,+    typeName,+    valueType,+    primValueType,+    leadingOperator,+    progImports,+    decImports,+    progModuleTypes,+    identifierReference,+    prettyStacktrace,++    -- * Queries on expressions+    typeOf,++    -- * Queries on patterns and params+    patternIdents,+    patternNames,+    patternMap,+    patternType,+    patternStructType,+    patternParam,+    patternOrderZero,+    patternDimNames,++    -- * Queries on types+    uniqueness,+    unique,+    aliases,+    diet,+    arrayRank,+    arrayShape,+    nestedDims,+    orderZero,+    unfoldFunType,+    foldFunType,+    typeVars,+    typeDimNames,+    primByteSize,++    -- * Operations on types+    rank,+    peelArray,+    stripArray,+    arrayOf,+    toStructural,+    toStruct,+    fromStruct,+    setAliases,+    addAliases,+    setUniqueness,+    noSizes,+    addSizes,+    anySizes,+    traverseDims,+    DimPos (..),+    mustBeExplicit,+    mustBeExplicitInType,+    tupleRecord,+    isTupleRecord,+    areTupleFields,+    tupleFields,+    tupleFieldNames,+    sortFields,+    sortConstrs,+    isTypeParam,+    isSizeParam,+    combineTypeShapes,+    matchDims,+    unscopeType,+    onRecordField,+    -- | Values of these types are produces by the parser.  They use+    -- unadorned names and have no type information, apart from that+    -- which is syntactically required.+    NoInfo (..),+    UncheckedType,+    UncheckedTypeExp,+    UncheckedIdent,+    UncheckedTypeDecl,+    UncheckedDimIndex,+    UncheckedExp,+    UncheckedModExp,+    UncheckedSigExp,+    UncheckedTypeParam,+    UncheckedPattern,+    UncheckedValBind,+    UncheckedDec,+    UncheckedSpec,+    UncheckedProg,+    UncheckedCase,+  )+where++import Control.Monad.State+import Data.Bifoldable+import Data.Bifunctor+import Data.Bitraversable (bitraverse)+import Data.Char+import Data.Foldable+import Data.List (genericLength, isPrefixOf, nub, sortOn)+import qualified Data.Map.Strict as M+import Data.Maybe+import Data.Ord+import qualified Data.Set as S+import qualified Futhark.IR.Primitive as Primitive+import Futhark.Util (maxinum)+import Futhark.Util.Pretty+import Language.Futhark.Syntax++-- | Return the dimensionality of a type.  For non-arrays, this is+-- zero.  For a one-dimensional array it is one, for a two-dimensional+-- it is two, and so forth.+arrayRank :: TypeBase dim as -> Int+arrayRank = shapeRank . arrayShape++-- | Return the shape of a type - for non-arrays, this is 'mempty'.+arrayShape :: TypeBase dim as -> ShapeDecl dim+arrayShape (Array _ _ _ ds) = ds+arrayShape _ = mempty++-- | Return any shape declarations in the type, with duplicates+-- removed.+nestedDims :: TypeBase (DimDecl VName) as -> [DimDecl VName]+nestedDims t =+  case t of+    Array _ _ a ds ->+      nub $ nestedDims (Scalar a) <> shapeDims ds+    Scalar (Record fs) ->+      nub $ foldMap nestedDims fs+    Scalar Prim {} ->+      mempty+    Scalar (Sum cs) ->+      nub $ foldMap (foldMap nestedDims) cs+    Scalar (Arrow _ v t1 t2) ->+      filter (notV v) $ nestedDims t1 <> nestedDims t2+    Scalar (TypeVar _ _ _ targs) ->+      concatMap typeArgDims targs+  where+    typeArgDims (TypeArgDim d _) = [d]+    typeArgDims (TypeArgType at _) = nestedDims at++    notV Unnamed = const True+    notV (Named v) = (/= NamedDim (qualName v))++-- | Change the shape of a type to be just the rank.+noSizes :: TypeBase (DimDecl vn) as -> TypeBase () as+noSizes = first $ const ()++-- | Add size annotations that are all 'AnyDim'.+addSizes :: TypeBase () as -> TypeBase (DimDecl vn) as+addSizes = first $ const AnyDim++-- | Change all size annotations to be 'AnyDim'.+anySizes :: TypeBase (DimDecl vn) as -> TypeBase (DimDecl vn) as+anySizes = first $ const AnyDim++-- | Where does this dimension occur?+data DimPos+  = -- | Immediately in the argument to 'traverseDims'.+    PosImmediate+  | -- | In a function parameter type.+    PosParam+  | -- | In a function return type.+    PosReturn+  deriving (Eq, Ord, Show)++-- | Perform a traversal (possibly including replacement) on sizes+-- that are parameters in a function type, but also including the type+-- immediately passed to the function.  Also passes along a set of the+-- parameter names inside the type that have come in scope at the+-- occurrence of the dimension.+traverseDims ::+  forall f fdim tdim als.+  Applicative f =>+  (S.Set VName -> DimPos -> fdim -> f tdim) ->+  TypeBase fdim als ->+  f (TypeBase tdim als)+traverseDims f = go mempty PosImmediate+  where+    go ::+      forall als'.+      S.Set VName ->+      DimPos ->+      TypeBase fdim als' ->+      f (TypeBase tdim als')+    go bound b t@Array {} =+      bitraverse (f bound b) pure t+    go bound b (Scalar (Record fields)) =+      Scalar . Record <$> traverse (go bound b) fields+    go bound b (Scalar (TypeVar as u tn targs)) =+      Scalar <$> (TypeVar as u tn <$> traverse (onTypeArg bound b) targs)+    go bound b (Scalar (Sum cs)) =+      Scalar . Sum <$> traverse (traverse (go bound b)) cs+    go _ _ (Scalar (Prim t)) =+      pure $ Scalar $ Prim t+    go bound _ (Scalar (Arrow als p t1 t2)) =+      Scalar <$> (Arrow als p <$> go bound' PosParam t1 <*> go bound' PosReturn t2)+      where+        bound' = case p of+          Named p' -> S.insert p' bound+          Unnamed -> bound++    onTypeArg bound b (TypeArgDim d loc) =+      TypeArgDim <$> f bound b d <*> pure loc+    onTypeArg bound b (TypeArgType t loc) =+      TypeArgType <$> go bound b t <*> pure loc++mustBeExplicitAux :: StructType -> M.Map VName Bool+mustBeExplicitAux t =+  execState (traverseDims onDim t) mempty+  where+    onDim bound _ (NamedDim d)+      | qualLeaf d `S.member` bound =+        modify $ \s -> M.insertWith (&&) (qualLeaf d) False s+    onDim _ PosImmediate (NamedDim d) =+      modify $ \s -> M.insertWith (&&) (qualLeaf d) False s+    onDim _ _ (NamedDim d) =+      modify $ M.insertWith (&&) (qualLeaf d) True+    onDim _ _ _ =+      return ()++-- | Figure out which of the sizes in a parameter type must be passed+-- explicitly, because their first use is as something else than just+-- an array dimension.  'mustBeExplicit' is like this function, but+-- first decomposes into parameter types.+mustBeExplicitInType :: StructType -> S.Set VName+mustBeExplicitInType t =+  S.fromList $ M.keys $ M.filter id $ mustBeExplicitAux t++-- | Figure out which of the sizes in a binding type must be passed+-- explicitly, because their first use is as something else than just+-- an array dimension.+mustBeExplicit :: StructType -> S.Set VName+mustBeExplicit bind_t =+  let (ts, ret) = unfoldFunType bind_t+      alsoRet =+        M.unionWith (&&) $+          M.fromList $ zip (S.toList $ typeDimNames ret) $ repeat True+   in S.fromList $ M.keys $ M.filter id $ alsoRet $ foldl' onType mempty ts+  where+    onType uses t = uses <> mustBeExplicitAux t -- Left-biased union.++-- | Return the uniqueness of a type.+uniqueness :: TypeBase shape as -> Uniqueness+uniqueness (Array _ u _ _) = u+uniqueness (Scalar (TypeVar _ u _ _)) = u+uniqueness (Scalar (Sum ts)) = foldMap (foldMap uniqueness) $ M.elems ts+uniqueness (Scalar (Record fs)) = foldMap uniqueness $ M.elems fs+uniqueness _ = Nonunique++-- | @unique t@ is 'True' if the type of the argument is unique.+unique :: TypeBase shape as -> Bool+unique = (== Unique) . uniqueness++-- | Return the set of all variables mentioned in the aliasing of a+-- type.+aliases :: Monoid as => TypeBase shape as -> as+aliases = bifoldMap (const mempty) id++-- | @diet t@ returns a description of how a function parameter of+-- type @t@ might consume its argument.+diet :: TypeBase shape as -> Diet+diet (Scalar (Record ets)) = RecordDiet $ fmap diet ets+diet (Scalar (Prim _)) = Observe+diet (Scalar (Arrow _ _ t1 t2)) = FuncDiet (diet t1) (diet t2)+diet (Array _ Unique _ _) = Consume+diet (Array _ Nonunique _ _) = Observe+diet (Scalar (TypeVar _ Unique _ _)) = Consume+diet (Scalar (TypeVar _ Nonunique _ _)) = Observe+diet (Scalar Sum {}) = Observe++-- | Convert any type to one that has rank information, no alias+-- information, and no embedded names.+toStructural ::+  TypeBase dim as ->+  TypeBase () ()+toStructural = flip setAliases () . first (const ())++-- | Remove aliasing information from a type.+toStruct ::+  TypeBase dim as ->+  TypeBase dim ()+toStruct t = t `setAliases` ()++-- | Replace no aliasing with an empty alias set.+fromStruct ::+  TypeBase dim as ->+  TypeBase dim Aliasing+fromStruct t = t `setAliases` S.empty++-- | @peelArray n t@ returns the type resulting from peeling the first+-- @n@ array dimensions from @t@.  Returns @Nothing@ if @t@ has less+-- than @n@ dimensions.+peelArray :: Int -> TypeBase dim as -> Maybe (TypeBase dim as)+peelArray n (Array als u t shape)+  | shapeRank shape == n =+    Just $ Scalar t `addAliases` const als+  | otherwise =+    Array als u t <$> stripDims n shape+peelArray _ _ = Nothing++-- | @arrayOf t s u@ constructs an array type.  The convenience+-- compared to using the 'Array' constructor directly is that @t@ can+-- itself be an array.  If @t@ is an @n@-dimensional array, and @s@ is+-- a list of length @n@, the resulting type is of an @n+m@ dimensions.+-- The uniqueness of the new array will be @u@, no matter the+-- uniqueness of @t@.+arrayOf ::+  Monoid as =>+  TypeBase dim as ->+  ShapeDecl dim ->+  Uniqueness ->+  TypeBase dim as+arrayOf t = arrayOfWithAliases (t `setUniqueness` Nonunique) mempty++arrayOfWithAliases ::+  Monoid as =>+  TypeBase dim as ->+  as ->+  ShapeDecl dim ->+  Uniqueness ->+  TypeBase dim as+arrayOfWithAliases (Array as1 _ et shape1) as2 shape2 u =+  Array (as1 <> as2) u et (shape2 <> shape1)+arrayOfWithAliases (Scalar t) as shape u =+  Array as u (second (const ()) t) shape++-- | @stripArray n t@ removes the @n@ outermost layers of the array.+-- Essentially, it is the type of indexing an array of type @t@ with+-- @n@ indexes.+stripArray :: Int -> TypeBase dim as -> TypeBase dim as+stripArray n (Array als u et shape)+  | Just shape' <- stripDims n shape =+    Array als u et shape'+  | otherwise =+    Scalar et `setUniqueness` u `setAliases` als+stripArray _ t = t++-- | Create a record type corresponding to a tuple with the given+-- element types.+tupleRecord :: [TypeBase dim as] -> TypeBase dim as+tupleRecord = Scalar . Record . M.fromList . zip tupleFieldNames++-- | Does this type corespond to a tuple?  If so, return the elements+-- of that tuple.+isTupleRecord :: TypeBase dim as -> Maybe [TypeBase dim as]+isTupleRecord (Scalar (Record fs)) = areTupleFields fs+isTupleRecord _ = Nothing++-- | Does this record map correspond to a tuple?+areTupleFields :: M.Map Name a -> Maybe [a]+areTupleFields fs =+  let fs' = sortFields fs+   in if and $ zipWith (==) (map fst fs') tupleFieldNames+        then Just $ map snd fs'+        else Nothing++-- | Construct a record map corresponding to a tuple.+tupleFields :: [a] -> M.Map Name a+tupleFields as = M.fromList $ zip tupleFieldNames as++-- | Increasing field names for a tuple (starts at 0).+tupleFieldNames :: [Name]+tupleFieldNames = map (nameFromString . show) [(0 :: Int) ..]++-- | Sort fields by their name; taking care to sort numeric fields by+-- their numeric value.  This ensures that tuples and tuple-like+-- records match.+sortFields :: M.Map Name a -> [(Name, a)]+sortFields l = map snd $ sortOn fst $ zip (map (fieldish . fst) l') l'+  where+    l' = M.toList l+    fieldish s = case reads $ nameToString s of+      [(x, "")] -> Left (x :: Int)+      _ -> Right s++-- | Sort the constructors of a sum type in some well-defined (but not+-- otherwise significant) manner.+sortConstrs :: M.Map Name a -> [(Name, a)]+sortConstrs cs = sortOn fst $ M.toList cs++-- | Is this a 'TypeParamType'?+isTypeParam :: TypeParamBase vn -> Bool+isTypeParam TypeParamType {} = True+isTypeParam TypeParamDim {} = False++-- | Is this a 'TypeParamDim'?+isSizeParam :: TypeParamBase vn -> Bool+isSizeParam = not . isTypeParam++-- | Combine the shape information of types as much as possible. The first+-- argument is the orignal type and the second is the type of the transformed+-- expression. This is necessary since the original type may contain additional+-- information (e.g., shape restrictions) from the user given annotation.+combineTypeShapes ::+  (Monoid as, ArrayDim dim) =>+  TypeBase dim as ->+  TypeBase dim as ->+  TypeBase dim as+combineTypeShapes (Scalar (Record ts1)) (Scalar (Record ts2))+  | M.keys ts1 == M.keys ts2 =+    Scalar $ Record $ M.map (uncurry combineTypeShapes) (M.intersectionWith (,) ts1 ts2)+combineTypeShapes (Scalar (Arrow als1 p1 a1 b1)) (Scalar (Arrow als2 _p2 a2 b2)) =+  Scalar $ Arrow (als1 <> als2) p1 (combineTypeShapes a1 a2) (combineTypeShapes b1 b2)+combineTypeShapes (Scalar (TypeVar als1 u1 v targs1)) (Scalar (TypeVar als2 _ _ targs2)) =+  Scalar $ TypeVar (als1 <> als2) u1 v $ zipWith f targs1 targs2+  where+    f (TypeArgType t1 loc) (TypeArgType t2 _) =+      TypeArgType (combineTypeShapes t1 t2) loc+    f targ _ = targ+combineTypeShapes (Array als1 u1 et1 shape1) (Array als2 _u2 et2 _shape2) =+  arrayOfWithAliases+    ( combineTypeShapes (Scalar et1) (Scalar et2)+        `setAliases` mempty+    )+    (als1 <> als2)+    shape1+    u1+combineTypeShapes _ new_tp = new_tp++-- | Match the dimensions of otherwise assumed-equal types.+matchDims ::+  (Monoid as, Monad m) =>+  (d1 -> d2 -> m d1) ->+  TypeBase d1 as ->+  TypeBase d2 as ->+  m (TypeBase d1 as)+matchDims onDims t1 t2 =+  case (t1, t2) of+    (Array als1 u1 et1 shape1, Array als2 u2 et2 shape2) ->+      flip setAliases (als1 <> als2)+        <$> ( arrayOf+                <$> matchDims onDims (Scalar et1) (Scalar et2)+                <*> onShapes shape1 shape2+                <*> pure (min u1 u2)+            )+    (Scalar (Record f1), Scalar (Record f2)) ->+      Scalar . Record+        <$> traverse (uncurry (matchDims onDims)) (M.intersectionWith (,) f1 f2)+    (Scalar (Sum cs1), Scalar (Sum cs2)) ->+      Scalar . Sum+        <$> traverse+          (traverse (uncurry (matchDims onDims)))+          (M.intersectionWith zip cs1 cs2)+    (Scalar (Arrow als1 p1 a1 b1), Scalar (Arrow als2 _p2 a2 b2)) ->+      Scalar+        <$> (Arrow (als1 <> als2) p1 <$> matchDims onDims a1 a2 <*> matchDims onDims b1 b2)+    ( Scalar (TypeVar als1 u v targs1),+      Scalar (TypeVar als2 _ _ targs2)+      ) ->+        Scalar . TypeVar (als1 <> als2) u v <$> zipWithM matchTypeArg targs1 targs2+    _ -> return t1+  where+    matchTypeArg ta@TypeArgType {} _ = return ta+    matchTypeArg a _ = return a++    onShapes shape1 shape2 =+      ShapeDecl <$> zipWithM onDims (shapeDims shape1) (shapeDims shape2)++-- | Set the uniqueness attribute of a type.  If the type is a record+-- or sum type, the uniqueness of its components will be modified.+setUniqueness :: TypeBase dim as -> Uniqueness -> TypeBase dim as+setUniqueness (Array als _ et shape) u =+  Array als u et shape+setUniqueness (Scalar (TypeVar als _ t targs)) u =+  Scalar $ TypeVar als u t targs+setUniqueness (Scalar (Record ets)) u =+  Scalar $ Record $ fmap (`setUniqueness` u) ets+setUniqueness (Scalar (Sum ets)) u =+  Scalar $ Sum $ fmap (map (`setUniqueness` u)) ets+setUniqueness t _ = t++-- | @t \`setAliases\` als@ returns @t@, but with @als@ substituted for+-- any already present aliasing.+setAliases :: TypeBase dim asf -> ast -> TypeBase dim ast+setAliases t = addAliases t . const++-- | @t \`addAliases\` f@ returns @t@, but with any already present+-- aliasing replaced by @f@ applied to that aliasing.+addAliases ::+  TypeBase dim asf ->+  (asf -> ast) ->+  TypeBase dim ast+addAliases = flip second++intValueType :: IntValue -> IntType+intValueType Int8Value {} = Int8+intValueType Int16Value {} = Int16+intValueType Int32Value {} = Int32+intValueType Int64Value {} = Int64++floatValueType :: FloatValue -> FloatType+floatValueType Float32Value {} = Float32+floatValueType Float64Value {} = Float64++-- | The type of a basic value.+primValueType :: PrimValue -> PrimType+primValueType (SignedValue v) = Signed $ intValueType v+primValueType (UnsignedValue v) = Unsigned $ intValueType v+primValueType (FloatValue v) = FloatType $ floatValueType v+primValueType BoolValue {} = Bool++-- | The type of the value.+valueType :: Value -> ValueType+valueType (PrimValue bv) = Scalar $ Prim $ primValueType bv+valueType (ArrayValue _ t) = t++-- | The size of values of this type, in bytes.+primByteSize :: Num a => PrimType -> a+primByteSize (Signed it) = Primitive.intByteSize it+primByteSize (Unsigned it) = Primitive.intByteSize it+primByteSize (FloatType ft) = Primitive.floatByteSize ft+primByteSize Bool = 1++-- | Construct a 'ShapeDecl' with the given number of 'AnyDim'+-- dimensions.+rank :: Int -> ShapeDecl (DimDecl VName)+rank n = ShapeDecl $ replicate n AnyDim++-- | The type is leaving a scope, so clean up any aliases that+-- reference the bound variables, and turn any dimensions that name+-- them into AnyDim instead.+unscopeType :: S.Set VName -> PatternType -> PatternType+unscopeType bound_here t = first onDim $ t `addAliases` S.map unbind+  where+    unbind (AliasBound v) | v `S.member` bound_here = AliasFree v+    unbind a = a+    onDim (NamedDim qn) | qualLeaf qn `S.member` bound_here = AnyDim+    onDim d = d++-- | Perform some operation on a given record field.  Returns+-- 'Nothing' if that field does not exist.+onRecordField ::+  (TypeBase dim als -> TypeBase dim als) ->+  [Name] ->+  TypeBase dim als ->+  Maybe (TypeBase dim als)+onRecordField f [] t = Just $ f t+onRecordField f (k : ks) (Scalar (Record m)) = do+  t <- onRecordField f ks =<< M.lookup k m+  Just $ Scalar $ Record $ M.insert k t m+onRecordField _ _ _ = Nothing++-- | The type of an Futhark term.  The aliasing will refer to itself, if+-- the term is a non-tuple-typed variable.+typeOf :: ExpBase Info VName -> PatternType+typeOf (Literal val _) = Scalar $ Prim $ primValueType val+typeOf (IntLit _ (Info t) _) = t+typeOf (FloatLit _ (Info t) _) = t+typeOf (Parens e _) = typeOf e+typeOf (QualParens _ e _) = typeOf e+typeOf (TupLit es _) = tupleRecord $ map typeOf es+typeOf (RecordLit fs _) =+  -- Reverse, because M.unions is biased to the left.+  Scalar $ Record $ M.unions $ reverse $ map record fs+  where+    record (RecordFieldExplicit name e _) = M.singleton name $ typeOf e+    record (RecordFieldImplicit name (Info t) _) =+      M.singleton (baseName name) $+        t+          `addAliases` S.insert (AliasBound name)+typeOf (ArrayLit _ (Info t) _) = t+typeOf (StringLit vs _) =+  Array+    mempty+    Unique+    (Prim (Unsigned Int8))+    (ShapeDecl [ConstDim $ genericLength vs])+typeOf (Range _ _ _ (Info t, _) _) = t+typeOf (BinOp _ _ _ _ (Info t) _ _) = t+typeOf (Project _ _ (Info t) _) = t+typeOf (If _ _ _ (Info t, _) _) = t+typeOf (Var _ (Info t) _) = t+typeOf (Ascript e _ _) = typeOf e+typeOf (Coerce _ _ (Info t, _) _) = t+typeOf (Apply _ _ _ (Info t, _) _) = t+typeOf (Negate e _) = typeOf e+typeOf (LetPat _ _ _ (Info t, _) _) = t+typeOf (LetFun _ _ _ (Info t) _) = t+typeOf (LetWith _ _ _ _ _ (Info t) _) = t+typeOf (Index _ _ (Info t, _) _) = t+typeOf (Update e _ _ _) = typeOf e `setAliases` mempty+typeOf (RecordUpdate _ _ _ (Info t) _) = t+typeOf (Assert _ e _ _) = typeOf e+typeOf (DoLoop _ _ _ _ _ (Info (t, _)) _) = t+typeOf (Lambda params _ _ (Info (als, t)) _) =+  unscopeType bound_here $ foldr (arrow . patternParam) t params `setAliases` als+  where+    bound_here =+      S.map identName (mconcat $ map patternIdents params)+        `S.difference` S.fromList (mapMaybe (named . patternParam) params)+    arrow (px, tx) y = Scalar $ Arrow () px tx y+    named (Named x, _) = Just x+    named (Unnamed, _) = Nothing+typeOf (OpSection _ (Info t) _) =+  t+typeOf (OpSectionLeft _ _ _ (_, Info pt2) (Info ret, _) _) =+  foldFunType [fromStruct pt2] ret+typeOf (OpSectionRight _ _ _ (Info pt1, _) (Info ret) _) =+  foldFunType [fromStruct pt1] ret+typeOf (ProjectSection _ (Info t) _) = t+typeOf (IndexSection _ (Info t) _) = t+typeOf (Constr _ _ (Info t) _) = t+typeOf (Match _ cs (Info t, _) _) =+  unscopeType (foldMap unscopeSet cs) t+  where+    unscopeSet (CasePat p _ _) = S.map identName $ patternIdents p+typeOf (Attr _ e _) = typeOf e++-- | @foldFunType ts ret@ creates a function type ('Arrow') that takes+-- @ts@ as parameters and returns @ret@.+foldFunType :: Monoid as => [TypeBase dim as] -> TypeBase dim as -> TypeBase dim as+foldFunType ps ret = foldr arrow ret ps+  where+    arrow t1 t2 = Scalar $ Arrow mempty Unnamed t1 t2++-- | Extract the parameter types and return type from a type.+-- If the type is not an arrow type, the list of parameter types is empty.+unfoldFunType :: TypeBase dim as -> ([TypeBase dim as], TypeBase dim as)+unfoldFunType (Scalar (Arrow _ _ t1 t2)) =+  let (ps, r) = unfoldFunType t2+   in (t1 : ps, r)+unfoldFunType t = ([], t)++-- | The type names mentioned in a type.+typeVars :: Monoid as => TypeBase dim as -> S.Set VName+typeVars t =+  case t of+    Scalar Prim {} -> mempty+    Scalar (TypeVar _ _ tn targs) ->+      mconcat $ typeVarFree tn : map typeArgFree targs+    Scalar (Arrow _ _ t1 t2) -> typeVars t1 <> typeVars t2+    Scalar (Record fields) -> foldMap typeVars fields+    Scalar (Sum cs) -> mconcat $ (foldMap . fmap) typeVars cs+    Array _ _ rt _ -> typeVars $ Scalar rt+  where+    typeVarFree = S.singleton . typeLeaf+    typeArgFree (TypeArgType ta _) = typeVars ta+    typeArgFree TypeArgDim {} = mempty++-- | @orderZero t@ is 'True' if the argument type has order 0, i.e., it is not+-- a function type, does not contain a function type as a subcomponent, and may+-- not be instantiated with a function type.+orderZero :: TypeBase dim as -> Bool+orderZero Array {} = True+orderZero (Scalar (Prim _)) = True+orderZero (Scalar (Record fs)) = all orderZero $ M.elems fs+orderZero (Scalar TypeVar {}) = True+orderZero (Scalar Arrow {}) = False+orderZero (Scalar (Sum cs)) = all (all orderZero) cs++-- | Extract all the shape names that occur in a given pattern.+patternDimNames :: PatternBase Info VName -> S.Set VName+patternDimNames (TuplePattern ps _) = foldMap patternDimNames ps+patternDimNames (RecordPattern fs _) = foldMap (patternDimNames . snd) fs+patternDimNames (PatternParens p _) = patternDimNames p+patternDimNames (Id _ (Info tp) _) = typeDimNames tp+patternDimNames (Wildcard (Info tp) _) = typeDimNames tp+patternDimNames (PatternAscription p (TypeDecl _ (Info t)) _) =+  patternDimNames p <> typeDimNames t+patternDimNames (PatternLit _ (Info tp) _) = typeDimNames tp+patternDimNames (PatternConstr _ _ ps _) = foldMap patternDimNames ps++-- | Extract all the shape names that occur in a given type.+typeDimNames :: TypeBase (DimDecl VName) als -> S.Set VName+typeDimNames = foldMap dimName . nestedDims+  where+    dimName :: DimDecl VName -> S.Set VName+    dimName (NamedDim qn) = S.singleton $ qualLeaf qn+    dimName _ = mempty++-- | @patternOrderZero pat@ is 'True' if all of the types in the given pattern+-- have order 0.+patternOrderZero :: PatternBase Info vn -> Bool+patternOrderZero pat = case pat of+  TuplePattern ps _ -> all patternOrderZero ps+  RecordPattern fs _ -> all (patternOrderZero . snd) fs+  PatternParens p _ -> patternOrderZero p+  Id _ (Info t) _ -> orderZero t+  Wildcard (Info t) _ -> orderZero t+  PatternAscription p _ _ -> patternOrderZero p+  PatternLit _ (Info t) _ -> orderZero t+  PatternConstr _ _ ps _ -> all patternOrderZero ps++-- | The set of identifiers bound in a pattern.+patternIdents :: (Functor f, Ord vn) => PatternBase f vn -> S.Set (IdentBase f vn)+patternIdents (Id v t loc) = S.singleton $ Ident v t loc+patternIdents (PatternParens p _) = patternIdents p+patternIdents (TuplePattern pats _) = mconcat $ map patternIdents pats+patternIdents (RecordPattern fs _) = mconcat $ map (patternIdents . snd) fs+patternIdents Wildcard {} = mempty+patternIdents (PatternAscription p _ _) = patternIdents p+patternIdents PatternLit {} = mempty+patternIdents (PatternConstr _ _ ps _) = mconcat $ map patternIdents ps++-- | The set of names bound in a pattern.+patternNames :: (Functor f, Ord vn) => PatternBase f vn -> S.Set vn+patternNames (Id v _ _) = S.singleton v+patternNames (PatternParens p _) = patternNames p+patternNames (TuplePattern pats _) = mconcat $ map patternNames pats+patternNames (RecordPattern fs _) = mconcat $ map (patternNames . snd) fs+patternNames Wildcard {} = mempty+patternNames (PatternAscription p _ _) = patternNames p+patternNames PatternLit {} = mempty+patternNames (PatternConstr _ _ ps _) = mconcat $ map patternNames ps++-- | A mapping from names bound in a map to their identifier.+patternMap :: (Functor f) => PatternBase f VName -> M.Map VName (IdentBase f VName)+patternMap pat =+  M.fromList $ zip (map identName idents) idents+  where+    idents = S.toList $ patternIdents pat++-- | The type of values bound by the pattern.+patternType :: PatternBase Info VName -> PatternType+patternType (Wildcard (Info t) _) = t+patternType (PatternParens p _) = patternType p+patternType (Id _ (Info t) _) = t+patternType (TuplePattern pats _) = tupleRecord $ map patternType pats+patternType (RecordPattern fs _) = Scalar $ Record $ patternType <$> M.fromList fs+patternType (PatternAscription p _ _) = patternType p+patternType (PatternLit _ (Info t) _) = t+patternType (PatternConstr _ (Info t) _ _) = t++-- | The type matched by the pattern, including shape declarations if present.+patternStructType :: PatternBase Info VName -> StructType+patternStructType = toStruct . patternType++-- | When viewed as a function parameter, does this pattern correspond+-- to a named parameter of some type?+patternParam :: PatternBase Info VName -> (PName, StructType)+patternParam (PatternParens p _) =+  patternParam p+patternParam (PatternAscription (Id v _ _) td _) =+  (Named v, unInfo $ expandedType td)+patternParam (Id v (Info t) _) =+  (Named v, toStruct t)+patternParam p =+  (Unnamed, patternStructType p)++-- | Names of primitive types to types.  This is only valid if no+-- shadowing is going on, but useful for tools.+namesToPrimTypes :: M.Map Name PrimType+namesToPrimTypes =+  M.fromList+    [ (nameFromString $ pretty t, t)+      | t <-+          Bool :+          map Signed [minBound .. maxBound]+            ++ map Unsigned [minBound .. maxBound]+            ++ map FloatType [minBound .. maxBound]+    ]++-- | The nature of something predefined.  These can either be+-- monomorphic or overloaded.  An overloaded builtin is a list valid+-- types it can be instantiated with, to the parameter and result+-- type, with 'Nothing' representing the overloaded parameter type.+data Intrinsic+  = IntrinsicMonoFun [PrimType] PrimType+  | IntrinsicOverloadedFun [PrimType] [Maybe PrimType] (Maybe PrimType)+  | IntrinsicPolyFun [TypeParamBase VName] [StructType] StructType+  | IntrinsicType PrimType+  | IntrinsicEquality -- Special cased.++-- | A map of all built-ins.+intrinsics :: M.Map VName Intrinsic+intrinsics =+  M.fromList $+    zipWith namify [10 ..] $+      map primFun (M.toList Primitive.primFuns)+        ++ [("opaque", IntrinsicPolyFun [tp_a] [Scalar t_a] $ Scalar t_a)]+        ++ map unOpFun Primitive.allUnOps+        ++ map binOpFun Primitive.allBinOps+        ++ map cmpOpFun Primitive.allCmpOps+        ++ map convOpFun Primitive.allConvOps+        ++ map signFun Primitive.allIntTypes+        ++ map unsignFun Primitive.allIntTypes+        ++ map+          intrinsicType+          ( map Signed [minBound .. maxBound]+              ++ map Unsigned [minBound .. maxBound]+              ++ map FloatType [minBound .. maxBound]+              ++ [Bool]+          )+        +++        -- This overrides the ! from Primitive.+        [ ( "!",+            IntrinsicOverloadedFun+              ( map Signed [minBound .. maxBound]+                  ++ map Unsigned [minBound .. maxBound]+                  ++ [Bool]+              )+              [Nothing]+              Nothing+          )+        ]+        +++        -- The reason for the loop formulation is to ensure that we+        -- get a missing case warning if we forget a case.+        mapMaybe mkIntrinsicBinOp [minBound .. maxBound]+        ++ [ ( "flatten",+               IntrinsicPolyFun+                 [tp_a]+                 [Array () Nonunique t_a (rank 2)]+                 $ Array () Nonunique t_a (rank 1)+             ),+             ( "unflatten",+               IntrinsicPolyFun+                 [tp_a]+                 [ Scalar $ Prim $ Signed Int64,+                   Scalar $ Prim $ Signed Int64,+                   Array () Nonunique t_a (rank 1)+                 ]+                 $ Array () Nonunique t_a (rank 2)+             ),+             ( "concat",+               IntrinsicPolyFun+                 [tp_a]+                 [arr_a, arr_a]+                 uarr_a+             ),+             ( "rotate",+               IntrinsicPolyFun+                 [tp_a]+                 [Scalar $ Prim $ Signed Int64, arr_a]+                 arr_a+             ),+             ("transpose", IntrinsicPolyFun [tp_a] [arr_2d_a] arr_2d_a),+             ( "scatter",+               IntrinsicPolyFun+                 [tp_a]+                 [ Array () Unique t_a (rank 1),+                   Array () Nonunique (Prim $ Signed Int64) (rank 1),+                   Array () Nonunique t_a (rank 1)+                 ]+                 $ Array () Unique t_a (rank 1)+             ),+             ("zip", IntrinsicPolyFun [tp_a, tp_b] [arr_a, arr_b] arr_a_b),+             ("unzip", IntrinsicPolyFun [tp_a, tp_b] [arr_a_b] t_arr_a_arr_b),+             ( "hist",+               IntrinsicPolyFun+                 [tp_a]+                 [ Scalar $ Prim $ Signed Int64,+                   uarr_a,+                   Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a),+                   Scalar t_a,+                   Array () Nonunique (Prim $ Signed Int64) (rank 1),+                   arr_a+                 ]+                 uarr_a+             ),+             ("map", IntrinsicPolyFun [tp_a, tp_b] [Scalar t_a `arr` Scalar t_b, arr_a] uarr_b),+             ( "reduce",+               IntrinsicPolyFun+                 [tp_a]+                 [Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a), Scalar t_a, arr_a]+                 $ Scalar t_a+             ),+             ( "reduce_comm",+               IntrinsicPolyFun+                 [tp_a]+                 [Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a), Scalar t_a, arr_a]+                 $ Scalar t_a+             ),+             ( "scan",+               IntrinsicPolyFun+                 [tp_a]+                 [Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a), Scalar t_a, arr_a]+                 uarr_a+             ),+             ( "partition",+               IntrinsicPolyFun+                 [tp_a]+                 [ Scalar (Prim $ Signed Int32),+                   Scalar t_a `arr` Scalar (Prim $ Signed Int64),+                   arr_a+                 ]+                 $ tupleRecord [uarr_a, Array () Unique (Prim $ Signed Int64) (rank 1)]+             ),+             ( "map_stream",+               IntrinsicPolyFun+                 [tp_a, tp_b]+                 [Scalar (Prim $ Signed Int64) `karr` (arr_ka `arr` arr_kb), arr_a]+                 uarr_b+             ),+             ( "map_stream_per",+               IntrinsicPolyFun+                 [tp_a, tp_b]+                 [Scalar (Prim $ Signed Int64) `karr` (arr_ka `arr` arr_kb), arr_a]+                 uarr_b+             ),+             ( "reduce_stream",+               IntrinsicPolyFun+                 [tp_a, tp_b]+                 [ Scalar t_b `arr` (Scalar t_b `arr` Scalar t_b),+                   Scalar (Prim $ Signed Int64) `karr` (arr_ka `arr` Scalar t_b),+                   arr_a+                 ]+                 $ Scalar t_b+             ),+             ( "reduce_stream_per",+               IntrinsicPolyFun+                 [tp_a, tp_b]+                 [ Scalar t_b `arr` (Scalar t_b `arr` Scalar t_b),+                   Scalar (Prim $ Signed Int64) `karr` (arr_ka `arr` Scalar t_b),+                   arr_a+                 ]+                 $ Scalar t_b+             ),+             ("trace", IntrinsicPolyFun [tp_a] [Scalar t_a] $ Scalar t_a),+             ("break", IntrinsicPolyFun [tp_a] [Scalar t_a] $ Scalar t_a)+           ]+  where+    tv_a = VName (nameFromString "a") 0+    t_a = TypeVar () Nonunique (typeName tv_a) []+    arr_a = Array () Nonunique t_a (rank 1)+    arr_2d_a = Array () Nonunique t_a (rank 2)+    uarr_a = Array () Unique t_a (rank 1)+    tp_a = TypeParamType Unlifted tv_a mempty++    tv_b = VName (nameFromString "b") 1+    t_b = TypeVar () Nonunique (typeName tv_b) []+    arr_b = Array () Nonunique t_b (rank 1)+    uarr_b = Array () Unique t_b (rank 1)+    tp_b = TypeParamType Unlifted tv_b mempty++    arr_a_b =+      Array+        ()+        Nonunique+        (Record (M.fromList $ zip tupleFieldNames [Scalar t_a, Scalar t_b]))+        (rank 1)+    t_arr_a_arr_b = Scalar $ Record $ M.fromList $ zip tupleFieldNames [arr_a, arr_b]++    arr x y = Scalar $ Arrow mempty Unnamed x y++    kv = VName (nameFromString "k") 2+    arr_ka = Array () Nonunique t_a (ShapeDecl [NamedDim $ qualName kv])+    arr_kb = Array () Nonunique t_b (ShapeDecl [NamedDim $ qualName kv])+    karr x y = Scalar $ Arrow mempty (Named kv) x y++    namify i (k, v) = (VName (nameFromString k) i, v)++    primFun (name, (ts, t, _)) =+      (name, IntrinsicMonoFun (map unPrim ts) $ unPrim t)++    unOpFun bop = (pretty bop, IntrinsicMonoFun [t] t)+      where+        t = unPrim $ Primitive.unOpType bop++    binOpFun bop = (pretty bop, IntrinsicMonoFun [t, t] t)+      where+        t = unPrim $ Primitive.binOpType bop++    cmpOpFun bop = (pretty bop, IntrinsicMonoFun [t, t] Bool)+      where+        t = unPrim $ Primitive.cmpOpType bop++    convOpFun cop = (pretty cop, IntrinsicMonoFun [unPrim ft] $ unPrim tt)+      where+        (ft, tt) = Primitive.convOpType cop++    signFun t = ("sign_" ++ pretty t, IntrinsicMonoFun [Unsigned t] $ Signed t)++    unsignFun t = ("unsign_" ++ pretty t, IntrinsicMonoFun [Signed t] $ Unsigned t)++    unPrim (Primitive.IntType t) = Signed t+    unPrim (Primitive.FloatType t) = FloatType t+    unPrim Primitive.Bool = Bool+    unPrim Primitive.Cert = Bool++    intrinsicType t = (pretty t, IntrinsicType t)++    anyIntType =+      map Signed [minBound .. maxBound]+        ++ map Unsigned [minBound .. maxBound]+    anyNumberType =+      anyIntType+        ++ map FloatType [minBound .. maxBound]+    anyPrimType = Bool : anyNumberType++    mkIntrinsicBinOp :: BinOp -> Maybe (String, Intrinsic)+    mkIntrinsicBinOp op = do+      op' <- intrinsicBinOp op+      return (pretty op, op')++    binOp ts = Just $ IntrinsicOverloadedFun ts [Nothing, Nothing] Nothing+    ordering = Just $ IntrinsicOverloadedFun anyPrimType [Nothing, Nothing] (Just Bool)++    intrinsicBinOp Plus = binOp anyNumberType+    intrinsicBinOp Minus = binOp anyNumberType+    intrinsicBinOp Pow = binOp anyNumberType+    intrinsicBinOp Times = binOp anyNumberType+    intrinsicBinOp Divide = binOp anyNumberType+    intrinsicBinOp Mod = binOp anyNumberType+    intrinsicBinOp Quot = binOp anyIntType+    intrinsicBinOp Rem = binOp anyIntType+    intrinsicBinOp ShiftR = binOp anyIntType+    intrinsicBinOp ShiftL = binOp anyIntType+    intrinsicBinOp Band = binOp anyIntType+    intrinsicBinOp Xor = binOp anyIntType+    intrinsicBinOp Bor = binOp anyIntType+    intrinsicBinOp LogAnd = binOp [Bool]+    intrinsicBinOp LogOr = binOp [Bool]+    intrinsicBinOp Equal = Just IntrinsicEquality+    intrinsicBinOp NotEqual = Just IntrinsicEquality+    intrinsicBinOp Less = ordering+    intrinsicBinOp Leq = ordering+    intrinsicBinOp Greater = ordering+    intrinsicBinOp Geq = ordering+    intrinsicBinOp _ = Nothing++-- | The largest tag used by an intrinsic - this can be used to+-- determine whether a 'VName' refers to an intrinsic or a user-defined name.+maxIntrinsicTag :: Int+maxIntrinsicTag = maxinum $ map baseTag $ M.keys intrinsics++-- | Create a name with no qualifiers from a name.+qualName :: v -> QualName v+qualName = QualName []++-- | Add another qualifier (at the head) to a qualified name.+qualify :: v -> QualName v -> QualName v+qualify k (QualName ks v) = QualName (k : ks) v++-- | Create a type name name with no qualifiers from a 'VName'.+typeName :: VName -> TypeName+typeName = typeNameFromQualName . qualName++-- | The modules imported by a Futhark program.+progImports :: ProgBase f vn -> [(String, SrcLoc)]+progImports = concatMap decImports . progDecs++-- | The modules imported by a single declaration.+decImports :: DecBase f vn -> [(String, SrcLoc)]+decImports (OpenDec x _) = modExpImports x+decImports (ModDec md) = modExpImports $ modExp md+decImports SigDec {} = []+decImports TypeDec {} = []+decImports ValDec {} = []+decImports (LocalDec d _) = decImports d+decImports (ImportDec x _ loc) = [(x, loc)]++modExpImports :: ModExpBase f vn -> [(String, SrcLoc)]+modExpImports ModVar {} = []+modExpImports (ModParens p _) = modExpImports p+modExpImports (ModImport f _ loc) = [(f, loc)]+modExpImports (ModDecs ds _) = concatMap decImports ds+modExpImports (ModApply _ me _ _ _) = modExpImports me+modExpImports (ModAscript me _ _ _) = modExpImports me+modExpImports ModLambda {} = []++-- | The set of module types used in any exported (non-local)+-- declaration.+progModuleTypes :: Ord vn => ProgBase f vn -> S.Set vn+progModuleTypes = mconcat . map onDec . progDecs+  where+    onDec (OpenDec x _) = onModExp x+    onDec (ModDec md) =+      maybe mempty (onSigExp . fst) (modSignature md) <> onModExp (modExp md)+    onDec SigDec {} = mempty+    onDec TypeDec {} = mempty+    onDec ValDec {} = mempty+    onDec LocalDec {} = mempty+    onDec ImportDec {} = mempty++    onModExp ModVar {} = mempty+    onModExp (ModParens p _) = onModExp p+    onModExp ModImport {} = mempty+    onModExp (ModDecs ds _) = mconcat $ map onDec ds+    onModExp (ModApply me1 me2 _ _ _) = onModExp me1 <> onModExp me2+    onModExp (ModAscript me se _ _) = onModExp me <> onSigExp se+    onModExp (ModLambda p r me _) =+      onModParam p <> maybe mempty (onSigExp . fst) r <> onModExp me++    onModParam = onSigExp . modParamType++    onSigExp (SigVar v _ _) = S.singleton $ qualLeaf v+    onSigExp (SigParens e _) = onSigExp e+    onSigExp SigSpecs {} = mempty+    onSigExp (SigWith e _ _) = onSigExp e+    onSigExp (SigArrow _ e1 e2 _) = onSigExp e1 <> onSigExp e2++-- | Extract a leading @((name, namespace, file), remainder)@ from a+-- documentation comment string.  These are formatted as+-- \`name\`\@namespace[\@file].  Let us hope that this pattern does not occur+-- anywhere else.+identifierReference :: String -> Maybe ((String, String, Maybe FilePath), String)+identifierReference ('`' : s)+  | (identifier, '`' : '@' : s') <- break (== '`') s,+    (namespace, s'') <- span isAlpha s',+    not $ null namespace =+    case s'' of+      '@' : '"' : s'''+        | (file, '"' : s'''') <- span (/= '"') s''' ->+          Just ((identifier, namespace, Just file), s'''')+      _ -> Just ((identifier, namespace, Nothing), s'')+identifierReference _ = Nothing++-- | Given an operator name, return the operator that determines its+-- syntactical properties.+leadingOperator :: Name -> BinOp+leadingOperator s =+  maybe Backtick snd $+    find ((`isPrefixOf` s') . fst) $+      sortOn (Down . length . fst) $+        zip (map pretty operators) operators+  where+    s' = nameToString s+    operators :: [BinOp]+    operators = [minBound .. maxBound :: BinOp]  -- | A type with no aliasing information but shape annotations. type UncheckedType = TypeBase (ShapeDecl Name) ()
src/Language/Futhark/Query.hs view
@@ -1,33 +1,34 @@ {-# LANGUAGE FlexibleContexts #-}+ -- | Facilities for answering queries about a program, such as "what -- appears at this source location", or "where is this name bound". -- The intent is that this is used as a building block for IDE-like -- functionality. module Language.Futhark.Query-  ( BoundTo(..)-  , boundLoc-  , AtPos(..)-  , atPos-  , Pos(..)+  ( BoundTo (..),+    boundLoc,+    AtPos (..),+    atPos,+    Pos (..),   )-  where+where  import Control.Monad import Control.Monad.State import Data.List (find) import qualified Data.Map as M-import qualified System.FilePath.Posix as Posix--import Futhark.Util.Loc (Pos(..), Loc(..))+import Futhark.Util.Loc (Loc (..), Pos (..)) import Language.Futhark import Language.Futhark.Semantic import Language.Futhark.Traversals+import qualified System.FilePath.Posix as Posix  -- | What a name is bound to.-data BoundTo = BoundTerm StructType Loc-             | BoundModule Loc-             | BoundModuleType Loc-             | BoundType Loc+data BoundTo+  = BoundTerm StructType Loc+  | BoundModule Loc+  | BoundModuleType Loc+  | BoundType Loc   deriving (Eq, Show)  data Def = DefBound BoundTo | DefIndirect VName@@ -52,8 +53,8 @@   mconcat $ map (patternDefs . snd) fields patternDefs (PatternParens pat _) =   patternDefs pat-patternDefs Wildcard{} = mempty-patternDefs PatternLit{} = mempty+patternDefs Wildcard {} = mempty+patternDefs PatternLit {} = mempty patternDefs (PatternAscription pat _ _) =   patternDefs pat patternDefs (PatternConstr _ _ pats _) =@@ -68,50 +69,54 @@ expDefs :: Exp -> Defs expDefs e =   execState (astMap mapper e) extra-  where mapper = ASTMapper { mapOnExp = onExp-                           , mapOnName = pure-                           , mapOnQualName = pure-                           , mapOnStructType = pure-                           , mapOnPatternType = pure-                           }-        onExp e' = do-          modify (<>expDefs e')-          return e'+  where+    mapper =+      ASTMapper+        { mapOnExp = onExp,+          mapOnName = pure,+          mapOnQualName = pure,+          mapOnStructType = pure,+          mapOnPatternType = pure+        }+    onExp e' = do+      modify (<> expDefs e')+      return e' -        identDefs (Ident v (Info vt) vloc) =-          M.singleton v $ DefBound $ BoundTerm (toStruct vt) $ locOf vloc+    identDefs (Ident v (Info vt) vloc) =+      M.singleton v $ DefBound $ BoundTerm (toStruct vt) $ locOf vloc -        extra =-          case e of-            LetPat pat _ _ _ _ ->-              patternDefs pat-            Lambda params _ _ _ _ ->-              mconcat (map patternDefs params)-            LetFun name (tparams, params, _, Info ret, _) _ _ loc ->-              let name_t = foldFunType (map patternStructType params) ret-              in M.singleton name (DefBound $ BoundTerm name_t (locOf loc)) <>-                 mconcat (map typeParamDefs tparams) <>-                 mconcat (map patternDefs params)-            LetWith v _ _ _ _ _ _ ->-              identDefs v-            DoLoop _ merge _ form _ _ _ ->-              patternDefs merge <>-              case form of-                For i _ -> identDefs i-                ForIn pat _ -> patternDefs pat-                While{} -> mempty-            _ ->-              mempty+    extra =+      case e of+        LetPat pat _ _ _ _ ->+          patternDefs pat+        Lambda params _ _ _ _ ->+          mconcat (map patternDefs params)+        LetFun name (tparams, params, _, Info ret, _) _ _ loc ->+          let name_t = foldFunType (map patternStructType params) ret+           in M.singleton name (DefBound $ BoundTerm name_t (locOf loc))+                <> mconcat (map typeParamDefs tparams)+                <> mconcat (map patternDefs params)+        LetWith v _ _ _ _ _ _ ->+          identDefs v+        DoLoop _ merge _ form _ _ _ ->+          patternDefs merge+            <> case form of+              For i _ -> identDefs i+              ForIn pat _ -> patternDefs pat+              While {} -> mempty+        _ ->+          mempty  valBindDefs :: ValBind -> Defs valBindDefs vbind =   M.insert (valBindName vbind) (DefBound $ BoundTerm vbind_t (locOf vbind)) $-  mconcat (map typeParamDefs (valBindTypeParams vbind)) <>-  mconcat (map patternDefs (valBindParams vbind)) <>-  expDefs (valBindBody vbind)-  where vbind_t =-          foldFunType (map patternStructType (valBindParams vbind)) $-          fst $ unInfo $ valBindRetType vbind+    mconcat (map typeParamDefs (valBindTypeParams vbind))+      <> mconcat (map patternDefs (valBindParams vbind))+      <> expDefs (valBindBody vbind)+  where+    vbind_t =+      foldFunType (map patternStructType (valBindParams vbind)) $+        fst $ unInfo $ valBindRetType vbind  typeBindDefs :: TypeBind -> Defs typeBindDefs tbind =@@ -119,15 +124,15 @@  modParamDefs :: ModParam -> Defs modParamDefs (ModParam p se _ loc) =-  M.singleton p (DefBound $ BoundModule $ locOf loc) <>-  sigExpDefs se+  M.singleton p (DefBound $ BoundModule $ locOf loc)+    <> sigExpDefs se  modExpDefs :: ModExp -> Defs-modExpDefs ModVar{} =+modExpDefs ModVar {} =   mempty modExpDefs (ModParens me _) =   modExpDefs me-modExpDefs ModImport{} =+modExpDefs ModImport {} =   mempty modExpDefs (ModDecs decs _) =   mconcat $ map decDefs decs@@ -140,26 +145,26 @@  modBindDefs :: ModBind -> Defs modBindDefs mbind =-  M.singleton (modName mbind) (DefBound $ BoundModule $ locOf mbind) <>-  mconcat (map modParamDefs (modParams mbind)) <>-  modExpDefs (modExp mbind) <>-  case modSignature mbind of-    Nothing -> mempty-    Just (_, Info substs) ->-      M.map DefIndirect substs+  M.singleton (modName mbind) (DefBound $ BoundModule $ locOf mbind)+    <> mconcat (map modParamDefs (modParams mbind))+    <> modExpDefs (modExp mbind)+    <> case modSignature mbind of+      Nothing -> mempty+      Just (_, Info substs) ->+        M.map DefIndirect substs  specDefs :: Spec -> Defs specDefs spec =   case spec of     ValSpec v tparams tdecl _ loc ->       let vdef = DefBound $ BoundTerm (unInfo $ expandedType tdecl) (locOf loc)-      in M.insert v vdef $ mconcat (map typeParamDefs tparams)+       in M.insert v vdef $ mconcat (map typeParamDefs tparams)     TypeAbbrSpec tbind -> typeBindDefs tbind     TypeSpec _ v _ _ loc ->       M.singleton v $ DefBound $ BoundType $ locOf loc     ModSpec v se _ loc ->-      M.singleton v (DefBound $ BoundModuleType $ locOf loc) <>-      sigExpDefs se+      M.singleton v (DefBound $ BoundModuleType $ locOf loc)+        <> sigExpDefs se     IncludeSpec se _ -> sigExpDefs se  sigExpDefs :: SigExp -> Defs@@ -173,8 +178,8 @@  sigBindDefs :: SigBind -> Defs sigBindDefs sbind =-  M.singleton (sigName sbind) (DefBound $ BoundModuleType $ locOf sbind) <>-  sigExpDefs (sigExp sbind)+  M.singleton (sigName sbind) (DefBound $ BoundModuleType $ locOf sbind)+    <> sigExpDefs (sigExp sbind)  decDefs :: Dec -> Defs decDefs (ValDec vbind) = valBindDefs vbind@@ -183,7 +188,7 @@ decDefs (SigDec mbind) = sigBindDefs mbind decDefs (OpenDec me _) = modExpDefs me decDefs (LocalDec dec _) = decDefs dec-decDefs ImportDec{} = mempty+decDefs ImportDec {} = mempty  -- | All bindings of everything in the program. progDefs :: Prog -> Defs@@ -191,9 +196,10 @@  allBindings :: Imports -> M.Map VName BoundTo allBindings imports = M.mapMaybe forward defs-  where defs = mconcat $ map (progDefs . fileProg . snd) imports-        forward (DefBound x) = Just x-        forward (DefIndirect v) = forward =<< M.lookup v defs+  where+    defs = mconcat $ map (progDefs . fileProg . snd) imports+    forward (DefBound x) = Just x+    forward (DefIndirect v) = forward =<< M.lookup v defs  data RawAtPos = RawAtName (QualName VName) Loc @@ -223,12 +229,13 @@       atPosInTypeExp e1 pos `mplus` atPosInTypeExp e2 pos     TESum cs _ ->       msum $ map (`atPosInTypeExp` pos) $ concatMap snd cs-  where inArg (TypeArgExpDim dim _) = inDim dim-        inArg (TypeArgExpType e2) = atPosInTypeExp e2 pos-        inDim (DimExpNamed qn loc) = do-          guard $ loc `contains` pos-          Just $ RawAtName qn $ locOf loc-        inDim _ = Nothing+  where+    inArg (TypeArgExpDim dim _) = inDim dim+    inArg (TypeArgExpType e2) = atPosInTypeExp e2 pos+    inDim (DimExpNamed qn loc) = do+      guard $ loc `contains` pos+      Just $ RawAtName qn $ locOf loc+    inDim _ = Nothing  atPosInPattern :: Pattern -> Pos -> Maybe RawAtPos atPosInPattern (Id vn _ loc) pos = do@@ -244,8 +251,8 @@   atPosInPattern pat pos `mplus` atPosInTypeExp (declaredType tdecl) pos atPosInPattern (PatternConstr _ _ pats _) pos =   msum $ map (`atPosInPattern` pos) pats-atPosInPattern PatternLit{} _ = Nothing-atPosInPattern Wildcard{} _ = Nothing+atPosInPattern PatternLit {} _ = Nothing+atPosInPattern Wildcard {} _ = Nothing  atPosInExp :: Exp -> Pos -> Maybe RawAtPos atPosInExp (Var qn _ loc) pos = do@@ -253,28 +260,21 @@   Just $ RawAtName qn $ locOf loc atPosInExp (QualParens (qn, loc) _ _) pos   | loc `contains` pos = Just $ RawAtName qn $ locOf loc- -- All the value cases are TODO - we need another RawAtPos constructor.-atPosInExp Literal{} _ = Nothing-atPosInExp IntLit{} _ = Nothing-atPosInExp FloatLit{} _ = Nothing-+atPosInExp Literal {} _ = Nothing+atPosInExp IntLit {} _ = Nothing+atPosInExp FloatLit {} _ = Nothing atPosInExp (LetPat pat _ _ _ _) pos   | pat `contains` pos = atPosInPattern pat pos- atPosInExp (LetWith a b _ _ _ _ _) pos   | a `contains` pos = Just $ RawAtName (qualName $ identName a) (locOf a)   | b `contains` pos = Just $ RawAtName (qualName $ identName b) (locOf b)- atPosInExp (DoLoop _ merge _ _ _ _ _) pos   | merge `contains` pos = atPosInPattern merge pos- atPosInExp (Ascript _ tdecl _) pos   | tdecl `contains` pos = atPosInTypeExp (declaredType tdecl) pos- atPosInExp (Coerce _ tdecl _ _) pos   | tdecl `contains` pos = atPosInTypeExp (declaredType tdecl) pos- atPosInExp e pos = do   guard $ e `contains` pos   -- Use the Either monad for short-circuiting for efficiency reasons.@@ -282,16 +282,19 @@   case astMap mapper e of     Left atpos -> Just atpos     Right _ -> Nothing-  where mapper = ASTMapper { mapOnExp = onExp-                           , mapOnName = pure-                           , mapOnQualName = pure-                           , mapOnStructType = pure-                           , mapOnPatternType = pure-                           }-        onExp e' =-          case atPosInExp e' pos of-            Just atpos -> Left atpos-            Nothing -> Right e'+  where+    mapper =+      ASTMapper+        { mapOnExp = onExp,+          mapOnName = pure,+          mapOnQualName = pure,+          mapOnStructType = pure,+          mapOnPatternType = pure+        }+    onExp e' =+      case atPosInExp e' pos of+        Just atpos -> Left atpos+        Nothing -> Right e'  atPosInModExp :: ModExp -> Pos -> Maybe RawAtPos atPosInModExp (ModVar qn loc) pos = do@@ -299,7 +302,7 @@   Just $ RawAtName qn $ locOf loc atPosInModExp (ModParens me _) pos =   atPosInModExp me pos-atPosInModExp ModImport{} _ =+atPosInModExp ModImport {} _ =   Nothing atPosInModExp (ModDecs decs _) pos =   msum $ map (`atPosInDec` pos) decs@@ -315,15 +318,16 @@   case spec of     ValSpec _ _ tdecl _ _ -> atPosInTypeExp (declaredType tdecl) pos     TypeAbbrSpec tbind -> atPosInTypeBind tbind pos-    TypeSpec{} -> Nothing+    TypeSpec {} -> Nothing     ModSpec _ se _ _ -> atPosInSigExp se pos     IncludeSpec se _ -> atPosInSigExp se pos  atPosInSigExp :: SigExp -> Pos -> Maybe RawAtPos atPosInSigExp se pos =   case se of-    SigVar qn _ loc -> do guard $ loc `contains` pos-                          Just $ RawAtName qn $ locOf loc+    SigVar qn _ loc -> do+      guard $ loc `contains` pos+      Just $ RawAtName qn $ locOf loc     SigParens e _ -> atPosInSigExp e pos     SigSpecs specs _ -> msum $ map (`atPosInSpec` pos) specs     SigWith e _ _ -> atPosInSigExp e pos@@ -331,20 +335,22 @@  atPosInValBind :: ValBind -> Pos -> Maybe RawAtPos atPosInValBind vbind pos =-  msum (map (`atPosInPattern` pos) (valBindParams vbind)) `mplus`-  atPosInExp (valBindBody vbind) pos `mplus`-  join (atPosInTypeExp <$> valBindRetDecl vbind <*> pure pos)+  msum (map (`atPosInPattern` pos) (valBindParams vbind))+    `mplus` atPosInExp (valBindBody vbind) pos+    `mplus` join (atPosInTypeExp <$> valBindRetDecl vbind <*> pure pos)  atPosInTypeBind :: TypeBind -> Pos -> Maybe RawAtPos atPosInTypeBind = atPosInTypeExp . declaredType . typeExp  atPosInModBind :: ModBind -> Pos -> Maybe RawAtPos atPosInModBind (ModBind _ params sig e _ _) pos =-  msum (map inParam params) `mplus`-  atPosInModExp e pos `mplus`-  case sig of Nothing -> Nothing-              Just (se, _) -> atPosInSigExp se pos-  where inParam (ModParam _ se _ _) = atPosInSigExp se pos+  msum (map inParam params)+    `mplus` atPosInModExp e pos+    `mplus` case sig of+      Nothing -> Nothing+      Just (se, _) -> atPosInSigExp se pos+  where+    inParam (ModParam _ se _ _) = atPosInSigExp se pos  atPosInSigBind :: SigBind -> Pos -> Maybe RawAtPos atPosInSigBind = atPosInSigExp . sigExp@@ -359,7 +365,7 @@     SigDec sbind -> atPosInSigBind sbind pos     OpenDec e _ -> atPosInModExp e pos     LocalDec dec' _ -> atPosInDec dec' pos-    ImportDec{} -> Nothing+    ImportDec {} -> Nothing  atPosInProg :: Prog -> Pos -> Maybe RawAtPos atPosInProg prog pos =@@ -367,9 +373,12 @@  containingModule :: Imports -> Pos -> Maybe FileModule containingModule imports (Pos file _ _ _) =-  snd <$> find ((==file') . fst) imports-  where file' = includeToString $ mkInitialImport $-                fst $ Posix.splitExtension file+  snd <$> find ((== file') . fst) imports+  where+    file' =+      includeToString $+        mkInitialImport $+          fst $ Posix.splitExtension file  -- | Information about what is at the given source location. data AtPos = AtName (QualName VName) (Maybe BoundTo) Loc
src/Language/Futhark/Semantic.hs view
@@ -1,39 +1,36 @@ {-# LANGUAGE Safe #-}+ -- | Definitions of various semantic objects (*not* the Futhark -- semantics themselves). module Language.Futhark.Semantic-  ( ImportName-  , mkInitialImport-  , mkImportFrom-  , includeToFilePath-  , includeToString--  , FileModule(..)-  , Imports--  , Namespace(..)-  , Env(..)-  , TySet-  , FunSig(..)-  , NameMap-  , BoundV(..)-  , Mod(..)-  , TypeBinding(..)-  , MTy(..)+  ( ImportName,+    mkInitialImport,+    mkImportFrom,+    includeToFilePath,+    includeToString,+    FileModule (..),+    Imports,+    Namespace (..),+    Env (..),+    TySet,+    FunSig (..),+    NameMap,+    BoundV (..),+    Mod (..),+    TypeBinding (..),+    MTy (..),   ) where  import qualified Data.Map.Strict as M-import qualified System.FilePath.Posix as Posix+import Futhark.Util (dropLast, fromPOSIX, toPOSIX)+import Futhark.Util.Loc+import Futhark.Util.Pretty+import Language.Futhark import qualified System.FilePath as Native-+import qualified System.FilePath.Posix as Posix import Prelude hiding (mod) -import Language.Futhark-import Futhark.Util (dropLast, toPOSIX, fromPOSIX)-import Futhark.Util.Pretty-import Futhark.Util.Loc- -- | Canonical reference to a Futhark code file.  Does not include the -- @.fut@ extension.  This is most often a path relative to the -- current working directory of the compiler.@@ -55,13 +52,14 @@ mkImportFrom (ImportName includer _) includee   | Posix.isAbsolute includee = ImportName includee   | otherwise = ImportName $ Posix.normalise $ Posix.joinPath $ includer' ++ includee'-  where (dotdots, includee') = span ("../"==) $ Posix.splitPath includee-        includer_parts = init $ Posix.splitPath includer-        includer'-          | length dotdots > length includer_parts =-              replicate (length dotdots - length includer_parts) "../"-          | otherwise =-              dropLast (length dotdots) includer_parts+  where+    (dotdots, includee') = span ("../" ==) $ Posix.splitPath includee+    includer_parts = init $ Posix.splitPath includer+    includer'+      | length dotdots > length includer_parts =+        replicate (length dotdots - length includer_parts) "../"+      | otherwise =+        dropLast (length dotdots) includer_parts  -- | Create a @.fut@ file corresponding to an 'ImportName'. includeToFilePath :: ImportName -> Native.FilePath@@ -74,70 +72,79 @@  -- | The result of type checking some file.  Can be passed to further -- invocations of the type checker.-data FileModule = FileModule { fileAbs :: TySet -- ^ Abstract types.-                             , fileEnv :: Env-                             , fileProg :: Prog-                             }+data FileModule = FileModule+  { -- | Abstract types.+    fileAbs :: TySet,+    fileEnv :: Env,+    fileProg :: Prog+  }  -- | A mapping from import names to imports.  The ordering is significant. type Imports = [(String, FileModule)]  -- | The space inhabited by a name.-data Namespace = Term -- ^ Functions and values.-               | Type-               | Signature-               deriving (Eq, Ord, Show, Enum)+data Namespace+  = -- | Functions and values.+    Term+  | Type+  | Signature+  deriving (Eq, Ord, Show, Enum)  -- | A mapping of abstract types to their liftedness. type TySet = M.Map (QualName VName) Liftedness  -- | Representation of a module, which is either a plain environment, -- or a parametric module ("functor" in SML).-data Mod = ModEnv Env-         | ModFun FunSig-         deriving (Show)+data Mod+  = ModEnv Env+  | ModFun FunSig+  deriving (Show)  -- | A parametric functor consists of a set of abstract types, the -- environment of its parameter, and the resulting module type.-data FunSig = FunSig { funSigAbs :: TySet-                     , funSigMod :: Mod-                     , funSigMty :: MTy-                     }-            deriving (Show)+data FunSig = FunSig+  { funSigAbs :: TySet,+    funSigMod :: Mod,+    funSigMty :: MTy+  }+  deriving (Show)  -- | Representation of a module type.-data MTy = MTy { mtyAbs :: TySet-                 -- ^ Abstract types in the module type.-               , mtyMod :: Mod-               }-         deriving (Show)+data MTy = MTy+  { -- | Abstract types in the module type.+    mtyAbs :: TySet,+    mtyMod :: Mod+  }+  deriving (Show)  -- | A binding from a name to its definition as a type. data TypeBinding = TypeAbbr Liftedness [TypeParam] StructType-                 deriving (Eq, Show)+  deriving (Eq, Show)  -- | Type parameters, list of parameter types (optinally named), and -- return type.  The type parameters are in scope in both parameter -- types and the return type.  Non-functional values have only a -- return type. data BoundV = BoundV [TypeParam] StructType-                deriving (Show)+  deriving (Show)  -- | A mapping from names (which always exist in some namespace) to a -- unique (tagged) name. type NameMap = M.Map (Namespace, Name) (QualName VName)  -- | Modules produces environment with this representation.-data Env = Env { envVtable :: M.Map VName BoundV-               , envTypeTable :: M.Map VName TypeBinding-               , envSigTable :: M.Map VName MTy-               , envModTable :: M.Map VName Mod-               , envNameMap :: NameMap-               } deriving (Show)+data Env = Env+  { envVtable :: M.Map VName BoundV,+    envTypeTable :: M.Map VName TypeBinding,+    envSigTable :: M.Map VName MTy,+    envModTable :: M.Map VName Mod,+    envNameMap :: NameMap+  }+  deriving (Show)  instance Semigroup Env where   Env vt1 tt1 st1 mt1 nt1 <> Env vt2 tt2 st2 mt2 nt2 =-    Env (vt1<>vt2) (tt1<>tt2) (st1<>st2) (mt1<>mt2) (nt1<>nt2)+    Env (vt1 <> vt2) (tt1 <> tt2) (st1 <> st2) (mt1 <> mt2) (nt1 <> nt2)  instance Pretty Namespace where   ppr Term = text "name"@@ -156,21 +163,27 @@  instance Pretty Env where   ppr (Env vtable ttable sigtable modtable _) =-    nestedBlock "{" "}" $ stack $ punctuate line $ concat-    [map renderTypeBind (M.toList ttable),-     map renderValBind (M.toList vtable),-     map renderModType (M.toList sigtable),-     map renderMod (M.toList modtable)]-    where renderTypeBind (name, TypeAbbr l tps tp) =-            p l <+> pprName name <> mconcat (map ((text " "<>) . ppr) tps) <>-            text " =" <+> ppr tp-            where p Lifted = text "type^"-                  p SizeLifted = text "type~"-                  p Unlifted = text "type"-          renderValBind (name, BoundV tps t) =-            text "val" <+> pprName name <> mconcat (map ((text " "<>) . ppr) tps) <>-            text " =" <+> ppr t-          renderModType (name, _sig) =-            text "module type" <+> pprName name-          renderMod (name, mod) =-            text "module" <+> pprName name <> text " =" <+> ppr mod+    nestedBlock "{" "}" $+      stack $+        punctuate line $+          concat+            [ map renderTypeBind (M.toList ttable),+              map renderValBind (M.toList vtable),+              map renderModType (M.toList sigtable),+              map renderMod (M.toList modtable)+            ]+    where+      renderTypeBind (name, TypeAbbr l tps tp) =+        p l <+> pprName name <> mconcat (map ((text " " <>) . ppr) tps)+          <> text " =" <+> ppr tp+        where+          p Lifted = text "type^"+          p SizeLifted = text "type~"+          p Unlifted = text "type"+      renderValBind (name, BoundV tps t) =+        text "val" <+> pprName name <> mconcat (map ((text " " <>) . ppr) tps)+          <> text " =" <+> ppr t+      renderModType (name, _sig) =+        text "module type" <+> pprName name+      renderMod (name, mod) =+        text "module" <+> pprName name <> text " =" <+> ppr mod
src/Language/Futhark/Syntax.hs view
@@ -1,1105 +1,1219 @@-{-# LANGUAGE FlexibleContexts           #-}-{-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE MultiParamTypeClasses      #-}-{-# LANGUAGE Safe                       #-}-{-# LANGUAGE StandaloneDeriving         #-}-{-# LANGUAGE Strict                     #-}--- | The Futhark source language AST definition.  Many types, such as--- 'ExpBase'@, are parametrised by type and name representation.  See--- the @https://futhark.readthedocs.org@ for a language reference, or--- this module may be a little hard to understand.-module Language.Futhark.Syntax-  (-   module Language.Futhark.Core--  -- * Types-  , Uniqueness(..)-  , IntType(..)-  , FloatType(..)-  , PrimType(..)-  , ArrayDim (..)-  , DimDecl (..)-  , ShapeDecl (..)-  , shapeRank-  , stripDims-  , unifyShapes-  , TypeName(..)-  , typeNameFromQualName-  , qualNameFromTypeName-  , TypeBase(..)-  , TypeArg(..)-  , DimExp(..)-  , TypeExp(..)-  , TypeArgExp(..)-  , PName(..)-  , ScalarTypeBase(..)-  , PatternType-  , StructType-  , ValueType-  , Diet(..)-  , TypeDeclBase (..)--    -- * Values-  , IntValue(..)-  , FloatValue(..)-  , PrimValue(..)-  , IsPrimValue(..)-  , Value(..)--  -- * Abstract syntax tree-  , AttrInfo(..)-  , BinOp (..)-  , IdentBase (..)-  , Inclusiveness(..)-  , DimIndexBase(..)-  , ExpBase(..)-  , FieldBase(..)-  , CaseBase(..)-  , LoopFormBase (..)-  , PatternBase(..)--  -- * Module language-  , SpecBase(..)-  , SigExpBase(..)-  , TypeRefBase(..)-  , SigBindBase(..)-  , ModExpBase(..)-  , ModBindBase(..)-  , ModParamBase(..)--  -- * Definitions-  , DocComment(..)-  , ValBindBase(..)-  , EntryPoint(..)-  , EntryType(..)-  , Liftedness(..)-  , TypeBindBase(..)-  , TypeParamBase(..)-  , typeParamName-  , ProgBase(..)-  , DecBase(..)--  -- * Miscellaneous-  , Showable-  , NoInfo(..)-  , Info(..)-  , Alias(..)-  , Aliasing-  , QualName(..)-  )-  where--import           Control.Applicative-import           Control.Monad-import           Data.Array-import           Data.Bifoldable-import           Data.Bifunctor-import           Data.Bitraversable-import           Data.Foldable-import qualified Data.Map.Strict                  as M-import           Data.Monoid                      hiding (Sum)-import           Data.Ord-import qualified Data.Set                         as S-import           Data.Traversable-import qualified Data.List.NonEmpty               as NE-import           Prelude--import           Futhark.IR.Primitive (FloatType (..),-                                                   FloatValue (..),-                                                   IntType (..), IntValue (..))-import           Futhark.Util.Pretty-import           Futhark.Util.Loc-import           Language.Futhark.Core---- | Convenience class for deriving 'Show' instances for the AST.-class (Show vn,-       Show (f VName),-       Show (f (Diet, Maybe VName)),-       Show (f String),-       Show (f [VName]),-       Show (f ([VName], [VName])),-       Show (f PatternType),-       Show (f (PatternType, [VName])),-       Show (f (StructType, [VName])),-       Show (f EntryPoint),-       Show (f Int),-       Show (f StructType),-       Show (f (StructType, Maybe VName)),-       Show (f (Aliasing, StructType)),-       Show (f (M.Map VName VName)),-       Show (f Uniqueness)) => Showable f vn where---- | No information functor.  Usually used for placeholder type- or--- aliasing information.-data NoInfo a = NoInfo-              deriving (Eq, Ord, Show)--instance Show vn => Showable NoInfo vn where-instance Functor NoInfo where-  fmap _ NoInfo = NoInfo-instance Foldable NoInfo where-  foldr _ b NoInfo = b-instance Traversable NoInfo where-  traverse _ NoInfo = pure NoInfo---- | Some information.  The dual to 'NoInfo'-newtype Info a = Info { unInfo :: a }-            deriving (Eq, Ord, Show)--instance Show vn => Showable Info vn where-instance Functor Info where-  fmap f (Info x) = Info $ f x-instance Foldable Info where-  foldr f b (Info x) = f x b-instance Traversable Info where-  traverse f (Info x) = Info <$> f x---- | Low-level primitive types.-data PrimType = Signed IntType-              | Unsigned IntType-              | FloatType FloatType-              | Bool-              deriving (Eq, Ord, Show)---- | Non-array values.-data PrimValue = SignedValue !IntValue-               | UnsignedValue !IntValue-               | FloatValue !FloatValue-               | BoolValue !Bool-               deriving (Eq, Ord, Show)---- | A class for converting ordinary Haskell values to primitive--- Futhark values.-class IsPrimValue v where-  primValue :: v -> PrimValue--instance IsPrimValue Int where-  primValue = SignedValue . Int32Value . fromIntegral--instance IsPrimValue Int8 where-  primValue = SignedValue . Int8Value-instance IsPrimValue Int16 where-  primValue = SignedValue . Int16Value-instance IsPrimValue Int32 where-  primValue = SignedValue . Int32Value-instance IsPrimValue Int64 where-  primValue = SignedValue . Int64Value--instance IsPrimValue Word8 where-  primValue = UnsignedValue . Int8Value . fromIntegral-instance IsPrimValue Word16 where-  primValue = UnsignedValue . Int16Value . fromIntegral-instance IsPrimValue Word32 where-  primValue = UnsignedValue . Int32Value . fromIntegral-instance IsPrimValue Word64 where-  primValue = UnsignedValue . Int64Value . fromIntegral--instance IsPrimValue Float where-  primValue = FloatValue . Float32Value--instance IsPrimValue Double where-  primValue = FloatValue . Float64Value--instance IsPrimValue Bool where-  primValue = BoolValue---- | The payload of an attribute.-data AttrInfo-  = AttrAtom Name-  | AttrComp Name [AttrInfo]-  deriving (Eq, Ord, Show)---- | A type class for things that can be array dimensions.-class Eq dim => ArrayDim dim where-  -- | @unifyDims x y@ combines @x@ and @y@ to contain their maximum-  -- common information, and fails if they conflict.-  unifyDims :: dim -> dim -> Maybe dim--instance ArrayDim () where-  unifyDims () () = Just ()---- | Declaration of a dimension size.-data DimDecl vn = NamedDim (QualName vn)-                  -- ^ The size of the dimension is this name, which-                  -- must be in scope.  In a return type, this will-                  -- give rise to an assertion.-                | ConstDim Int-                  -- ^ The size is a constant.-                | AnyDim-                  -- ^ No dimension declaration.-                deriving Show-deriving instance Eq (DimDecl Name)-deriving instance Eq (DimDecl VName)-deriving instance Ord (DimDecl Name)-deriving instance Ord (DimDecl VName)--instance Functor DimDecl where-  fmap = fmapDefault--instance Foldable DimDecl where-  foldMap = foldMapDefault--instance Traversable DimDecl where-  traverse f (NamedDim qn) = NamedDim <$> traverse f qn-  traverse _ (ConstDim x) = pure $ ConstDim x-  traverse _ AnyDim = pure AnyDim---- Note that the notion of unifyDims here is intentionally not what we--- use when we do real type unification in the type checker.-instance ArrayDim (DimDecl VName) where-  unifyDims AnyDim y = Just y-  unifyDims x AnyDim = Just x-  unifyDims (NamedDim x) (NamedDim y) | x == y = Just $ NamedDim x-  unifyDims (ConstDim x) (ConstDim y) | x == y = Just $ ConstDim x-  unifyDims _ _ = Nothing---- | The size of an array type is a list of its dimension sizes.  If--- 'Nothing', that dimension is of a (statically) unknown size.-newtype ShapeDecl dim = ShapeDecl { shapeDims :: [dim] }-                      deriving (Eq, Ord, Show)--instance Foldable ShapeDecl where-  foldr f x (ShapeDecl ds) = foldr f x ds--instance Traversable ShapeDecl where-  traverse f (ShapeDecl ds) = ShapeDecl <$> traverse f ds--instance Functor ShapeDecl where-  fmap f (ShapeDecl ds) = ShapeDecl $ map f ds--instance Semigroup (ShapeDecl dim) where-  ShapeDecl l1 <> ShapeDecl l2 = ShapeDecl $ l1 ++ l2--instance Monoid (ShapeDecl dim) where-  mempty = ShapeDecl []---- | The number of dimensions contained in a shape.-shapeRank :: ShapeDecl dim -> Int-shapeRank = length . shapeDims---- | @stripDims n shape@ strips the outer @n@ dimensions from--- @shape@, returning 'Nothing' if this would result in zero or--- fewer dimensions.-stripDims :: Int -> ShapeDecl dim -> Maybe (ShapeDecl dim)-stripDims i (ShapeDecl l)-  | i < length l = Just $ ShapeDecl $ drop i l-  | otherwise    = Nothing----- | @unifyShapes x y@ combines @x@ and @y@ to contain their maximum--- common information, and fails if they conflict.-unifyShapes :: ArrayDim dim => ShapeDecl dim -> ShapeDecl dim -> Maybe (ShapeDecl dim)-unifyShapes (ShapeDecl xs) (ShapeDecl ys) = do-  guard $ length xs == length ys-  ShapeDecl <$> zipWithM unifyDims xs ys---- | A type name consists of qualifiers (for error messages) and a--- 'VName' (for equality checking).-data TypeName = TypeName { typeQuals :: [VName], typeLeaf :: VName }-              deriving (Show)--instance Eq TypeName where-  TypeName _ x == TypeName _ y = x == y--instance Ord TypeName where-  TypeName _ x `compare` TypeName _ y = x `compare` y---- | Convert a 'QualName' to a 'TypeName'.-typeNameFromQualName :: QualName VName -> TypeName-typeNameFromQualName (QualName qs x) = TypeName qs x---- | Convert a 'TypeName' to a 'QualName'.-qualNameFromTypeName :: TypeName -> QualName VName-qualNameFromTypeName (TypeName qs x) = QualName qs x---- | The name (if any) of a function parameter.  The 'Eq' and 'Ord'--- instances always compare values of this type equal.-data PName = Named VName | Unnamed-           deriving (Show)--instance Eq PName where-  _ == _ = True--instance Ord PName where-  _ <= _ = True---- | Types that can be elements of arrays.  This representation does--- allow arrays of records of functions, which is nonsensical, but it--- convolutes the code too much if we try to statically rule it out.-data ScalarTypeBase dim as-  = Prim PrimType-  | TypeVar as Uniqueness TypeName [TypeArg dim]-  | Record (M.Map Name (TypeBase dim as))-  | Sum (M.Map Name [TypeBase dim as])-  | Arrow as PName (TypeBase dim as) (TypeBase dim as)-    -- ^ The aliasing corresponds to the lexical-    -- closure of the function.-  deriving (Eq, Ord, Show)--instance Bitraversable ScalarTypeBase where-  bitraverse _ _ (Prim t) = pure $ Prim t-  bitraverse f g (Record fs) = Record <$> traverse (bitraverse f g) fs-  bitraverse f g (TypeVar als u t args) =-    TypeVar <$> g als <*> pure u <*> pure t <*> traverse (traverse f) args-  bitraverse f g (Arrow als v t1 t2) =-    Arrow <$> g als <*> pure v <*> bitraverse f g t1 <*> bitraverse f g t2-  bitraverse f g (Sum cs) = Sum <$> (traverse . traverse) (bitraverse f g) cs--instance Bifunctor ScalarTypeBase where-  bimap = bimapDefault--instance Bifoldable ScalarTypeBase where-  bifoldMap = bifoldMapDefault---- | An expanded Futhark type is either an array, or something that--- can be an element of an array.  When comparing types for equality,--- function parameter names are ignored.  This representation permits--- some malformed types (arrays of functions), but importantly rules--- out arrays-of-arrays.-data TypeBase dim as-  = Scalar (ScalarTypeBase dim as)-  | Array as Uniqueness (ScalarTypeBase dim ()) (ShapeDecl dim)-  deriving (Eq, Ord, Show)--instance Bitraversable TypeBase where-  bitraverse f g (Scalar t) = Scalar <$> bitraverse f g t-  bitraverse f g (Array a u t shape) =-    Array <$> g a <*> pure u <*> bitraverse f pure t <*> traverse f shape--instance Bifunctor TypeBase where-  bimap = bimapDefault--instance Bifoldable TypeBase where-  bifoldMap = bifoldMapDefault---- | An argument passed to a type constructor.-data TypeArg dim = TypeArgDim dim SrcLoc-                 | TypeArgType (TypeBase dim ()) SrcLoc-             deriving (Eq, Ord, Show)--instance Traversable TypeArg where-  traverse f (TypeArgDim v loc) = TypeArgDim <$> f v <*> pure loc-  traverse f (TypeArgType t loc) = TypeArgType <$> bitraverse f pure t <*> pure loc--instance Functor TypeArg where-  fmap = fmapDefault--instance Foldable TypeArg where-  foldMap = foldMapDefault---- | A variable that is aliased.  Can be still in-scope, or have gone--- out of scope and be free.  In the latter case, it behaves more like--- an equivalence class.  See uniqueness-error18.fut for an example of--- why this is necessary.-data Alias = AliasBound { aliasVar :: VName }-           | AliasFree { aliasVar :: VName }-           deriving (Eq, Ord, Show)---- | Aliasing for a type, which is a set of the variables that are--- aliased.-type Aliasing = S.Set Alias---- | A type with aliasing information and shape annotations, used for--- describing the type patterns and expressions.-type PatternType = TypeBase (DimDecl VName) Aliasing---- | A "structural" type with shape annotations and no aliasing--- information, used for declarations.-type StructType = TypeBase (DimDecl VName) ()---- | A value type contains full, manifest size information.-type ValueType = TypeBase Int32 ()---- | A dimension declaration expression for use in a 'TypeExp'.-data DimExp vn = DimExpNamed (QualName vn) SrcLoc-                 -- ^ The size of the dimension is this name, which-                 -- must be in scope.-               | DimExpConst Int SrcLoc-                  -- ^ The size is a constant.-               | DimExpAny-                  -- ^ No dimension declaration.-                deriving Show-deriving instance Eq (DimExp Name)-deriving instance Eq (DimExp VName)-deriving instance Ord (DimExp Name)-deriving instance Ord (DimExp VName)---- | An unstructured type with type variables and possibly shape--- declarations - this is what the user types in the source program.--- These are used to construct 'TypeBase's in the type checker.-data TypeExp vn = TEVar (QualName vn) SrcLoc-                | TETuple [TypeExp vn] SrcLoc-                | TERecord [(Name, TypeExp vn)] SrcLoc-                | TEArray (TypeExp vn) (DimExp vn) SrcLoc-                | TEUnique (TypeExp vn) SrcLoc-                | TEApply (TypeExp vn) (TypeArgExp vn) SrcLoc-                | TEArrow (Maybe vn) (TypeExp vn) (TypeExp vn) SrcLoc-                | TESum [(Name, [TypeExp vn])] SrcLoc-                 deriving (Show)-deriving instance Eq (TypeExp Name)-deriving instance Eq (TypeExp VName)-deriving instance Ord (TypeExp Name)-deriving instance Ord (TypeExp VName)--instance Located (TypeExp vn) where-  locOf (TEArray _ _ loc)   = locOf loc-  locOf (TETuple _ loc)     = locOf loc-  locOf (TERecord _ loc)    = locOf loc-  locOf (TEVar _ loc)       = locOf loc-  locOf (TEUnique _ loc)    = locOf loc-  locOf (TEApply _ _ loc)   = locOf loc-  locOf (TEArrow _ _ _ loc) = locOf loc-  locOf (TESum _ loc)      = locOf loc---- | A type argument expression passed to a type constructor.-data TypeArgExp vn = TypeArgExpDim (DimExp vn) SrcLoc-                   | TypeArgExpType (TypeExp vn)-                deriving (Show)-deriving instance Eq (TypeArgExp Name)-deriving instance Eq (TypeArgExp VName)-deriving instance Ord (TypeArgExp Name)-deriving instance Ord (TypeArgExp VName)--instance Located (TypeArgExp vn) where-  locOf (TypeArgExpDim _ loc) = locOf loc-  locOf (TypeArgExpType t)    = locOf t---- | A declaration of the type of something.-data TypeDeclBase f vn =-  TypeDecl { declaredType :: TypeExp vn-                             -- ^ The type declared by the user.-           , expandedType :: f StructType-                             -- ^ The type deduced by the type checker.-           }-deriving instance Showable f vn => Show (TypeDeclBase f vn)-deriving instance Eq (TypeDeclBase NoInfo VName)-deriving instance Ord (TypeDeclBase NoInfo VName)--instance Located (TypeDeclBase f vn) where-  locOf = locOf . declaredType---- | Information about which parts of a value/type are consumed.-data Diet = RecordDiet (M.Map Name Diet) -- ^ Consumes these fields in the record.-          | FuncDiet Diet Diet-            -- ^ A function that consumes its argument(s) like this.-            -- The final 'Diet' should always be 'Observe', as there-            -- is no way for a function to consume its return value.-          | Consume -- ^ Consumes this value.-          | Observe -- ^ Only observes value in this position, does-                    -- not consume.-            deriving (Eq, Show)---- | Simple Futhark values.  Values are fully evaluated and their type--- is always unambiguous.-data Value = PrimValue !PrimValue-           | ArrayValue !(Array Int Value) ValueType-             -- ^ It is assumed that the array is 0-indexed.  The type-             -- is the full type.-             deriving (Eq, Show)---- | An identifier consists of its name and the type of the value--- bound to the identifier.-data IdentBase f vn = Ident { identName   :: vn-                            , identType   :: f PatternType-                            , identSrcLoc :: SrcLoc-                            }-deriving instance Showable f vn => Show (IdentBase f vn)--instance Eq vn => Eq (IdentBase ty vn) where-  x == y = identName x == identName y--instance Ord vn => Ord (IdentBase ty vn) where-  compare = comparing identName--instance Located (IdentBase ty vn) where-  locOf = locOf . identSrcLoc---- | Default binary operators.-data BinOp =  Backtick-              -- ^ A pseudo-operator standing in for any normal-              -- identifier used as an operator (they all have the-              -- same fixity).-           -- Binary Ops for Numbers-           | Plus-           | Minus-           | Pow-           | Times-           | Divide-           | Mod-           | Quot-           | Rem-           | ShiftR-           | ShiftL-           | Band-           | Xor-           | Bor-           | LogAnd-           | LogOr-           -- Relational Ops for all primitive types at least-           | Equal-           | NotEqual-           | Less-           | Leq-           | Greater-           | Geq-           -- Some functional ops.-           | PipeRight -- ^ @|>@-           | PipeLeft -- ^ @<|@-           -- Misc-             deriving (Eq, Ord, Show, Enum, Bounded)---- | Whether a bound for an end-point of a 'DimSlice' or a range--- literal is inclusive or exclusive.-data Inclusiveness a = DownToExclusive a-                     | ToInclusive a -- ^ May be "down to" if step is negative.-                     | UpToExclusive a-                     deriving (Eq, Ord, Show)--instance Located a => Located (Inclusiveness a) where-  locOf (DownToExclusive x) = locOf x-  locOf (ToInclusive x) = locOf x-  locOf (UpToExclusive x) = locOf x--instance Functor Inclusiveness where-  fmap = fmapDefault--instance Foldable Inclusiveness where-  foldMap = foldMapDefault--instance Traversable Inclusiveness where-  traverse f (DownToExclusive x) = DownToExclusive <$> f x-  traverse f (ToInclusive x) = ToInclusive <$> f x-  traverse f (UpToExclusive x) = UpToExclusive <$> f x---- | An indexing of a single dimension.-data DimIndexBase f vn = DimFix (ExpBase f vn)-                       | DimSlice (Maybe (ExpBase f vn))-                                  (Maybe (ExpBase f vn))-                                  (Maybe (ExpBase f vn))-deriving instance Showable f vn => Show (DimIndexBase f vn)-deriving instance Eq (DimIndexBase NoInfo VName)-deriving instance Ord (DimIndexBase NoInfo VName)---- | A name qualified with a breadcrumb of module accesses.-data QualName vn = QualName { qualQuals :: ![vn]-                            , qualLeaf  :: !vn-                            }-  deriving (Show)--instance Eq (QualName Name) where-  QualName qs1 v1 == QualName qs2 v2 = qs1 == qs2 && v1 == v2--instance Eq (QualName VName) where-  QualName _ v1 == QualName _ v2 = v1 == v2--instance Ord (QualName Name) where-  QualName qs1 v1 `compare` QualName qs2 v2 = compare (qs1, v1) (qs2, v2)--instance Ord (QualName VName) where-  QualName _ v1 `compare` QualName _ v2 = compare v1 v2--instance Functor QualName where-  fmap = fmapDefault--instance Foldable QualName where-  foldMap = foldMapDefault--instance Traversable QualName where-  traverse f (QualName qs v) = QualName <$> traverse f qs <*> f v---- | The Futhark expression language.------ In a value of type @Exp f vn@, annotations are wrapped in the--- functor @f@, and all names are of type @vn@.------ This allows us to encode whether or not the expression has been--- type-checked in the Haskell type of the expression.  Specifically,--- the parser will produce expressions of type @Exp 'NoInfo' 'Name'@,--- and the type checker will convert these to @Exp 'Info' 'VName'@, in--- which type information is always present and all names are unique.-data ExpBase f vn =-              Literal PrimValue SrcLoc--            | IntLit Integer (f PatternType) SrcLoc-            -- ^ A polymorphic integral literal.--            | FloatLit Double (f PatternType) SrcLoc-            -- ^ A polymorphic decimal literal.--            | StringLit [Word8] SrcLoc-            -- ^ A string literal is just a fancy syntax for an array-            -- of bytes.--            | Parens (ExpBase f vn) SrcLoc-            -- ^ A parenthesized expression.--            | QualParens (QualName vn, SrcLoc) (ExpBase f vn) SrcLoc--            | TupLit    [ExpBase f vn] SrcLoc-            -- ^ Tuple literals, e.g., @{1+3, {x, y+z}}@.--            | RecordLit [FieldBase f vn] SrcLoc-            -- ^ Record literals, e.g. @{x=2,y=3,z}@.--            | ArrayLit  [ExpBase f vn] (f PatternType) SrcLoc-            -- ^ Array literals, e.g., @[ [1+x, 3], [2, 1+4] ]@.-            -- Second arg is the row type of the rows of the array.--            | Range (ExpBase f vn) (Maybe (ExpBase f vn)) (Inclusiveness (ExpBase f vn))-              (f PatternType, f [VName]) SrcLoc--            | Var (QualName vn) (f PatternType) SrcLoc--            | Ascript (ExpBase f vn) (TypeDeclBase f vn) SrcLoc-            -- ^ Type ascription: @e : t@.--            | Coerce (ExpBase f vn) (TypeDeclBase f vn) (f PatternType, f [VName]) SrcLoc-            -- ^ Size coercion: @e :> t@.--            | LetPat (PatternBase f vn) (ExpBase f vn) (ExpBase f vn)-              (f PatternType, f [VName]) SrcLoc--            | LetFun vn ([TypeParamBase vn],-                         [PatternBase f vn],-                         Maybe (TypeExp vn),-                         f StructType,-                         ExpBase f vn)-              (ExpBase f vn) (f PatternType) SrcLoc--            | If (ExpBase f vn) (ExpBase f vn) (ExpBase f vn) (f PatternType, f [VName]) SrcLoc--            | Apply (ExpBase f vn) (ExpBase f vn)-              (f (Diet, Maybe VName)) (f PatternType, f [VName]) SrcLoc-              -- ^ The @Maybe VName@ is a possible existential size-              -- that is instantiated by this argument..-              ---              -- The @[VName]@ are the existential sizes that come-              -- into being at this call site.--            | Negate (ExpBase f vn) SrcLoc-              -- ^ Numeric negation (ugly special case; Haskell did it first).--            | Lambda [PatternBase f vn] (ExpBase f vn)-              (Maybe (TypeExp vn)) (f (Aliasing, StructType)) SrcLoc--            | OpSection (QualName vn) (f PatternType) SrcLoc-              -- ^ @+@; first two types are operands, third is result.-            | OpSectionLeft (QualName vn) (f PatternType) (ExpBase f vn)-              (f (StructType, Maybe VName), f StructType) (f PatternType, f [VName]) SrcLoc-              -- ^ @2+@; first type is operand, second is result.-            | OpSectionRight (QualName vn) (f PatternType) (ExpBase f vn)-              (f StructType, f (StructType, Maybe VName)) (f PatternType) SrcLoc-              -- ^ @+2@; first type is operand, second is result.-            | ProjectSection [Name] (f PatternType) SrcLoc-              -- ^ Field projection as a section: @(.x.y.z)@.-            | IndexSection [DimIndexBase f vn] (f PatternType) SrcLoc-              -- ^ Array indexing as a section: @(.[i,j])@.--            | DoLoop-              [VName] -- Size parameters.-              (PatternBase f vn) -- Merge variable pattern.-              (ExpBase f vn) -- Initial values of merge variables.-              (LoopFormBase f vn) -- Do or while loop.-              (ExpBase f vn) -- Loop body.-              (f (PatternType, [VName])) -- Return type.-              SrcLoc--            | BinOp (QualName vn, SrcLoc) (f PatternType)-              (ExpBase f vn, f (StructType, Maybe VName))-              (ExpBase f vn, f (StructType, Maybe VName))-              (f PatternType) (f [VName]) SrcLoc--            | Project Name (ExpBase f vn) (f PatternType) SrcLoc--            -- Primitive array operations-            | LetWith (IdentBase f vn) (IdentBase f vn)-                      [DimIndexBase f vn] (ExpBase f vn)-                      (ExpBase f vn) (f PatternType) SrcLoc--            | Index (ExpBase f vn) [DimIndexBase f vn] (f PatternType, f [VName]) SrcLoc--            | Update (ExpBase f vn) [DimIndexBase f vn] (ExpBase f vn) SrcLoc--            | RecordUpdate (ExpBase f vn) [Name] (ExpBase f vn) (f PatternType) SrcLoc--            | Assert (ExpBase f vn) (ExpBase f vn) (f String) SrcLoc-            -- ^ Fail if the first expression does not return true,-            -- and return the value of the second expression if it-            -- does.--            | Constr Name [ExpBase f vn] (f PatternType) SrcLoc-            -- ^ An n-ary value constructor.--            | Match (ExpBase f vn) (NE.NonEmpty (CaseBase f vn))-              (f PatternType, f [VName]) SrcLoc-            -- ^ A match expression.--            | Attr AttrInfo (ExpBase f vn) SrcLoc-            -- ^ An attribute applied to the following expression.--deriving instance Showable f vn => Show (ExpBase f vn)-deriving instance Eq (ExpBase NoInfo VName)-deriving instance Ord (ExpBase NoInfo VName)--instance Located (ExpBase f vn) where-  locOf (Literal _ loc)                = locOf loc-  locOf (IntLit _ _ loc)               = locOf loc-  locOf (FloatLit _ _ loc)             = locOf loc-  locOf (Parens _ loc)                 = locOf loc-  locOf (QualParens _ _ loc)           = locOf loc-  locOf (TupLit _ pos)                 = locOf pos-  locOf (RecordLit _ pos)              = locOf pos-  locOf (Project _ _ _ pos)            = locOf pos-  locOf (ArrayLit _ _ pos)             = locOf pos-  locOf (StringLit _ loc)              = locOf loc-  locOf (Range _ _ _ _ pos)            = locOf pos-  locOf (BinOp _ _ _ _ _ _ loc)        = locOf loc-  locOf (If _ _ _ _ pos)               = locOf pos-  locOf (Var _ _ loc)                  = locOf loc-  locOf (Ascript _ _ loc)              = locOf loc-  locOf (Coerce _ _ _ loc)             = locOf loc-  locOf (Negate _ pos)                 = locOf pos-  locOf (Apply _ _ _ _ loc)            = locOf loc-  locOf (LetPat _ _ _ _ loc)           = locOf loc-  locOf (LetFun _ _ _ _ loc)           = locOf loc-  locOf (LetWith _ _ _ _ _ _ loc)      = locOf loc-  locOf (Index _ _ _ loc)              = locOf loc-  locOf (Update _ _ _ pos)             = locOf pos-  locOf (RecordUpdate _ _ _ _ pos)     = locOf pos-  locOf (Lambda _ _ _ _ loc)           = locOf loc-  locOf (OpSection _ _ loc)            = locOf loc-  locOf (OpSectionLeft _ _ _ _ _ loc)  = locOf loc-  locOf (OpSectionRight _ _ _ _ _ loc) = locOf loc-  locOf (ProjectSection _ _ loc)       = locOf loc-  locOf (IndexSection _ _ loc)         = locOf loc-  locOf (DoLoop _ _ _ _ _ _ loc)       = locOf loc-  locOf (Assert _ _ _ loc)             = locOf loc-  locOf (Constr _ _ _ loc)             = locOf loc-  locOf (Match _ _ _ loc)              = locOf loc-  locOf (Attr _ _ loc)                 = locOf loc---- | An entry in a record literal.-data FieldBase f vn = RecordFieldExplicit Name (ExpBase f vn) SrcLoc-                    | RecordFieldImplicit vn (f PatternType) SrcLoc-deriving instance Showable f vn => Show (FieldBase f vn)-deriving instance Eq (FieldBase NoInfo VName)-deriving instance Ord (FieldBase NoInfo VName)--instance Located (FieldBase f vn) where-  locOf (RecordFieldExplicit _ _ loc) = locOf loc-  locOf (RecordFieldImplicit _ _ loc) = locOf loc---- | A case in a match expression.-data CaseBase f vn = CasePat (PatternBase f vn) (ExpBase f vn) SrcLoc-deriving instance Showable f vn => Show (CaseBase f vn)-deriving instance Eq (CaseBase NoInfo VName)-deriving instance Ord (CaseBase NoInfo VName)--instance Located (CaseBase f vn) where-  locOf (CasePat _ _ loc) = locOf loc---- | Whether the loop is a @for@-loop or a @while@-loop.-data LoopFormBase f vn = For (IdentBase f vn) (ExpBase f vn)-                       | ForIn (PatternBase f vn) (ExpBase f vn)-                       | While (ExpBase f vn)-deriving instance Showable f vn => Show (LoopFormBase f vn)-deriving instance Eq (LoopFormBase NoInfo VName)-deriving instance Ord (LoopFormBase NoInfo VName)---- | A pattern as used most places where variables are bound (function--- parameters, @let@ expressions, etc).-data PatternBase f vn = TuplePattern [PatternBase f vn] SrcLoc-                      | RecordPattern [(Name, PatternBase f vn)] SrcLoc-                      | PatternParens (PatternBase f vn) SrcLoc-                      | Id vn (f PatternType) SrcLoc-                      | Wildcard (f PatternType) SrcLoc -- Nothing, i.e. underscore.-                      | PatternAscription (PatternBase f vn) (TypeDeclBase f vn) SrcLoc-                      | PatternLit (ExpBase f vn) (f PatternType) SrcLoc-                      | PatternConstr Name (f PatternType) [PatternBase f vn] SrcLoc-deriving instance Showable f vn => Show (PatternBase f vn)-deriving instance Eq (PatternBase NoInfo VName)-deriving instance Ord (PatternBase NoInfo VName)--instance Located (PatternBase f vn) where-  locOf (TuplePattern _ loc)        = locOf loc-  locOf (RecordPattern _ loc)       = locOf loc-  locOf (PatternParens _ loc)       = locOf loc-  locOf (Id _ _ loc)                = locOf loc-  locOf (Wildcard _ loc)            = locOf loc-  locOf (PatternAscription _ _ loc) = locOf loc-  locOf (PatternLit _ _ loc)        = locOf loc-  locOf (PatternConstr _ _ _ loc)   = locOf loc---- | Documentation strings, including source location.-data DocComment = DocComment String SrcLoc-  deriving (Show)--instance Located DocComment where-  locOf (DocComment _ loc) = locOf loc---- | Part of the type of an entry point.  Has an actual type, and--- maybe also an ascribed type expression.-data EntryType =-  EntryType { entryType :: StructType-            , entryAscribed :: Maybe (TypeExp VName)-            }-  deriving (Show)---- | Information about the external interface exposed by an entry--- point.  The important thing is that that we remember the original--- source-language types, without desugaring them at all.  The--- annoying thing is that we do not require type annotations on entry--- points, so the types can be either ascribed or inferred.-data EntryPoint =-  EntryPoint { entryParams :: [EntryType]-             , entryReturn :: EntryType-             }-  deriving (Show)---- | Function Declarations-data ValBindBase f vn = ValBind-  { valBindEntryPoint :: Maybe (f EntryPoint)-    -- ^ Just if this function is an entry point.  If so, it also-    -- contains the externally visible interface.  Note that this may not-    -- strictly be well-typed after some desugaring operations, as it-    -- may refer to abstract types that are no longer in scope.-  , valBindName       :: vn-  , valBindRetDecl    :: Maybe (TypeExp vn)-  , valBindRetType    :: f (StructType, [VName])-  , valBindTypeParams :: [TypeParamBase vn]-  , valBindParams     :: [PatternBase f vn]-  , valBindBody       :: ExpBase f vn-  , valBindDoc        :: Maybe DocComment-  , valBindAttrs      :: [AttrInfo]-  , valBindLocation   :: SrcLoc-  }-deriving instance Showable f vn => Show (ValBindBase f vn)--instance Located (ValBindBase f vn) where-  locOf = locOf . valBindLocation---- | Type Declarations-data TypeBindBase f vn = TypeBind { typeAlias        :: vn-                                  , typeLiftedness   :: Liftedness-                                  , typeParams       :: [TypeParamBase vn]-                                  , typeExp          :: TypeDeclBase f vn-                                  , typeDoc          :: Maybe DocComment-                                  , typeBindLocation :: SrcLoc-                                  }-deriving instance Showable f vn => Show (TypeBindBase f vn)--instance Located (TypeBindBase f vn) where-  locOf = locOf . typeBindLocation---- | The liftedness of a type parameter.  By the @Ord@ instance,--- @Unlifted < SizeLifted < Lifted@.-data Liftedness-  = Unlifted-    -- ^ May only be instantiated with a zero-order type of (possibly-    -- symbolically) known size.-  | SizeLifted-    -- ^ May only be instantiated with a zero-order type, but the size-    -- can be varying.-  | Lifted-    -- ^ May be instantiated with a functional type.-  deriving (Eq, Ord, Show)---- | A type parameter.-data TypeParamBase vn-  = TypeParamDim vn SrcLoc-    -- ^ A type parameter that must be a size.-  | TypeParamType Liftedness vn SrcLoc-    -- ^ A type parameter that must be a type.-  deriving (Eq, Ord, Show)--instance Functor TypeParamBase where-  fmap = fmapDefault--instance Foldable TypeParamBase where-  foldMap = foldMapDefault--instance Traversable TypeParamBase where-  traverse f (TypeParamDim v loc) = TypeParamDim <$> f v <*> pure loc-  traverse f (TypeParamType l v loc) = TypeParamType l <$> f v <*> pure loc--instance Located (TypeParamBase vn) where-  locOf (TypeParamDim _ loc)    = locOf loc-  locOf (TypeParamType _ _ loc) = locOf loc---- | The name of a type parameter.-typeParamName :: TypeParamBase vn -> vn-typeParamName (TypeParamDim v _)    = v-typeParamName (TypeParamType _ v _) = v---- | A spec is a component of a module type.-data SpecBase f vn-  = ValSpec  { specName       :: vn-             , specTypeParams :: [TypeParamBase vn]-             , specType       :: TypeDeclBase f vn-             , specDoc        :: Maybe DocComment-             , specLocation   :: SrcLoc-             }-  | TypeAbbrSpec (TypeBindBase f vn)-  | TypeSpec Liftedness vn [TypeParamBase vn] (Maybe DocComment) SrcLoc-    -- ^ Abstract type.-  | ModSpec vn (SigExpBase f vn) (Maybe DocComment) SrcLoc-  | IncludeSpec (SigExpBase f vn) SrcLoc-deriving instance Showable f vn => Show (SpecBase f vn)--instance Located (SpecBase f vn) where-  locOf (ValSpec _ _ _ _ loc)  = locOf loc-  locOf (TypeAbbrSpec tbind)   = locOf tbind-  locOf (TypeSpec _ _ _ _ loc) = locOf loc-  locOf (ModSpec _ _ _ loc)    = locOf loc-  locOf (IncludeSpec _ loc)    = locOf loc---- | A module type expression.-data SigExpBase f vn-  = SigVar (QualName vn) (f (M.Map VName VName)) SrcLoc-  | SigParens (SigExpBase f vn) SrcLoc-  | SigSpecs [SpecBase f vn] SrcLoc-  | SigWith (SigExpBase f vn) (TypeRefBase f vn) SrcLoc-  | SigArrow (Maybe vn) (SigExpBase f vn) (SigExpBase f vn) SrcLoc-deriving instance Showable f vn => Show (SigExpBase f vn)---- | A type refinement.-data TypeRefBase f vn = TypeRef (QualName vn) [TypeParamBase vn] (TypeDeclBase f vn) SrcLoc-deriving instance Showable f vn => Show (TypeRefBase f vn)--instance Located (TypeRefBase f vn) where-  locOf (TypeRef _ _ _ loc) = locOf loc--instance Located (SigExpBase f vn) where-  locOf (SigVar _ _ loc)     = locOf loc-  locOf (SigParens _ loc)    = locOf loc-  locOf (SigSpecs _ loc)     = locOf loc-  locOf (SigWith _ _ loc)    = locOf loc-  locOf (SigArrow _ _ _ loc) = locOf loc---- | Module type binding.-data SigBindBase f vn = SigBind { sigName :: vn-                                , sigExp  :: SigExpBase f vn-                                , sigDoc  :: Maybe DocComment-                                , sigLoc  :: SrcLoc-                                }-deriving instance Showable f vn => Show (SigBindBase f vn)--instance Located (SigBindBase f vn) where-  locOf = locOf . sigLoc---- | Module expression.-data ModExpBase f vn-  = ModVar (QualName vn) SrcLoc-  | ModParens (ModExpBase f vn) SrcLoc-  | ModImport FilePath (f FilePath) SrcLoc-    -- ^ The contents of another file as a module.-  | ModDecs [DecBase f vn] SrcLoc-  | ModApply (ModExpBase f vn) (ModExpBase f vn)-    (f (M.Map VName VName)) (f (M.Map VName VName)) SrcLoc-    -- ^ Functor application.  The first mapping is from parameter-    -- names to argument names, while the second maps names in the-    -- constructed module to the names inside the functor.-  | ModAscript (ModExpBase f vn) (SigExpBase f vn) (f (M.Map VName VName)) SrcLoc-  | ModLambda (ModParamBase f vn)-    (Maybe (SigExpBase f vn, f (M.Map VName VName)))-    (ModExpBase f vn)-    SrcLoc-deriving instance Showable f vn => Show (ModExpBase f vn)--instance Located (ModExpBase f vn) where-  locOf (ModVar _ loc)         = locOf loc-  locOf (ModParens _ loc)      = locOf loc-  locOf (ModImport _ _ loc)    = locOf loc-  locOf (ModDecs _ loc)        = locOf loc-  locOf (ModApply _ _ _ _ loc) = locOf loc-  locOf (ModAscript _ _ _ loc) = locOf loc-  locOf (ModLambda _ _ _ loc)  = locOf loc---- | A module binding.-data ModBindBase f vn =-  ModBind { modName      :: vn-          , modParams    :: [ModParamBase f vn]-          , modSignature :: Maybe (SigExpBase f vn, f (M.Map VName VName))-          , modExp       :: ModExpBase f vn-          , modDoc       :: Maybe DocComment-          , modLocation  :: SrcLoc-          }-deriving instance Showable f vn => Show (ModBindBase f vn)--instance Located (ModBindBase f vn) where-  locOf = locOf . modLocation---- | A module parameter.-data ModParamBase f vn = ModParam { modParamName     :: vn-                                  , modParamType     :: SigExpBase f vn-                                  , modParamAbs      :: f [VName]-                                  , modParamLocation :: SrcLoc-                                  }-deriving instance Showable f vn => Show (ModParamBase f vn)--instance Located (ModParamBase f vn) where-  locOf = locOf . modParamLocation---- | A top-level binding.-data DecBase f vn = ValDec (ValBindBase f vn)-                  | TypeDec (TypeBindBase f vn)-                  | SigDec (SigBindBase f vn)-                  | ModDec (ModBindBase f vn)-                  | OpenDec (ModExpBase f vn) SrcLoc-                  | LocalDec (DecBase f vn) SrcLoc-                  | ImportDec FilePath (f FilePath) SrcLoc-deriving instance Showable f vn => Show (DecBase f vn)--instance Located (DecBase f vn) where-  locOf (ValDec d)          = locOf d-  locOf (TypeDec d)         = locOf d-  locOf (SigDec d)          = locOf d-  locOf (ModDec d)          = locOf d-  locOf (OpenDec _ loc)     = locOf loc-  locOf (LocalDec _ loc)    = locOf loc-  locOf (ImportDec _ _ loc) = locOf loc---- | The program described by a single Futhark file.  May depend on--- other files.-data ProgBase f vn = Prog { progDoc :: Maybe DocComment-                          , progDecs :: [DecBase f vn]-                          }-deriving instance Showable f vn => Show (ProgBase f vn)----- Some prettyprinting definitions are here because we need them in---- the Attributes module.--instance Pretty PrimType where-  ppr (Unsigned Int8)  = text "u8"-  ppr (Unsigned Int16) = text "u16"-  ppr (Unsigned Int32) = text "u32"-  ppr (Unsigned Int64) = text "u64"-  ppr (Signed t)       = ppr t-  ppr (FloatType t)    = ppr t-  ppr Bool             = text "bool"--instance Pretty BinOp where-  ppr Backtick  = text "``"-  ppr Plus      = text "+"-  ppr Minus     = text "-"-  ppr Pow       = text "**"-  ppr Times     = text "*"-  ppr Divide    = text "/"-  ppr Mod       = text "%"-  ppr Quot      = text "//"-  ppr Rem       = text "%%"-  ppr ShiftR    = text ">>"-  ppr ShiftL    = text "<<"-  ppr Band      = text "&"-  ppr Xor       = text "^"-  ppr Bor       = text "|"-  ppr LogAnd    = text "&&"-  ppr LogOr     = text "||"-  ppr Equal     = text "=="-  ppr NotEqual  = text "!="-  ppr Less      = text "<"-  ppr Leq       = text "<="-  ppr Greater   = text ">"-  ppr Geq       = text ">="-  ppr PipeLeft  = text "<|"+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE Strict #-}++-- | The Futhark source language AST definition.  Many types, such as+-- 'ExpBase'@, are parametrised by type and name representation.  See+-- the @https://futhark.readthedocs.org@ for a language reference, or+-- this module may be a little hard to understand.+module Language.Futhark.Syntax+  ( module Language.Futhark.Core,++    -- * Types+    Uniqueness (..),+    IntType (..),+    FloatType (..),+    PrimType (..),+    ArrayDim (..),+    DimDecl (..),+    ShapeDecl (..),+    shapeRank,+    stripDims,+    unifyShapes,+    TypeName (..),+    typeNameFromQualName,+    qualNameFromTypeName,+    TypeBase (..),+    TypeArg (..),+    DimExp (..),+    TypeExp (..),+    TypeArgExp (..),+    PName (..),+    ScalarTypeBase (..),+    PatternType,+    StructType,+    ValueType,+    Diet (..),+    TypeDeclBase (..),++    -- * Values+    IntValue (..),+    FloatValue (..),+    PrimValue (..),+    IsPrimValue (..),+    Value (..),++    -- * Abstract syntax tree+    AttrInfo (..),+    BinOp (..),+    IdentBase (..),+    Inclusiveness (..),+    DimIndexBase (..),+    ExpBase (..),+    FieldBase (..),+    CaseBase (..),+    LoopFormBase (..),+    PatternBase (..),++    -- * Module language+    SpecBase (..),+    SigExpBase (..),+    TypeRefBase (..),+    SigBindBase (..),+    ModExpBase (..),+    ModBindBase (..),+    ModParamBase (..),++    -- * Definitions+    DocComment (..),+    ValBindBase (..),+    EntryPoint (..),+    EntryType (..),+    Liftedness (..),+    TypeBindBase (..),+    TypeParamBase (..),+    typeParamName,+    ProgBase (..),+    DecBase (..),++    -- * Miscellaneous+    Showable,+    NoInfo (..),+    Info (..),+    Alias (..),+    Aliasing,+    QualName (..),+  )+where++import Control.Applicative+import Control.Monad+import Data.Array+import Data.Bifoldable+import Data.Bifunctor+import Data.Bitraversable+import Data.Foldable+import qualified Data.List.NonEmpty as NE+import qualified Data.Map.Strict as M+import Data.Monoid hiding (Sum)+import Data.Ord+import qualified Data.Set as S+import Data.Traversable+import Futhark.IR.Primitive+  ( FloatType (..),+    FloatValue (..),+    IntType (..),+    IntValue (..),+  )+import Futhark.Util.Loc+import Futhark.Util.Pretty+import Language.Futhark.Core+import Prelude++-- | Convenience class for deriving 'Show' instances for the AST.+class+  ( Show vn,+    Show (f VName),+    Show (f (Diet, Maybe VName)),+    Show (f String),+    Show (f [VName]),+    Show (f ([VName], [VName])),+    Show (f PatternType),+    Show (f (PatternType, [VName])),+    Show (f (StructType, [VName])),+    Show (f EntryPoint),+    Show (f Int),+    Show (f StructType),+    Show (f (StructType, Maybe VName)),+    Show (f (Aliasing, StructType)),+    Show (f (M.Map VName VName)),+    Show (f Uniqueness)+  ) =>+  Showable f vn++-- | No information functor.  Usually used for placeholder type- or+-- aliasing information.+data NoInfo a = NoInfo+  deriving (Eq, Ord, Show)++instance Show vn => Showable NoInfo vn++instance Functor NoInfo where+  fmap _ NoInfo = NoInfo++instance Foldable NoInfo where+  foldr _ b NoInfo = b++instance Traversable NoInfo where+  traverse _ NoInfo = pure NoInfo++-- | Some information.  The dual to 'NoInfo'+newtype Info a = Info {unInfo :: a}+  deriving (Eq, Ord, Show)++instance Show vn => Showable Info vn++instance Functor Info where+  fmap f (Info x) = Info $ f x++instance Foldable Info where+  foldr f b (Info x) = f x b++instance Traversable Info where+  traverse f (Info x) = Info <$> f x++-- | Low-level primitive types.+data PrimType+  = Signed IntType+  | Unsigned IntType+  | FloatType FloatType+  | Bool+  deriving (Eq, Ord, Show)++-- | Non-array values.+data PrimValue+  = SignedValue !IntValue+  | UnsignedValue !IntValue+  | FloatValue !FloatValue+  | BoolValue !Bool+  deriving (Eq, Ord, Show)++-- | A class for converting ordinary Haskell values to primitive+-- Futhark values.+class IsPrimValue v where+  primValue :: v -> PrimValue++instance IsPrimValue Int where+  primValue = SignedValue . Int32Value . fromIntegral++instance IsPrimValue Int8 where+  primValue = SignedValue . Int8Value++instance IsPrimValue Int16 where+  primValue = SignedValue . Int16Value++instance IsPrimValue Int32 where+  primValue = SignedValue . Int32Value++instance IsPrimValue Int64 where+  primValue = SignedValue . Int64Value++instance IsPrimValue Word8 where+  primValue = UnsignedValue . Int8Value . fromIntegral++instance IsPrimValue Word16 where+  primValue = UnsignedValue . Int16Value . fromIntegral++instance IsPrimValue Word32 where+  primValue = UnsignedValue . Int32Value . fromIntegral++instance IsPrimValue Word64 where+  primValue = UnsignedValue . Int64Value . fromIntegral++instance IsPrimValue Float where+  primValue = FloatValue . Float32Value++instance IsPrimValue Double where+  primValue = FloatValue . Float64Value++instance IsPrimValue Bool where+  primValue = BoolValue++-- | The payload of an attribute.+data AttrInfo+  = AttrAtom Name+  | AttrComp Name [AttrInfo]+  deriving (Eq, Ord, Show)++-- | A type class for things that can be array dimensions.+class Eq dim => ArrayDim dim where+  -- | @unifyDims x y@ combines @x@ and @y@ to contain their maximum+  -- common information, and fails if they conflict.+  unifyDims :: dim -> dim -> Maybe dim++instance ArrayDim () where+  unifyDims () () = Just ()++-- | Declaration of a dimension size.+data DimDecl vn+  = -- | The size of the dimension is this name, which+    -- must be in scope.  In a return type, this will+    -- give rise to an assertion.+    NamedDim (QualName vn)+  | -- | The size is a constant.+    ConstDim Int+  | -- | No dimension declaration.+    AnyDim+  deriving (Show)++deriving instance Eq (DimDecl Name)++deriving instance Eq (DimDecl VName)++deriving instance Ord (DimDecl Name)++deriving instance Ord (DimDecl VName)++instance Functor DimDecl where+  fmap = fmapDefault++instance Foldable DimDecl where+  foldMap = foldMapDefault++instance Traversable DimDecl where+  traverse f (NamedDim qn) = NamedDim <$> traverse f qn+  traverse _ (ConstDim x) = pure $ ConstDim x+  traverse _ AnyDim = pure AnyDim++-- Note that the notion of unifyDims here is intentionally not what we+-- use when we do real type unification in the type checker.+instance ArrayDim (DimDecl VName) where+  unifyDims AnyDim y = Just y+  unifyDims x AnyDim = Just x+  unifyDims (NamedDim x) (NamedDim y) | x == y = Just $ NamedDim x+  unifyDims (ConstDim x) (ConstDim y) | x == y = Just $ ConstDim x+  unifyDims _ _ = Nothing++-- | The size of an array type is a list of its dimension sizes.  If+-- 'Nothing', that dimension is of a (statically) unknown size.+newtype ShapeDecl dim = ShapeDecl {shapeDims :: [dim]}+  deriving (Eq, Ord, Show)++instance Foldable ShapeDecl where+  foldr f x (ShapeDecl ds) = foldr f x ds++instance Traversable ShapeDecl where+  traverse f (ShapeDecl ds) = ShapeDecl <$> traverse f ds++instance Functor ShapeDecl where+  fmap f (ShapeDecl ds) = ShapeDecl $ map f ds++instance Semigroup (ShapeDecl dim) where+  ShapeDecl l1 <> ShapeDecl l2 = ShapeDecl $ l1 ++ l2++instance Monoid (ShapeDecl dim) where+  mempty = ShapeDecl []++-- | The number of dimensions contained in a shape.+shapeRank :: ShapeDecl dim -> Int+shapeRank = length . shapeDims++-- | @stripDims n shape@ strips the outer @n@ dimensions from+-- @shape@, returning 'Nothing' if this would result in zero or+-- fewer dimensions.+stripDims :: Int -> ShapeDecl dim -> Maybe (ShapeDecl dim)+stripDims i (ShapeDecl l)+  | i < length l = Just $ ShapeDecl $ drop i l+  | otherwise = Nothing++-- | @unifyShapes x y@ combines @x@ and @y@ to contain their maximum+-- common information, and fails if they conflict.+unifyShapes :: ArrayDim dim => ShapeDecl dim -> ShapeDecl dim -> Maybe (ShapeDecl dim)+unifyShapes (ShapeDecl xs) (ShapeDecl ys) = do+  guard $ length xs == length ys+  ShapeDecl <$> zipWithM unifyDims xs ys++-- | A type name consists of qualifiers (for error messages) and a+-- 'VName' (for equality checking).+data TypeName = TypeName {typeQuals :: [VName], typeLeaf :: VName}+  deriving (Show)++instance Eq TypeName where+  TypeName _ x == TypeName _ y = x == y++instance Ord TypeName where+  TypeName _ x `compare` TypeName _ y = x `compare` y++-- | Convert a 'QualName' to a 'TypeName'.+typeNameFromQualName :: QualName VName -> TypeName+typeNameFromQualName (QualName qs x) = TypeName qs x++-- | Convert a 'TypeName' to a 'QualName'.+qualNameFromTypeName :: TypeName -> QualName VName+qualNameFromTypeName (TypeName qs x) = QualName qs x++-- | The name (if any) of a function parameter.  The 'Eq' and 'Ord'+-- instances always compare values of this type equal.+data PName = Named VName | Unnamed+  deriving (Show)++instance Eq PName where+  _ == _ = True++instance Ord PName where+  _ <= _ = True++-- | Types that can be elements of arrays.  This representation does+-- allow arrays of records of functions, which is nonsensical, but it+-- convolutes the code too much if we try to statically rule it out.+data ScalarTypeBase dim as+  = Prim PrimType+  | TypeVar as Uniqueness TypeName [TypeArg dim]+  | Record (M.Map Name (TypeBase dim as))+  | Sum (M.Map Name [TypeBase dim as])+  | -- | The aliasing corresponds to the lexical+    -- closure of the function.+    Arrow as PName (TypeBase dim as) (TypeBase dim as)+  deriving (Eq, Ord, Show)++instance Bitraversable ScalarTypeBase where+  bitraverse _ _ (Prim t) = pure $ Prim t+  bitraverse f g (Record fs) = Record <$> traverse (bitraverse f g) fs+  bitraverse f g (TypeVar als u t args) =+    TypeVar <$> g als <*> pure u <*> pure t <*> traverse (traverse f) args+  bitraverse f g (Arrow als v t1 t2) =+    Arrow <$> g als <*> pure v <*> bitraverse f g t1 <*> bitraverse f g t2+  bitraverse f g (Sum cs) = Sum <$> (traverse . traverse) (bitraverse f g) cs++instance Bifunctor ScalarTypeBase where+  bimap = bimapDefault++instance Bifoldable ScalarTypeBase where+  bifoldMap = bifoldMapDefault++-- | An expanded Futhark type is either an array, or something that+-- can be an element of an array.  When comparing types for equality,+-- function parameter names are ignored.  This representation permits+-- some malformed types (arrays of functions), but importantly rules+-- out arrays-of-arrays.+data TypeBase dim as+  = Scalar (ScalarTypeBase dim as)+  | Array as Uniqueness (ScalarTypeBase dim ()) (ShapeDecl dim)+  deriving (Eq, Ord, Show)++instance Bitraversable TypeBase where+  bitraverse f g (Scalar t) = Scalar <$> bitraverse f g t+  bitraverse f g (Array a u t shape) =+    Array <$> g a <*> pure u <*> bitraverse f pure t <*> traverse f shape++instance Bifunctor TypeBase where+  bimap = bimapDefault++instance Bifoldable TypeBase where+  bifoldMap = bifoldMapDefault++-- | An argument passed to a type constructor.+data TypeArg dim+  = TypeArgDim dim SrcLoc+  | TypeArgType (TypeBase dim ()) SrcLoc+  deriving (Eq, Ord, Show)++instance Traversable TypeArg where+  traverse f (TypeArgDim v loc) = TypeArgDim <$> f v <*> pure loc+  traverse f (TypeArgType t loc) = TypeArgType <$> bitraverse f pure t <*> pure loc++instance Functor TypeArg where+  fmap = fmapDefault++instance Foldable TypeArg where+  foldMap = foldMapDefault++-- | A variable that is aliased.  Can be still in-scope, or have gone+-- out of scope and be free.  In the latter case, it behaves more like+-- an equivalence class.  See uniqueness-error18.fut for an example of+-- why this is necessary.+data Alias+  = AliasBound {aliasVar :: VName}+  | AliasFree {aliasVar :: VName}+  deriving (Eq, Ord, Show)++-- | Aliasing for a type, which is a set of the variables that are+-- aliased.+type Aliasing = S.Set Alias++-- | A type with aliasing information and shape annotations, used for+-- describing the type patterns and expressions.+type PatternType = TypeBase (DimDecl VName) Aliasing++-- | A "structural" type with shape annotations and no aliasing+-- information, used for declarations.+type StructType = TypeBase (DimDecl VName) ()++-- | A value type contains full, manifest size information.+type ValueType = TypeBase Int64 ()++-- | A dimension declaration expression for use in a 'TypeExp'.+data DimExp vn+  = -- | The size of the dimension is this name, which+    -- must be in scope.+    DimExpNamed (QualName vn) SrcLoc+  | -- | The size is a constant.+    DimExpConst Int SrcLoc+  | -- | No dimension declaration.+    DimExpAny+  deriving (Show)++deriving instance Eq (DimExp Name)++deriving instance Eq (DimExp VName)++deriving instance Ord (DimExp Name)++deriving instance Ord (DimExp VName)++-- | An unstructured type with type variables and possibly shape+-- declarations - this is what the user types in the source program.+-- These are used to construct 'TypeBase's in the type checker.+data TypeExp vn+  = TEVar (QualName vn) SrcLoc+  | TETuple [TypeExp vn] SrcLoc+  | TERecord [(Name, TypeExp vn)] SrcLoc+  | TEArray (TypeExp vn) (DimExp vn) SrcLoc+  | TEUnique (TypeExp vn) SrcLoc+  | TEApply (TypeExp vn) (TypeArgExp vn) SrcLoc+  | TEArrow (Maybe vn) (TypeExp vn) (TypeExp vn) SrcLoc+  | TESum [(Name, [TypeExp vn])] SrcLoc+  deriving (Show)++deriving instance Eq (TypeExp Name)++deriving instance Eq (TypeExp VName)++deriving instance Ord (TypeExp Name)++deriving instance Ord (TypeExp VName)++instance Located (TypeExp vn) where+  locOf (TEArray _ _ loc) = locOf loc+  locOf (TETuple _ loc) = locOf loc+  locOf (TERecord _ loc) = locOf loc+  locOf (TEVar _ loc) = locOf loc+  locOf (TEUnique _ loc) = locOf loc+  locOf (TEApply _ _ loc) = locOf loc+  locOf (TEArrow _ _ _ loc) = locOf loc+  locOf (TESum _ loc) = locOf loc++-- | A type argument expression passed to a type constructor.+data TypeArgExp vn+  = TypeArgExpDim (DimExp vn) SrcLoc+  | TypeArgExpType (TypeExp vn)+  deriving (Show)++deriving instance Eq (TypeArgExp Name)++deriving instance Eq (TypeArgExp VName)++deriving instance Ord (TypeArgExp Name)++deriving instance Ord (TypeArgExp VName)++instance Located (TypeArgExp vn) where+  locOf (TypeArgExpDim _ loc) = locOf loc+  locOf (TypeArgExpType t) = locOf t++-- | A declaration of the type of something.+data TypeDeclBase f vn = TypeDecl+  { -- | The type declared by the user.+    declaredType :: TypeExp vn,+    -- | The type deduced by the type checker.+    expandedType :: f StructType+  }++deriving instance Showable f vn => Show (TypeDeclBase f vn)++deriving instance Eq (TypeDeclBase NoInfo VName)++deriving instance Ord (TypeDeclBase NoInfo VName)++instance Located (TypeDeclBase f vn) where+  locOf = locOf . declaredType++-- | Information about which parts of a value/type are consumed.+data Diet+  = -- | Consumes these fields in the record.+    RecordDiet (M.Map Name Diet)+  | -- | A function that consumes its argument(s) like this.+    -- The final 'Diet' should always be 'Observe', as there+    -- is no way for a function to consume its return value.+    FuncDiet Diet Diet+  | -- | Consumes this value.+    Consume+  | -- | Only observes value in this position, does+    -- not consume.+    Observe+  deriving (Eq, Show)++-- | Simple Futhark values.  Values are fully evaluated and their type+-- is always unambiguous.+data Value+  = PrimValue !PrimValue+  | -- | It is assumed that the array is 0-indexed.  The type+    -- is the full type.+    ArrayValue !(Array Int Value) ValueType+  deriving (Eq, Show)++-- | An identifier consists of its name and the type of the value+-- bound to the identifier.+data IdentBase f vn = Ident+  { identName :: vn,+    identType :: f PatternType,+    identSrcLoc :: SrcLoc+  }++deriving instance Showable f vn => Show (IdentBase f vn)++instance Eq vn => Eq (IdentBase ty vn) where+  x == y = identName x == identName y++instance Ord vn => Ord (IdentBase ty vn) where+  compare = comparing identName++instance Located (IdentBase ty vn) where+  locOf = locOf . identSrcLoc++-- | Default binary operators.+data BinOp+  = -- | A pseudo-operator standing in for any normal+    -- identifier used as an operator (they all have the+    -- same fixity).+    -- Binary Ops for Numbers+    Backtick+  | Plus+  | Minus+  | Pow+  | Times+  | Divide+  | Mod+  | Quot+  | Rem+  | ShiftR+  | ShiftL+  | Band+  | Xor+  | Bor+  | LogAnd+  | LogOr+  | -- Relational Ops for all primitive types at least+    Equal+  | NotEqual+  | Less+  | Leq+  | Greater+  | Geq+  | -- Some functional ops.++    -- | @|>@+    PipeRight+  | -- | @<|@+    -- Misc+    PipeLeft+  deriving (Eq, Ord, Show, Enum, Bounded)++-- | Whether a bound for an end-point of a 'DimSlice' or a range+-- literal is inclusive or exclusive.+data Inclusiveness a+  = DownToExclusive a+  | -- | May be "down to" if step is negative.+    ToInclusive a+  | UpToExclusive a+  deriving (Eq, Ord, Show)++instance Located a => Located (Inclusiveness a) where+  locOf (DownToExclusive x) = locOf x+  locOf (ToInclusive x) = locOf x+  locOf (UpToExclusive x) = locOf x++instance Functor Inclusiveness where+  fmap = fmapDefault++instance Foldable Inclusiveness where+  foldMap = foldMapDefault++instance Traversable Inclusiveness where+  traverse f (DownToExclusive x) = DownToExclusive <$> f x+  traverse f (ToInclusive x) = ToInclusive <$> f x+  traverse f (UpToExclusive x) = UpToExclusive <$> f x++-- | An indexing of a single dimension.+data DimIndexBase f vn+  = DimFix (ExpBase f vn)+  | DimSlice+      (Maybe (ExpBase f vn))+      (Maybe (ExpBase f vn))+      (Maybe (ExpBase f vn))++deriving instance Showable f vn => Show (DimIndexBase f vn)++deriving instance Eq (DimIndexBase NoInfo VName)++deriving instance Ord (DimIndexBase NoInfo VName)++-- | A name qualified with a breadcrumb of module accesses.+data QualName vn = QualName+  { qualQuals :: ![vn],+    qualLeaf :: !vn+  }+  deriving (Show)++instance Eq (QualName Name) where+  QualName qs1 v1 == QualName qs2 v2 = qs1 == qs2 && v1 == v2++instance Eq (QualName VName) where+  QualName _ v1 == QualName _ v2 = v1 == v2++instance Ord (QualName Name) where+  QualName qs1 v1 `compare` QualName qs2 v2 = compare (qs1, v1) (qs2, v2)++instance Ord (QualName VName) where+  QualName _ v1 `compare` QualName _ v2 = compare v1 v2++instance Functor QualName where+  fmap = fmapDefault++instance Foldable QualName where+  foldMap = foldMapDefault++instance Traversable QualName where+  traverse f (QualName qs v) = QualName <$> traverse f qs <*> f v++-- | The Futhark expression language.+--+-- In a value of type @Exp f vn@, annotations are wrapped in the+-- functor @f@, and all names are of type @vn@.+--+-- This allows us to encode whether or not the expression has been+-- type-checked in the Haskell type of the expression.  Specifically,+-- the parser will produce expressions of type @Exp 'NoInfo' 'Name'@,+-- and the type checker will convert these to @Exp 'Info' 'VName'@, in+-- which type information is always present and all names are unique.+data ExpBase f vn+  = Literal PrimValue SrcLoc+  | -- | A polymorphic integral literal.+    IntLit Integer (f PatternType) SrcLoc+  | -- | A polymorphic decimal literal.+    FloatLit Double (f PatternType) SrcLoc+  | -- | A string literal is just a fancy syntax for an array+    -- of bytes.+    StringLit [Word8] SrcLoc+  | -- | A parenthesized expression.+    Parens (ExpBase f vn) SrcLoc+  | QualParens (QualName vn, SrcLoc) (ExpBase f vn) SrcLoc+  | -- | Tuple literals, e.g., @{1+3, {x, y+z}}@.+    TupLit [ExpBase f vn] SrcLoc+  | -- | Record literals, e.g. @{x=2,y=3,z}@.+    RecordLit [FieldBase f vn] SrcLoc+  | -- | Array literals, e.g., @[ [1+x, 3], [2, 1+4] ]@.+    -- Second arg is the row type of the rows of the array.+    ArrayLit [ExpBase f vn] (f PatternType) SrcLoc+  | Range+      (ExpBase f vn)+      (Maybe (ExpBase f vn))+      (Inclusiveness (ExpBase f vn))+      (f PatternType, f [VName])+      SrcLoc+  | Var (QualName vn) (f PatternType) SrcLoc+  | -- | Type ascription: @e : t@.+    Ascript (ExpBase f vn) (TypeDeclBase f vn) SrcLoc+  | -- | Size coercion: @e :> t@.+    Coerce (ExpBase f vn) (TypeDeclBase f vn) (f PatternType, f [VName]) SrcLoc+  | LetPat+      (PatternBase f vn)+      (ExpBase f vn)+      (ExpBase f vn)+      (f PatternType, f [VName])+      SrcLoc+  | LetFun+      vn+      ( [TypeParamBase vn],+        [PatternBase f vn],+        Maybe (TypeExp vn),+        f StructType,+        ExpBase f vn+      )+      (ExpBase f vn)+      (f PatternType)+      SrcLoc+  | If (ExpBase f vn) (ExpBase f vn) (ExpBase f vn) (f PatternType, f [VName]) SrcLoc+  | -- | The @Maybe VName@ is a possible existential size+    -- that is instantiated by this argument..+    --+    -- The @[VName]@ are the existential sizes that come+    -- into being at this call site.+    Apply+      (ExpBase f vn)+      (ExpBase f vn)+      (f (Diet, Maybe VName))+      (f PatternType, f [VName])+      SrcLoc+  | -- | Numeric negation (ugly special case; Haskell did it first).+    Negate (ExpBase f vn) SrcLoc+  | Lambda+      [PatternBase f vn]+      (ExpBase f vn)+      (Maybe (TypeExp vn))+      (f (Aliasing, StructType))+      SrcLoc+  | -- | @+@; first two types are operands, third is result.+    OpSection (QualName vn) (f PatternType) SrcLoc+  | -- | @2+@; first type is operand, second is result.+    OpSectionLeft+      (QualName vn)+      (f PatternType)+      (ExpBase f vn)+      (f (StructType, Maybe VName), f StructType)+      (f PatternType, f [VName])+      SrcLoc+  | -- | @+2@; first type is operand, second is result.+    OpSectionRight+      (QualName vn)+      (f PatternType)+      (ExpBase f vn)+      (f StructType, f (StructType, Maybe VName))+      (f PatternType)+      SrcLoc+  | -- | Field projection as a section: @(.x.y.z)@.+    ProjectSection [Name] (f PatternType) SrcLoc+  | -- | Array indexing as a section: @(.[i,j])@.+    IndexSection [DimIndexBase f vn] (f PatternType) SrcLoc+  | DoLoop+      [VName] -- Size parameters.+      (PatternBase f vn) -- Merge variable pattern.+      (ExpBase f vn) -- Initial values of merge variables.+      (LoopFormBase f vn) -- Do or while loop.+      (ExpBase f vn) -- Loop body.+      (f (PatternType, [VName])) -- Return type.+      SrcLoc+  | BinOp+      (QualName vn, SrcLoc)+      (f PatternType)+      (ExpBase f vn, f (StructType, Maybe VName))+      (ExpBase f vn, f (StructType, Maybe VName))+      (f PatternType)+      (f [VName])+      SrcLoc+  | Project Name (ExpBase f vn) (f PatternType) SrcLoc+  | -- Primitive array operations+    LetWith+      (IdentBase f vn)+      (IdentBase f vn)+      [DimIndexBase f vn]+      (ExpBase f vn)+      (ExpBase f vn)+      (f PatternType)+      SrcLoc+  | Index (ExpBase f vn) [DimIndexBase f vn] (f PatternType, f [VName]) SrcLoc+  | Update (ExpBase f vn) [DimIndexBase f vn] (ExpBase f vn) SrcLoc+  | RecordUpdate (ExpBase f vn) [Name] (ExpBase f vn) (f PatternType) SrcLoc+  | -- | Fail if the first expression does not return true,+    -- and return the value of the second expression if it+    -- does.+    Assert (ExpBase f vn) (ExpBase f vn) (f String) SrcLoc+  | -- | An n-ary value constructor.+    Constr Name [ExpBase f vn] (f PatternType) SrcLoc+  | -- | A match expression.+    Match+      (ExpBase f vn)+      (NE.NonEmpty (CaseBase f vn))+      (f PatternType, f [VName])+      SrcLoc+  | -- | An attribute applied to the following expression.+    Attr AttrInfo (ExpBase f vn) SrcLoc++deriving instance Showable f vn => Show (ExpBase f vn)++deriving instance Eq (ExpBase NoInfo VName)++deriving instance Ord (ExpBase NoInfo VName)++instance Located (ExpBase f vn) where+  locOf (Literal _ loc) = locOf loc+  locOf (IntLit _ _ loc) = locOf loc+  locOf (FloatLit _ _ loc) = locOf loc+  locOf (Parens _ loc) = locOf loc+  locOf (QualParens _ _ loc) = locOf loc+  locOf (TupLit _ pos) = locOf pos+  locOf (RecordLit _ pos) = locOf pos+  locOf (Project _ _ _ pos) = locOf pos+  locOf (ArrayLit _ _ pos) = locOf pos+  locOf (StringLit _ loc) = locOf loc+  locOf (Range _ _ _ _ pos) = locOf pos+  locOf (BinOp _ _ _ _ _ _ loc) = locOf loc+  locOf (If _ _ _ _ pos) = locOf pos+  locOf (Var _ _ loc) = locOf loc+  locOf (Ascript _ _ loc) = locOf loc+  locOf (Coerce _ _ _ loc) = locOf loc+  locOf (Negate _ pos) = locOf pos+  locOf (Apply _ _ _ _ loc) = locOf loc+  locOf (LetPat _ _ _ _ loc) = locOf loc+  locOf (LetFun _ _ _ _ loc) = locOf loc+  locOf (LetWith _ _ _ _ _ _ loc) = locOf loc+  locOf (Index _ _ _ loc) = locOf loc+  locOf (Update _ _ _ pos) = locOf pos+  locOf (RecordUpdate _ _ _ _ pos) = locOf pos+  locOf (Lambda _ _ _ _ loc) = locOf loc+  locOf (OpSection _ _ loc) = locOf loc+  locOf (OpSectionLeft _ _ _ _ _ loc) = locOf loc+  locOf (OpSectionRight _ _ _ _ _ loc) = locOf loc+  locOf (ProjectSection _ _ loc) = locOf loc+  locOf (IndexSection _ _ loc) = locOf loc+  locOf (DoLoop _ _ _ _ _ _ loc) = locOf loc+  locOf (Assert _ _ _ loc) = locOf loc+  locOf (Constr _ _ _ loc) = locOf loc+  locOf (Match _ _ _ loc) = locOf loc+  locOf (Attr _ _ loc) = locOf loc++-- | An entry in a record literal.+data FieldBase f vn+  = RecordFieldExplicit Name (ExpBase f vn) SrcLoc+  | RecordFieldImplicit vn (f PatternType) SrcLoc++deriving instance Showable f vn => Show (FieldBase f vn)++deriving instance Eq (FieldBase NoInfo VName)++deriving instance Ord (FieldBase NoInfo VName)++instance Located (FieldBase f vn) where+  locOf (RecordFieldExplicit _ _ loc) = locOf loc+  locOf (RecordFieldImplicit _ _ loc) = locOf loc++-- | A case in a match expression.+data CaseBase f vn = CasePat (PatternBase f vn) (ExpBase f vn) SrcLoc++deriving instance Showable f vn => Show (CaseBase f vn)++deriving instance Eq (CaseBase NoInfo VName)++deriving instance Ord (CaseBase NoInfo VName)++instance Located (CaseBase f vn) where+  locOf (CasePat _ _ loc) = locOf loc++-- | Whether the loop is a @for@-loop or a @while@-loop.+data LoopFormBase f vn+  = For (IdentBase f vn) (ExpBase f vn)+  | ForIn (PatternBase f vn) (ExpBase f vn)+  | While (ExpBase f vn)++deriving instance Showable f vn => Show (LoopFormBase f vn)++deriving instance Eq (LoopFormBase NoInfo VName)++deriving instance Ord (LoopFormBase NoInfo VName)++-- | A pattern as used most places where variables are bound (function+-- parameters, @let@ expressions, etc).+data PatternBase f vn+  = TuplePattern [PatternBase f vn] SrcLoc+  | RecordPattern [(Name, PatternBase f vn)] SrcLoc+  | PatternParens (PatternBase f vn) SrcLoc+  | Id vn (f PatternType) SrcLoc+  | Wildcard (f PatternType) SrcLoc -- Nothing, i.e. underscore.+  | PatternAscription (PatternBase f vn) (TypeDeclBase f vn) SrcLoc+  | PatternLit (ExpBase f vn) (f PatternType) SrcLoc+  | PatternConstr Name (f PatternType) [PatternBase f vn] SrcLoc++deriving instance Showable f vn => Show (PatternBase f vn)++deriving instance Eq (PatternBase NoInfo VName)++deriving instance Ord (PatternBase NoInfo VName)++instance Located (PatternBase f vn) where+  locOf (TuplePattern _ loc) = locOf loc+  locOf (RecordPattern _ loc) = locOf loc+  locOf (PatternParens _ loc) = locOf loc+  locOf (Id _ _ loc) = locOf loc+  locOf (Wildcard _ loc) = locOf loc+  locOf (PatternAscription _ _ loc) = locOf loc+  locOf (PatternLit _ _ loc) = locOf loc+  locOf (PatternConstr _ _ _ loc) = locOf loc++-- | Documentation strings, including source location.+data DocComment = DocComment String SrcLoc+  deriving (Show)++instance Located DocComment where+  locOf (DocComment _ loc) = locOf loc++-- | Part of the type of an entry point.  Has an actual type, and+-- maybe also an ascribed type expression.+data EntryType = EntryType+  { entryType :: StructType,+    entryAscribed :: Maybe (TypeExp VName)+  }+  deriving (Show)++-- | Information about the external interface exposed by an entry+-- point.  The important thing is that that we remember the original+-- source-language types, without desugaring them at all.  The+-- annoying thing is that we do not require type annotations on entry+-- points, so the types can be either ascribed or inferred.+data EntryPoint = EntryPoint+  { entryParams :: [EntryType],+    entryReturn :: EntryType+  }+  deriving (Show)++-- | Function Declarations+data ValBindBase f vn = ValBind+  { -- | Just if this function is an entry point.  If so, it also+    -- contains the externally visible interface.  Note that this may not+    -- strictly be well-typed after some desugaring operations, as it+    -- may refer to abstract types that are no longer in scope.+    valBindEntryPoint :: Maybe (f EntryPoint),+    valBindName :: vn,+    valBindRetDecl :: Maybe (TypeExp vn),+    valBindRetType :: f (StructType, [VName]),+    valBindTypeParams :: [TypeParamBase vn],+    valBindParams :: [PatternBase f vn],+    valBindBody :: ExpBase f vn,+    valBindDoc :: Maybe DocComment,+    valBindAttrs :: [AttrInfo],+    valBindLocation :: SrcLoc+  }++deriving instance Showable f vn => Show (ValBindBase f vn)++instance Located (ValBindBase f vn) where+  locOf = locOf . valBindLocation++-- | Type Declarations+data TypeBindBase f vn = TypeBind+  { typeAlias :: vn,+    typeLiftedness :: Liftedness,+    typeParams :: [TypeParamBase vn],+    typeExp :: TypeDeclBase f vn,+    typeDoc :: Maybe DocComment,+    typeBindLocation :: SrcLoc+  }++deriving instance Showable f vn => Show (TypeBindBase f vn)++instance Located (TypeBindBase f vn) where+  locOf = locOf . typeBindLocation++-- | The liftedness of a type parameter.  By the @Ord@ instance,+-- @Unlifted < SizeLifted < Lifted@.+data Liftedness+  = -- | May only be instantiated with a zero-order type of (possibly+    -- symbolically) known size.+    Unlifted+  | -- | May only be instantiated with a zero-order type, but the size+    -- can be varying.+    SizeLifted+  | -- | May be instantiated with a functional type.+    Lifted+  deriving (Eq, Ord, Show)++-- | A type parameter.+data TypeParamBase vn+  = -- | A type parameter that must be a size.+    TypeParamDim vn SrcLoc+  | -- | A type parameter that must be a type.+    TypeParamType Liftedness vn SrcLoc+  deriving (Eq, Ord, Show)++instance Functor TypeParamBase where+  fmap = fmapDefault++instance Foldable TypeParamBase where+  foldMap = foldMapDefault++instance Traversable TypeParamBase where+  traverse f (TypeParamDim v loc) = TypeParamDim <$> f v <*> pure loc+  traverse f (TypeParamType l v loc) = TypeParamType l <$> f v <*> pure loc++instance Located (TypeParamBase vn) where+  locOf (TypeParamDim _ loc) = locOf loc+  locOf (TypeParamType _ _ loc) = locOf loc++-- | The name of a type parameter.+typeParamName :: TypeParamBase vn -> vn+typeParamName (TypeParamDim v _) = v+typeParamName (TypeParamType _ v _) = v++-- | A spec is a component of a module type.+data SpecBase f vn+  = ValSpec+      { specName :: vn,+        specTypeParams :: [TypeParamBase vn],+        specType :: TypeDeclBase f vn,+        specDoc :: Maybe DocComment,+        specLocation :: SrcLoc+      }+  | TypeAbbrSpec (TypeBindBase f vn)+  | -- | Abstract type.+    TypeSpec Liftedness vn [TypeParamBase vn] (Maybe DocComment) SrcLoc+  | ModSpec vn (SigExpBase f vn) (Maybe DocComment) SrcLoc+  | IncludeSpec (SigExpBase f vn) SrcLoc++deriving instance Showable f vn => Show (SpecBase f vn)++instance Located (SpecBase f vn) where+  locOf (ValSpec _ _ _ _ loc) = locOf loc+  locOf (TypeAbbrSpec tbind) = locOf tbind+  locOf (TypeSpec _ _ _ _ loc) = locOf loc+  locOf (ModSpec _ _ _ loc) = locOf loc+  locOf (IncludeSpec _ loc) = locOf loc++-- | A module type expression.+data SigExpBase f vn+  = SigVar (QualName vn) (f (M.Map VName VName)) SrcLoc+  | SigParens (SigExpBase f vn) SrcLoc+  | SigSpecs [SpecBase f vn] SrcLoc+  | SigWith (SigExpBase f vn) (TypeRefBase f vn) SrcLoc+  | SigArrow (Maybe vn) (SigExpBase f vn) (SigExpBase f vn) SrcLoc++deriving instance Showable f vn => Show (SigExpBase f vn)++-- | A type refinement.+data TypeRefBase f vn = TypeRef (QualName vn) [TypeParamBase vn] (TypeDeclBase f vn) SrcLoc++deriving instance Showable f vn => Show (TypeRefBase f vn)++instance Located (TypeRefBase f vn) where+  locOf (TypeRef _ _ _ loc) = locOf loc++instance Located (SigExpBase f vn) where+  locOf (SigVar _ _ loc) = locOf loc+  locOf (SigParens _ loc) = locOf loc+  locOf (SigSpecs _ loc) = locOf loc+  locOf (SigWith _ _ loc) = locOf loc+  locOf (SigArrow _ _ _ loc) = locOf loc++-- | Module type binding.+data SigBindBase f vn = SigBind+  { sigName :: vn,+    sigExp :: SigExpBase f vn,+    sigDoc :: Maybe DocComment,+    sigLoc :: SrcLoc+  }++deriving instance Showable f vn => Show (SigBindBase f vn)++instance Located (SigBindBase f vn) where+  locOf = locOf . sigLoc++-- | Module expression.+data ModExpBase f vn+  = ModVar (QualName vn) SrcLoc+  | ModParens (ModExpBase f vn) SrcLoc+  | -- | The contents of another file as a module.+    ModImport FilePath (f FilePath) SrcLoc+  | ModDecs [DecBase f vn] SrcLoc+  | -- | Functor application.  The first mapping is from parameter+    -- names to argument names, while the second maps names in the+    -- constructed module to the names inside the functor.+    ModApply+      (ModExpBase f vn)+      (ModExpBase f vn)+      (f (M.Map VName VName))+      (f (M.Map VName VName))+      SrcLoc+  | ModAscript (ModExpBase f vn) (SigExpBase f vn) (f (M.Map VName VName)) SrcLoc+  | ModLambda+      (ModParamBase f vn)+      (Maybe (SigExpBase f vn, f (M.Map VName VName)))+      (ModExpBase f vn)+      SrcLoc++deriving instance Showable f vn => Show (ModExpBase f vn)++instance Located (ModExpBase f vn) where+  locOf (ModVar _ loc) = locOf loc+  locOf (ModParens _ loc) = locOf loc+  locOf (ModImport _ _ loc) = locOf loc+  locOf (ModDecs _ loc) = locOf loc+  locOf (ModApply _ _ _ _ loc) = locOf loc+  locOf (ModAscript _ _ _ loc) = locOf loc+  locOf (ModLambda _ _ _ loc) = locOf loc++-- | A module binding.+data ModBindBase f vn = ModBind+  { modName :: vn,+    modParams :: [ModParamBase f vn],+    modSignature :: Maybe (SigExpBase f vn, f (M.Map VName VName)),+    modExp :: ModExpBase f vn,+    modDoc :: Maybe DocComment,+    modLocation :: SrcLoc+  }++deriving instance Showable f vn => Show (ModBindBase f vn)++instance Located (ModBindBase f vn) where+  locOf = locOf . modLocation++-- | A module parameter.+data ModParamBase f vn = ModParam+  { modParamName :: vn,+    modParamType :: SigExpBase f vn,+    modParamAbs :: f [VName],+    modParamLocation :: SrcLoc+  }++deriving instance Showable f vn => Show (ModParamBase f vn)++instance Located (ModParamBase f vn) where+  locOf = locOf . modParamLocation++-- | A top-level binding.+data DecBase f vn+  = ValDec (ValBindBase f vn)+  | TypeDec (TypeBindBase f vn)+  | SigDec (SigBindBase f vn)+  | ModDec (ModBindBase f vn)+  | OpenDec (ModExpBase f vn) SrcLoc+  | LocalDec (DecBase f vn) SrcLoc+  | ImportDec FilePath (f FilePath) SrcLoc++deriving instance Showable f vn => Show (DecBase f vn)++instance Located (DecBase f vn) where+  locOf (ValDec d) = locOf d+  locOf (TypeDec d) = locOf d+  locOf (SigDec d) = locOf d+  locOf (ModDec d) = locOf d+  locOf (OpenDec _ loc) = locOf loc+  locOf (LocalDec _ loc) = locOf loc+  locOf (ImportDec _ _ loc) = locOf loc++-- | The program described by a single Futhark file.  May depend on+-- other files.+data ProgBase f vn = Prog+  { progDoc :: Maybe DocComment,+    progDecs :: [DecBase f vn]+  }++deriving instance Showable f vn => Show (ProgBase f vn)++--- Some prettyprinting definitions are here because we need them in+--- the Attributes module.++instance Pretty PrimType where+  ppr (Unsigned Int8) = text "u8"+  ppr (Unsigned Int16) = text "u16"+  ppr (Unsigned Int32) = text "u32"+  ppr (Unsigned Int64) = text "u64"+  ppr (Signed t) = ppr t+  ppr (FloatType t) = ppr t+  ppr Bool = text "bool"++instance Pretty BinOp where+  ppr Backtick = text "``"+  ppr Plus = text "+"+  ppr Minus = text "-"+  ppr Pow = text "**"+  ppr Times = text "*"+  ppr Divide = text "/"+  ppr Mod = text "%"+  ppr Quot = text "//"+  ppr Rem = text "%%"+  ppr ShiftR = text ">>"+  ppr ShiftL = text "<<"+  ppr Band = text "&"+  ppr Xor = text "^"+  ppr Bor = text "|"+  ppr LogAnd = text "&&"+  ppr LogOr = text "||"+  ppr Equal = text "=="+  ppr NotEqual = text "!="+  ppr Less = text "<"+  ppr Leq = text "<="+  ppr Greater = text ">"+  ppr Geq = text ">="+  ppr PipeLeft = text "<|"   ppr PipeRight = text "|>"
src/Language/Futhark/Traversals.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleInstances #-}+ -- | -- -- Functions for generic traversals across Futhark syntax trees.  The@@ -20,36 +21,38 @@ -- functions expressing the operations to be performed on the various -- types of nodes. module Language.Futhark.Traversals-  ( ASTMapper(..)-  , ASTMappable(..)-  , identityMapper-  , bareExp-  ) where--import qualified Data.Set                as S-import qualified Data.List.NonEmpty               as NE+  ( ASTMapper (..),+    ASTMappable (..),+    identityMapper,+    bareExp,+  )+where -import           Language.Futhark.Syntax+import qualified Data.List.NonEmpty as NE+import qualified Data.Set as S+import Language.Futhark.Syntax  -- | Express a monad mapping operation on a syntax node.  Each element -- of this structure expresses the operation to be performed on a -- given child.-data ASTMapper m = ASTMapper {-    mapOnExp         :: ExpBase Info VName -> m (ExpBase Info VName)-  , mapOnName        :: VName -> m VName-  , mapOnQualName    :: QualName VName -> m (QualName VName)-  , mapOnStructType  :: StructType -> m StructType-  , mapOnPatternType :: PatternType -> m PatternType+data ASTMapper m = ASTMapper+  { mapOnExp :: ExpBase Info VName -> m (ExpBase Info VName),+    mapOnName :: VName -> m VName,+    mapOnQualName :: QualName VName -> m (QualName VName),+    mapOnStructType :: StructType -> m StructType,+    mapOnPatternType :: PatternType -> m PatternType   }  -- | An 'ASTMapper' that just leaves its input unchanged. identityMapper :: Monad m => ASTMapper m-identityMapper = ASTMapper { mapOnExp = return-                           , mapOnName = return-                           , mapOnQualName = return-                           , mapOnStructType = return-                           , mapOnPatternType = return-                           }+identityMapper =+  ASTMapper+    { mapOnExp = return,+      mapOnName = return,+      mapOnQualName = return,+      mapOnStructType = return,+      mapOnPatternType = return+    }  -- | The class of things that we can map an 'ASTMapper' across. class ASTMappable x where@@ -61,8 +64,8 @@  instance ASTMappable (ExpBase Info VName) where   astMap tv (Var name t loc) =-    Var <$> mapOnQualName tv name <*> traverse (mapOnPatternType tv) t <*>-    pure loc+    Var <$> mapOnQualName tv name <*> traverse (mapOnPatternType tv) t+      <*> pure loc   astMap _ (Literal val loc) =     pure $ Literal val loc   astMap _ (StringLit vs loc) =@@ -74,8 +77,9 @@   astMap tv (Parens e loc) =     Parens <$> mapOnExp tv e <*> pure loc   astMap tv (QualParens (name, nameloc) e loc) =-    QualParens <$> ((,) <$> mapOnQualName tv name <*> pure nameloc) <*>-    mapOnExp tv e <*> pure loc+    QualParens <$> ((,) <$> mapOnQualName tv name <*> pure nameloc)+      <*> mapOnExp tv e+      <*> pure loc   astMap tv (TupLit els loc) =     TupLit <$> mapM (mapOnExp tv) els <*> pure loc   astMap tv (RecordLit fields loc) =@@ -83,99 +87,134 @@   astMap tv (ArrayLit els t loc) =     ArrayLit <$> mapM (mapOnExp tv) els <*> traverse (mapOnPatternType tv) t <*> pure loc   astMap tv (Range start next end (t, ext) loc) =-    Range <$> mapOnExp tv start <*> traverse (mapOnExp tv) next <*>-    traverse (mapOnExp tv) end <*>-    ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext) <*> pure loc+    Range <$> mapOnExp tv start <*> traverse (mapOnExp tv) next+      <*> traverse (mapOnExp tv) end+      <*> ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext)+      <*> pure loc   astMap tv (Ascript e tdecl loc) =     Ascript <$> mapOnExp tv e <*> astMap tv tdecl <*> pure loc   astMap tv (Coerce e tdecl (t, ext) loc) =-    Coerce <$> mapOnExp tv e <*> astMap tv tdecl <*>-    ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext) <*> pure loc-  astMap tv (BinOp (fname, fname_loc) t (x,Info(xt,xext)) (y,Info(yt,yext)) (Info rt) ext loc) =-    BinOp <$> ((,) <$> mapOnQualName tv fname <*> pure fname_loc) <*>-    traverse (mapOnPatternType tv) t <*>-    ((,) <$> mapOnExp tv x <*>-     (Info <$> ((,) <$> mapOnStructType tv xt <*> pure xext))) <*>-    ((,) <$> mapOnExp tv y <*>-     (Info <$> ((,) <$> mapOnStructType tv yt <*> pure yext))) <*>-    (Info <$> mapOnPatternType tv rt) <*> pure ext <*> pure loc+    Coerce <$> mapOnExp tv e <*> astMap tv tdecl+      <*> ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext)+      <*> pure loc+  astMap tv (BinOp (fname, fname_loc) t (x, Info (xt, xext)) (y, Info (yt, yext)) (Info rt) ext loc) =+    BinOp <$> ((,) <$> mapOnQualName tv fname <*> pure fname_loc)+      <*> traverse (mapOnPatternType tv) t+      <*> ( (,) <$> mapOnExp tv x+              <*> (Info <$> ((,) <$> mapOnStructType tv xt <*> pure xext))+          )+      <*> ( (,) <$> mapOnExp tv y+              <*> (Info <$> ((,) <$> mapOnStructType tv yt <*> pure yext))+          )+      <*> (Info <$> mapOnPatternType tv rt)+      <*> pure ext+      <*> pure loc   astMap tv (Negate x loc) =     Negate <$> mapOnExp tv x <*> pure loc   astMap tv (If c texp fexp (t, ext) loc) =-    If <$> mapOnExp tv c <*> mapOnExp tv texp <*> mapOnExp tv fexp <*>-    ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext) <*> pure loc+    If <$> mapOnExp tv c <*> mapOnExp tv texp <*> mapOnExp tv fexp+      <*> ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext)+      <*> pure loc   astMap tv (Apply f arg d (Info t, ext) loc) =-    Apply <$> mapOnExp tv f <*> mapOnExp tv arg <*> pure d <*>-    ((,) <$> (Info <$> mapOnPatternType tv t) <*> pure ext) <*> pure loc+    Apply <$> mapOnExp tv f <*> mapOnExp tv arg <*> pure d+      <*> ((,) <$> (Info <$> mapOnPatternType tv t) <*> pure ext)+      <*> pure loc   astMap tv (LetPat pat e body (t, ext) loc) =-    LetPat <$> astMap tv pat <*> mapOnExp tv e <*> mapOnExp tv body <*>-    ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext) <*> pure loc+    LetPat <$> astMap tv pat <*> mapOnExp tv e <*> mapOnExp tv body+      <*> ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext)+      <*> pure loc   astMap tv (LetFun name (fparams, params, ret, t, e) body body_t loc) =-    LetFun <$> mapOnName tv name <*>-    ((,,,,) <$> mapM (astMap tv) fparams <*> mapM (astMap tv) params <*>-     traverse (astMap tv) ret <*> traverse (mapOnStructType tv) t <*>-     mapOnExp tv e) <*>-    mapOnExp tv body <*> traverse (mapOnPatternType tv) body_t <*> pure loc+    LetFun <$> mapOnName tv name+      <*> ( (,,,,) <$> mapM (astMap tv) fparams <*> mapM (astMap tv) params+              <*> traverse (astMap tv) ret+              <*> traverse (mapOnStructType tv) t+              <*> mapOnExp tv e+          )+      <*> mapOnExp tv body+      <*> traverse (mapOnPatternType tv) body_t+      <*> pure loc   astMap tv (LetWith dest src idxexps vexp body t loc) =-    LetWith <$>-    astMap tv dest <*> astMap tv src <*>-    mapM (astMap tv) idxexps <*> mapOnExp tv vexp <*>-    mapOnExp tv body <*> traverse (mapOnPatternType tv) t <*> pure loc+    LetWith+      <$> astMap tv dest+      <*> astMap tv src+      <*> mapM (astMap tv) idxexps+      <*> mapOnExp tv vexp+      <*> mapOnExp tv body+      <*> traverse (mapOnPatternType tv) t+      <*> pure loc   astMap tv (Update src slice v loc) =-    Update <$> mapOnExp tv src <*> mapM (astMap tv) slice <*>-    mapOnExp tv v <*> pure loc+    Update <$> mapOnExp tv src <*> mapM (astMap tv) slice+      <*> mapOnExp tv v+      <*> pure loc   astMap tv (RecordUpdate src fs v (Info t) loc) =-    RecordUpdate <$> mapOnExp tv src <*> pure fs <*>-    mapOnExp tv v <*> (Info <$> mapOnPatternType tv t) <*> pure loc+    RecordUpdate <$> mapOnExp tv src <*> pure fs+      <*> mapOnExp tv v+      <*> (Info <$> mapOnPatternType tv t)+      <*> pure loc   astMap tv (Project field e t loc) =     Project field <$> mapOnExp tv e <*> traverse (mapOnPatternType tv) t <*> pure loc   astMap tv (Index arr idxexps (t, ext) loc) =-    Index <$> mapOnExp tv arr <*> mapM (astMap tv) idxexps <*>-    ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext) <*> pure loc+    Index <$> mapOnExp tv arr <*> mapM (astMap tv) idxexps+      <*> ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext)+      <*> pure loc   astMap tv (Assert e1 e2 desc loc) =     Assert <$> mapOnExp tv e1 <*> mapOnExp tv e2 <*> pure desc <*> pure loc   astMap tv (Lambda params body ret t loc) =-    Lambda <$> mapM (astMap tv) params <*>-    mapOnExp tv body <*> traverse (astMap tv) ret <*>-    traverse (traverse $ mapOnStructType tv) t <*> pure loc+    Lambda <$> mapM (astMap tv) params+      <*> mapOnExp tv body+      <*> traverse (astMap tv) ret+      <*> traverse (traverse $ mapOnStructType tv) t+      <*> pure loc   astMap tv (OpSection name t loc) =-    OpSection <$> mapOnQualName tv name <*>-    traverse (mapOnPatternType tv) t <*> pure loc+    OpSection <$> mapOnQualName tv name+      <*> traverse (mapOnPatternType tv) t+      <*> pure loc   astMap tv (OpSectionLeft name t arg (Info (t1a, argext), t1b) (t2, retext) loc) =-    OpSectionLeft <$> mapOnQualName tv name <*>-    traverse (mapOnPatternType tv) t <*> mapOnExp tv arg <*>-    ((,) <$>-     (Info <$> ((,) <$> mapOnStructType tv t1a <*> pure argext)) <*>-     traverse (mapOnStructType tv) t1b) <*>-    ((,) <$> traverse (mapOnPatternType tv) t2 <*> pure retext) <*> pure loc-  astMap tv (OpSectionRight name t arg (t1a, Info (t1b,argext)) t2 loc) =-    OpSectionRight <$> mapOnQualName tv name <*>-    traverse (mapOnPatternType tv) t <*> mapOnExp tv arg <*>-    ((,) <$>-     traverse (mapOnStructType tv) t1a <*>-     (Info <$> ((,) <$> mapOnStructType tv t1b <*> pure argext))) <*>-    traverse (mapOnPatternType tv) t2 <*> pure loc+    OpSectionLeft <$> mapOnQualName tv name+      <*> traverse (mapOnPatternType tv) t+      <*> mapOnExp tv arg+      <*> ( (,)+              <$> (Info <$> ((,) <$> mapOnStructType tv t1a <*> pure argext))+              <*> traverse (mapOnStructType tv) t1b+          )+      <*> ((,) <$> traverse (mapOnPatternType tv) t2 <*> pure retext)+      <*> pure loc+  astMap tv (OpSectionRight name t arg (t1a, Info (t1b, argext)) t2 loc) =+    OpSectionRight <$> mapOnQualName tv name+      <*> traverse (mapOnPatternType tv) t+      <*> mapOnExp tv arg+      <*> ( (,)+              <$> traverse (mapOnStructType tv) t1a+              <*> (Info <$> ((,) <$> mapOnStructType tv t1b <*> pure argext))+          )+      <*> traverse (mapOnPatternType tv) t2+      <*> pure loc   astMap tv (ProjectSection fields t loc) =     ProjectSection fields <$> traverse (mapOnPatternType tv) t <*> pure loc   astMap tv (IndexSection idxs t loc) =-    IndexSection <$> mapM (astMap tv) idxs <*>-    traverse (mapOnPatternType tv) t <*> pure loc+    IndexSection <$> mapM (astMap tv) idxs+      <*> traverse (mapOnPatternType tv) t+      <*> pure loc   astMap tv (DoLoop sparams mergepat mergeexp form loopbody (Info (ret, ext)) loc) =-    DoLoop <$> mapM (mapOnName tv) sparams <*> astMap tv mergepat <*>-    mapOnExp tv mergeexp <*> astMap tv form <*> mapOnExp tv loopbody <*>-    (Info <$> ((,) <$> mapOnPatternType tv ret <*> pure ext)) <*> pure loc+    DoLoop <$> mapM (mapOnName tv) sparams <*> astMap tv mergepat+      <*> mapOnExp tv mergeexp+      <*> astMap tv form+      <*> mapOnExp tv loopbody+      <*> (Info <$> ((,) <$> mapOnPatternType tv ret <*> pure ext))+      <*> pure loc   astMap tv (Constr name es ts loc) =     Constr name <$> traverse (mapOnExp tv) es <*> traverse (mapOnPatternType tv) ts <*> pure loc   astMap tv (Match e cases (t, ext) loc) =     Match <$> mapOnExp tv e <*> astMap tv cases-          <*> ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext) <*> pure loc+      <*> ((,) <$> traverse (mapOnPatternType tv) t <*> pure ext)+      <*> pure loc   astMap tv (Attr attr e loc) =     Attr attr <$> mapOnExp tv e <*> pure loc  instance ASTMappable (LoopFormBase Info VName) where   astMap tv (For i bound) = For <$> astMap tv i <*> astMap tv bound   astMap tv (ForIn pat e) = ForIn <$> astMap tv pat <*> mapOnExp tv e-  astMap tv (While e)     = While <$> mapOnExp tv e+  astMap tv (While e) = While <$> mapOnExp tv e  instance ASTMappable (TypeExp VName) where   astMap tv (TEVar qn loc) = TEVar <$> mapOnQualName tv qn <*> pure loc@@ -206,8 +245,8 @@  instance ASTMappable (DimDecl VName) where   astMap tv (NamedDim vn) = NamedDim <$> mapOnQualName tv vn-  astMap _ (ConstDim k)   = pure $ ConstDim k-  astMap _ AnyDim         = pure AnyDim+  astMap _ (ConstDim k) = pure $ ConstDim k+  astMap _ AnyDim = pure AnyDim  instance ASTMappable (TypeParamBase VName) where   astMap = traverse . mapOnName@@ -215,10 +254,10 @@ instance ASTMappable (DimIndexBase Info VName) where   astMap tv (DimFix j) = DimFix <$> astMap tv j   astMap tv (DimSlice i j stride) =-    DimSlice <$>-    maybe (return Nothing) (fmap Just . astMap tv) i <*>-    maybe (return Nothing) (fmap Just . astMap tv) j <*>-    maybe (return Nothing) (fmap Just . astMap tv) stride+    DimSlice+      <$> maybe (return Nothing) (fmap Just . astMap tv) i+      <*> maybe (return Nothing) (fmap Just . astMap tv) j+      <*> maybe (return Nothing) (fmap Just . astMap tv) stride  instance ASTMappable Alias where   astMap tv (AliasBound v) = AliasBound <$> mapOnName tv v@@ -228,11 +267,15 @@   astMap tv = fmap S.fromList . traverse (astMap tv) . S.toList  type TypeTraverser f t dim1 als1 dim2 als2 =-  (TypeName -> f TypeName) -> (dim1 -> f dim2) -> (als1 -> f als2) ->-  t dim1 als1 -> f (t dim2 als2)+  (TypeName -> f TypeName) ->+  (dim1 -> f dim2) ->+  (als1 -> f als2) ->+  t dim1 als1 ->+  f (t dim2 als2) -traverseScalarType :: Applicative f =>-                      TypeTraverser f ScalarTypeBase dim1 als1 dims als2+traverseScalarType ::+  Applicative f =>+  TypeTraverser f ScalarTypeBase dim1 als1 dims als2 traverseScalarType _ _ _ (Prim t) = pure $ Prim t traverseScalarType f g h (Record fs) = Record <$> traverse (traverseType f g h) fs traverseScalarType f g h (TypeVar als u t args) =@@ -242,17 +285,22 @@ traverseScalarType f g h (Sum cs) =   Sum <$> (traverse . traverse) (traverseType f g h) cs -traverseType :: Applicative f =>-                TypeTraverser f TypeBase dim1 als1 dims als2+traverseType ::+  Applicative f =>+  TypeTraverser f TypeBase dim1 als1 dims als2 traverseType f g h (Array als u et shape) =-  Array <$> h als <*> pure u <*>-  traverseScalarType f g pure et <*> traverse g shape+  Array <$> h als <*> pure u+    <*> traverseScalarType f g pure et+    <*> traverse g shape traverseType f g h (Scalar t) =   Scalar <$> traverseScalarType f g h t -traverseTypeArg :: Applicative f =>-                   (TypeName -> f TypeName) -> (dim1 -> f dim2)-                -> TypeArg dim1 -> f (TypeArg dim2)+traverseTypeArg ::+  Applicative f =>+  (TypeName -> f TypeName) ->+  (dim1 -> f dim2) ->+  TypeArg dim1 ->+  f (TypeArg dim2) traverseTypeArg _ g (TypeArgDim d loc) =   TypeArgDim <$> g d <*> pure loc traverseTypeArg f g (TypeArgType t loc) =@@ -260,11 +308,13 @@  instance ASTMappable StructType where   astMap tv = traverseType f (astMap tv) pure-    where f = fmap typeNameFromQualName . mapOnQualName tv . qualNameFromTypeName+    where+      f = fmap typeNameFromQualName . mapOnQualName tv . qualNameFromTypeName  instance ASTMappable PatternType where   astMap tv = traverseType f (astMap tv) (astMap tv)-    where f = fmap typeNameFromQualName . mapOnQualName tv . qualNameFromTypeName+    where+      f = fmap typeNameFromQualName . mapOnQualName tv . qualNameFromTypeName  instance ASTMappable (TypeDeclBase Info VName) where   astMap tv (TypeDecl dt (Info et)) =@@ -288,7 +338,7 @@   astMap tv (Wildcard (Info t) loc) =     Wildcard <$> (Info <$> mapOnPatternType tv t) <*> pure loc   astMap tv (PatternLit e (Info t) loc) =-    PatternLit <$> astMap tv e <*> (Info <$> mapOnPatternType tv t) <*>  pure loc+    PatternLit <$> astMap tv e <*> (Info <$> mapOnPatternType tv t) <*> pure loc   astMap tv (PatternConstr n (Info t) ps loc) =     PatternConstr n <$> (Info <$> mapOnPatternType tv t) <*> mapM (astMap tv) ps <*> pure loc @@ -297,7 +347,8 @@     RecordFieldExplicit name <$> mapOnExp tv e <*> pure loc   astMap tv (RecordFieldImplicit name t loc) =     RecordFieldImplicit <$> mapOnName tv name-    <*> traverse (mapOnPatternType tv) t <*> pure loc+      <*> traverse (mapOnPatternType tv) t+      <*> pure loc  instance ASTMappable (CaseBase Info VName) where   astMap tv (CasePat pat e loc) =@@ -312,11 +363,11 @@ instance ASTMappable a => ASTMappable (NE.NonEmpty a) where   astMap tv = traverse $ astMap tv -instance (ASTMappable a, ASTMappable b) => ASTMappable (a,b) where-  astMap tv (x,y) = (,) <$> astMap tv x <*> astMap tv y+instance (ASTMappable a, ASTMappable b) => ASTMappable (a, b) where+  astMap tv (x, y) = (,) <$> astMap tv x <*> astMap tv y -instance (ASTMappable a, ASTMappable b, ASTMappable c) => ASTMappable (a,b,c) where-  astMap tv (x,y,z) = (,,) <$> astMap tv x <*> astMap tv y <*> astMap tv z+instance (ASTMappable a, ASTMappable b, ASTMappable c) => ASTMappable (a, b, c) where+  astMap tv (x, y, z) = (,,) <$> astMap tv x <*> astMap tv y <*> astMap tv z  -- It would be lovely if the following code would be written in terms -- of ASTMappable, but unfortunately it involves changing the Info@@ -373,13 +424,17 @@ bareExp (RecordLit fields loc) = RecordLit (map bareField fields) loc bareExp (ArrayLit els _ loc) = ArrayLit (map bareExp els) NoInfo loc bareExp (Range start next end _ loc) =-  Range (bareExp start) (fmap bareExp next)-  (fmap bareExp end) (NoInfo, NoInfo) loc+  Range+    (bareExp start)+    (fmap bareExp next)+    (fmap bareExp end)+    (NoInfo, NoInfo)+    loc bareExp (Ascript e tdecl loc) =   Ascript (bareExp e) (bareTypeDecl tdecl) loc bareExp (Coerce e tdecl _ loc) =   Coerce (bareExp e) (bareTypeDecl tdecl) (NoInfo, NoInfo) loc-bareExp (BinOp fname _ (x,_) (y,_) _ _ loc) =+bareExp (BinOp fname _ (x, _) (y, _) _ _ loc) =   BinOp fname NoInfo (bareExp x, NoInfo) (bareExp y, NoInfo) NoInfo NoInfo loc bareExp (Negate x loc) = Negate (bareExp x) loc bareExp (If c texp fexp _ loc) =@@ -391,9 +446,14 @@ bareExp (LetFun name (fparams, params, ret, _, e) body _ loc) =   LetFun name (fparams, map barePat params, ret, NoInfo, bareExp e) (bareExp body) NoInfo loc bareExp (LetWith (Ident dest _ destloc) (Ident src _ srcloc) idxexps vexp body _ loc) =-  LetWith (Ident dest NoInfo destloc) (Ident src NoInfo srcloc)-  (map bareDimIndex idxexps) (bareExp vexp)-  (bareExp body) NoInfo loc+  LetWith+    (Ident dest NoInfo destloc)+    (Ident src NoInfo srcloc)+    (map bareDimIndex idxexps)+    (bareExp vexp)+    (bareExp body)+    NoInfo+    loc bareExp (Update src slice v loc) =   Update (bareExp src) (map bareDimIndex slice) (bareExp v) loc bareExp (RecordUpdate src fs v _ loc) =@@ -413,11 +473,17 @@ bareExp (IndexSection slice _ loc) =   IndexSection (map bareDimIndex slice) NoInfo loc bareExp (DoLoop _ mergepat mergeexp form loopbody _ loc) =-  DoLoop [] (barePat mergepat) (bareExp mergeexp) (bareLoopForm form)-  (bareExp loopbody) NoInfo loc+  DoLoop+    []+    (barePat mergepat)+    (bareExp mergeexp)+    (bareLoopForm form)+    (bareExp loopbody)+    NoInfo+    loc bareExp (Constr name es _ loc) =   Constr name (map bareExp es) NoInfo loc bareExp (Match e cases _ loc) =-  Match (bareExp e) (fmap bareCase cases) (NoInfo,NoInfo) loc+  Match (bareExp e) (fmap bareCase cases) (NoInfo, NoInfo) loc bareExp (Attr attr e loc) =   Attr attr (bareExp e) loc
src/Language/Futhark/TypeChecker.hs view
@@ -1,41 +1,41 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Safe #-}+ -- | The type checker checks whether the program is type-consistent -- and adds type annotations and various other elaborations.  The -- program does not need to have any particular properties for the -- type checker to function; in particular it does not need unique -- names. module Language.Futhark.TypeChecker-  ( checkProg-  , checkExp-  , checkDec-  , checkModExp-  , TypeError-  , Warnings-  , initialEnv+  ( checkProg,+    checkExp,+    checkDec,+    checkModExp,+    TypeError,+    Warnings,+    initialEnv,   )-  where+where  import Control.Monad.Except import Control.Monad.Writer hiding (Sum)+import Data.Char (isAlpha, isAlphaNum)+import Data.Either import Data.List (isPrefixOf)+import qualified Data.Map.Strict as M import Data.Maybe-import Data.Either import Data.Ord-import qualified Data.Map.Strict as M import qualified Data.Set as S--import Prelude hiding (abs, mod)-+import Futhark.FreshNames hiding (newName)+import Futhark.Util.Pretty hiding (space) import Language.Futhark import Language.Futhark.Semantic-import Futhark.FreshNames hiding (newName)-import Language.Futhark.TypeChecker.Monad import Language.Futhark.TypeChecker.Modules+import Language.Futhark.TypeChecker.Monad import Language.Futhark.TypeChecker.Terms import Language.Futhark.TypeChecker.Types-import Futhark.Util.Pretty hiding (space)+import Prelude hiding (abs, mod)  --- The main checker @@ -44,82 +44,93 @@ -- Accepts a mapping from file names (excluding extension) to -- previously type checker results.  The 'FilePath' is used to resolve -- relative @import@s.-checkProg :: Imports-          -> VNameSource-          -> ImportName-          -> UncheckedProg-          -> Either TypeError (FileModule, Warnings, VNameSource)+checkProg ::+  Imports ->+  VNameSource ->+  ImportName ->+  UncheckedProg ->+  Either TypeError (FileModule, Warnings, VNameSource) checkProg files src name prog =   runTypeM initialEnv files' name src $ checkProgM prog-  where files' = M.map fileEnv $ M.fromList files+  where+    files' = M.map fileEnv $ M.fromList files  -- | Type check a single expression containing no type information, -- yielding either a type error or the same expression annotated with -- type information.  Also returns a list of type parameters, which -- will be nonempty if the expression is polymorphic.  See also -- 'checkProg'.-checkExp :: Imports-         -> VNameSource-         -> Env-         -> UncheckedExp-         -> Either TypeError ([TypeParam], Exp)+checkExp ::+  Imports ->+  VNameSource ->+  Env ->+  UncheckedExp ->+  Either TypeError ([TypeParam], Exp) checkExp files src env e = do   (e', _, _) <- runTypeM env files' (mkInitialImport "") src $ checkOneExp e   return e'-  where files' = M.map fileEnv $ M.fromList files+  where+    files' = M.map fileEnv $ M.fromList files  -- | Type check a single declaration containing no type information, -- yielding either a type error or the same declaration annotated with -- type information along the Env produced by that declaration.  See -- also 'checkProg'.-checkDec :: Imports-         -> VNameSource-         -> Env-         -> ImportName-         -> UncheckedDec-         -> Either TypeError (Env, Dec, VNameSource)+checkDec ::+  Imports ->+  VNameSource ->+  Env ->+  ImportName ->+  UncheckedDec ->+  Either TypeError (Env, Dec, VNameSource) checkDec files src env name d = do   ((env', d'), _, src') <- runTypeM env files' name src $ do     (_, env', d') <- checkOneDec d     return (env' <> env, d')   return (env', d', src')-  where files' = M.map fileEnv $ M.fromList files+  where+    files' = M.map fileEnv $ M.fromList files  -- | Type check a single module expression containing no type information, -- yielding either a type error or the same expression annotated with -- type information along the Env produced by that declaration.  See -- also 'checkProg'.-checkModExp :: Imports-            -> VNameSource-            -> Env-            -> ModExpBase NoInfo Name-            -> Either TypeError (MTy, ModExpBase Info VName)+checkModExp ::+  Imports ->+  VNameSource ->+  Env ->+  ModExpBase NoInfo Name ->+  Either TypeError (MTy, ModExpBase Info VName) checkModExp files src env me = do   (x, _, _) <- runTypeM env files' (mkInitialImport "") src $ checkOneModExp me   return x-  where files' = M.map fileEnv $ M.fromList files+  where+    files' = M.map fileEnv $ M.fromList files  -- | An initial environment for the type checker, containing -- intrinsics and such. initialEnv :: Env-initialEnv = intrinsicsModule-               { envModTable = initialModTable-               , envNameMap = M.insert-                              (Term, nameFromString "intrinsics")-                              (qualName intrinsics_v)-                              topLevelNameMap-               }-  where initialTypeTable = M.fromList $ mapMaybe addIntrinsicT $ M.toList intrinsics-        initialModTable = M.singleton intrinsics_v (ModEnv intrinsicsModule)+initialEnv =+  intrinsicsModule+    { envModTable = initialModTable,+      envNameMap =+        M.insert+          (Term, nameFromString "intrinsics")+          (qualName intrinsics_v)+          topLevelNameMap+    }+  where+    initialTypeTable = M.fromList $ mapMaybe addIntrinsicT $ M.toList intrinsics+    initialModTable = M.singleton intrinsics_v (ModEnv intrinsicsModule) -        intrinsics_v = VName (nameFromString "intrinsics") 0+    intrinsics_v = VName (nameFromString "intrinsics") 0 -        intrinsicsModule = Env mempty initialTypeTable mempty mempty intrinsicsNameMap+    intrinsicsModule = Env mempty initialTypeTable mempty mempty intrinsicsNameMap -        addIntrinsicT (name, IntrinsicType t) =-          Just (name, TypeAbbr Unlifted [] $ Scalar $ Prim t)-        addIntrinsicT _ =-          Nothing+    addIntrinsicT (name, IntrinsicType t) =+      Just (name, TypeAbbr Unlifted [] $ Scalar $ Prim t)+    addIntrinsicT _ =+      Nothing  checkProgM :: UncheckedProg -> TypeM FileModule checkProgM (Prog doc decs) = do@@ -127,54 +138,64 @@   (abs, env, decs') <- checkDecs decs   return (FileModule abs env $ Prog doc decs') -dupDefinitionError :: MonadTypeChecker m =>-                      Namespace -> Name -> SrcLoc -> SrcLoc -> m a+dupDefinitionError ::+  MonadTypeChecker m =>+  Namespace ->+  Name ->+  SrcLoc ->+  SrcLoc ->+  m a dupDefinitionError space name loc1 loc2 =   typeError loc1 mempty $-  "Duplicate definition of" <+> ppr space <+>-  pprName name <> ".  Previously defined at" <+> text (locStr loc2) <> "."+    "Duplicate definition of" <+> ppr space+      <+> pprName name <> ".  Previously defined at"+      <+> text (locStr loc2) <> "."  checkForDuplicateDecs :: [DecBase NoInfo Name] -> TypeM () checkForDuplicateDecs =   foldM_ (flip f) mempty-  where check namespace name loc known =-          case M.lookup (namespace, name) known of-            Just loc' ->-              dupDefinitionError namespace name loc loc'-            _ -> return $ M.insert (namespace, name) loc known--        f (ValDec vb) =-          check Term (valBindName vb) (srclocOf vb)--        f (TypeDec (TypeBind name _ _ _ _ loc)) =-          check Type name loc--        f (SigDec (SigBind name _ _ loc)) =-          check Signature name loc--        f (ModDec (ModBind name _ _ _ _ loc)) =-          check Term name loc+  where+    check namespace name loc known =+      case M.lookup (namespace, name) known of+        Just loc' ->+          dupDefinitionError namespace name loc loc'+        _ -> return $ M.insert (namespace, name) loc known -        f OpenDec{} = return-        f LocalDec{} = return-        f ImportDec{} = return+    f (ValDec vb) =+      check Term (valBindName vb) (srclocOf vb)+    f (TypeDec (TypeBind name _ _ _ _ loc)) =+      check Type name loc+    f (SigDec (SigBind name _ _ loc)) =+      check Signature name loc+    f (ModDec (ModBind name _ _ _ _ loc)) =+      check Term name loc+    f OpenDec {} = return+    f LocalDec {} = return+    f ImportDec {} = return  bindingTypeParams :: [TypeParam] -> TypeM a -> TypeM a bindingTypeParams tparams = localEnv env-  where env = mconcat $ map typeParamEnv tparams+  where+    env = mconcat $ map typeParamEnv tparams -        typeParamEnv (TypeParamDim v _) =-          mempty { envVtable =-                     M.singleton v $ BoundV [] (Scalar $ Prim $ Signed Int32) }-        typeParamEnv (TypeParamType l v _) =-          mempty { envTypeTable =-                     M.singleton v $ TypeAbbr l [] $-                     Scalar $ TypeVar () Nonunique (typeName v) [] }+    typeParamEnv (TypeParamDim v _) =+      mempty+        { envVtable =+            M.singleton v $ BoundV [] (Scalar $ Prim $ Signed Int64)+        }+    typeParamEnv (TypeParamType l v _) =+      mempty+        { envTypeTable =+            M.singleton v $+              TypeAbbr l [] $+                Scalar $ TypeVar () Nonunique (typeName v) []+        }  emptyDimParam :: StructType -> Bool emptyDimParam = isNothing . traverseDims onDim-  where onDim _ pos AnyDim | pos `elem` [PosImmediate, PosParam] = Nothing-        onDim _ _ d = Just d+  where+    onDim _ pos AnyDim | pos `elem` [PosImmediate, PosParam] = Nothing+    onDim _ _ d = Just d  -- In this function, after the recursion, we add the Env of the -- current Spec *after* the one that is returned from the recursive@@ -183,9 +204,7 @@ -- the specific structure of substitutions in case some module type is -- redundantly imported multiple times). checkSpecs :: [SpecBase NoInfo Name] -> TypeM (TySet, Env, [SpecBase Info VName])- checkSpecs [] = return (mempty, mempty, [])- checkSpecs (ValSpec name tparams vtype doc loc : specs) =   bindSpaced [(Term, name)] $ do     name' <- checkName Term name loc@@ -196,75 +215,90 @@      when (emptyDimParam $ unInfo $ expandedType vtype') $       typeError loc mempty $-      "All function parameters must have non-anonymous sizes." </>-      "Hint: add size parameters to" <+> pquote (pprName name') <> "."+        "All function parameters must have non-anonymous sizes."+          </> "Hint: add size parameters to" <+> pquote (pprName name') <> "."      let (params, _) = unfoldFunType $ unInfo $ expandedType vtype'     when (null params && any isSizeParam tparams) $-      typeError loc mempty-      "Size parameters are only allowed on bindings that also have value parameters."+      typeError+        loc+        mempty+        "Size parameters are only allowed on bindings that also have value parameters."      let binding = BoundV tparams' $ unInfo $ expandedType vtype'         valenv =-          mempty { envVtable = M.singleton name' binding-                 , envNameMap = M.singleton (Term, name) $ qualName name'-                 }+          mempty+            { envVtable = M.singleton name' binding,+              envNameMap = M.singleton (Term, name) $ qualName name'+            }     (abstypes, env, specs') <- localEnv valenv $ checkSpecs specs-    return (abstypes,-            env <> valenv,-            ValSpec name' tparams' vtype' doc loc : specs')-+    return+      ( abstypes,+        env <> valenv,+        ValSpec name' tparams' vtype' doc loc : specs'+      ) checkSpecs (TypeAbbrSpec tdec : specs) =   bindSpaced [(Type, typeAlias tdec)] $ do     (tenv, tdec') <- checkTypeBind tdec     (abstypes, env, specs') <- localEnv tenv $ checkSpecs specs-    return (abstypes,-            env <> tenv,-            TypeAbbrSpec tdec' : specs')-+    return+      ( abstypes,+        env <> tenv,+        TypeAbbrSpec tdec' : specs'+      ) checkSpecs (TypeSpec l name ps doc loc : specs) =   checkTypeParams ps $ \ps' ->-  bindSpaced [(Type, name)] $ do-    name' <- checkName Type name loc-    let tenv = mempty-               { envNameMap =-                   M.singleton (Type, name) $ qualName name'-               , envTypeTable =-                   M.singleton name' $ TypeAbbr l ps' $-                   Scalar $ TypeVar () Nonunique (typeName name') $-                   map typeParamToArg ps'-               }-    (abstypes, env, specs') <- localEnv tenv $ checkSpecs specs-    return (M.insert (qualName name') l abstypes,-            env <> tenv,-            TypeSpec l name' ps' doc loc : specs')-+    bindSpaced [(Type, name)] $ do+      name' <- checkName Type name loc+      let tenv =+            mempty+              { envNameMap =+                  M.singleton (Type, name) $ qualName name',+                envTypeTable =+                  M.singleton name' $+                    TypeAbbr l ps' $+                      Scalar $+                        TypeVar () Nonunique (typeName name') $+                          map typeParamToArg ps'+              }+      (abstypes, env, specs') <- localEnv tenv $ checkSpecs specs+      return+        ( M.insert (qualName name') l abstypes,+          env <> tenv,+          TypeSpec l name' ps' doc loc : specs'+        ) checkSpecs (ModSpec name sig doc loc : specs) =   bindSpaced [(Term, name)] $ do     name' <- checkName Term name loc     (mty, sig') <- checkSigExp sig-    let senv = mempty { envNameMap = M.singleton (Term, name) $ qualName name'-                      , envModTable = M.singleton name' $ mtyMod mty-                      }+    let senv =+          mempty+            { envNameMap = M.singleton (Term, name) $ qualName name',+              envModTable = M.singleton name' $ mtyMod mty+            }     (abstypes, env, specs') <- localEnv senv $ checkSpecs specs-    return (M.mapKeys (qualify name') (mtyAbs mty) <> abstypes,-            env <> senv,-            ModSpec name' sig' doc loc : specs')-+    return+      ( M.mapKeys (qualify name') (mtyAbs mty) <> abstypes,+        env <> senv,+        ModSpec name' sig' doc loc : specs'+      ) checkSpecs (IncludeSpec e loc : specs) = do   (e_abs, e_env, e') <- checkSigExpToEnv e    mapM_ (warnIfShadowing . fmap baseName) $ M.keys e_abs    (abstypes, env, specs') <- localEnv e_env $ checkSpecs specs-  return (abstypes <> e_abs,-          env <> e_env,-          IncludeSpec e' loc : specs')-  where warnIfShadowing qn =-          (lookupType loc qn >> warnAbout qn)-          `catchError` \_ -> return ()-        warnAbout qn =-          warn loc $ "Inclusion shadows type " ++ quote (pretty qn) ++ "."+  return+    ( abstypes <> e_abs,+      env <> e_env,+      IncludeSpec e' loc : specs'+    )+  where+    warnIfShadowing qn =+      (lookupType loc qn >> warnAbout qn)+        `catchError` \_ -> return ()+    warnAbout qn =+      warn loc $ "Inclusion shadows type " ++ quote (pretty qn) ++ "."  checkSigExp :: SigExpBase NoInfo Name -> TypeM (MTy, SigExpBase Info VName) checkSigExp (SigParens e loc) = do@@ -290,32 +324,40 @@     case maybe_pname of       Just pname -> bindSpaced [(Term, pname)] $ do         pname' <- checkName Term pname loc-        return (mempty { envNameMap = M.singleton (Term, pname) $ qualName pname'-                       , envModTable = M.singleton pname' e1_mod-                       },-                Just pname')+        return+          ( mempty+              { envNameMap = M.singleton (Term, pname) $ qualName pname',+                envModTable = M.singleton pname' e1_mod+              },+            Just pname'+          )       Nothing ->         return (mempty, Nothing)   (e2_mod, e2') <- localEnv env_for_e2 $ checkSigExp e2-  return (MTy mempty $ ModFun $ FunSig s_abs e1_mod e2_mod,-          SigArrow maybe_pname' e1' e2' loc)+  return+    ( MTy mempty $ ModFun $ FunSig s_abs e1_mod e2_mod,+      SigArrow maybe_pname' e1' e2' loc+    )  checkSigExpToEnv :: SigExpBase NoInfo Name -> TypeM (TySet, Env, SigExpBase Info VName) checkSigExpToEnv e = do   (MTy abs mod, e') <- checkSigExp e   case mod of     ModEnv env -> return (abs, env, e')-    ModFun{}   -> unappliedFunctor $ srclocOf e+    ModFun {} -> unappliedFunctor $ srclocOf e  checkSigBind :: SigBindBase NoInfo Name -> TypeM (Env, SigBindBase Info VName) checkSigBind (SigBind name e doc loc) = do   (env, e') <- checkSigExp e   bindSpaced [(Signature, name)] $ do     name' <- checkName Signature name loc-    return (mempty { envSigTable = M.singleton name' env-                   , envNameMap = M.singleton (Signature, name) (qualName name')-                   },-            SigBind name' e' doc loc)+    return+      ( mempty+          { envSigTable = M.singleton name' env,+            envNameMap = M.singleton (Signature, name) (qualName name')+          },+        SigBind name' e' doc loc+      )  checkOneModExp :: ModExpBase NoInfo Name -> TypeM (MTy, ModExpBase Info VName) checkOneModExp (ModParens e loc) = do@@ -324,18 +366,24 @@ checkOneModExp (ModDecs decs loc) = do   checkForDuplicateDecs decs   (abstypes, env, decs') <- checkDecs decs-  return (MTy abstypes $ ModEnv env,-          ModDecs decs' loc)+  return+    ( MTy abstypes $ ModEnv env,+      ModDecs decs' loc+    ) checkOneModExp (ModVar v loc) = do   (v', env) <- lookupMod loc v-  when (baseName (qualLeaf v') == nameFromString "intrinsics" &&-        baseTag (qualLeaf v') <= maxIntrinsicTag) $-    typeError loc mempty "The 'intrinsics' module may not be used in module expressions."+  when+    ( baseName (qualLeaf v') == nameFromString "intrinsics"+        && baseTag (qualLeaf v') <= maxIntrinsicTag+    )+    $ typeError loc mempty "The 'intrinsics' module may not be used in module expressions."   return (MTy mempty env, ModVar v' loc) checkOneModExp (ModImport name NoInfo loc) = do   (name', env) <- lookupImport loc name-  return (MTy mempty $ ModEnv env,-          ModImport name (Info name') loc)+  return+    ( MTy mempty $ ModEnv env,+      ModImport name (Info name') loc+    ) checkOneModExp (ModApply f e NoInfo NoInfo loc) = do   (f_mty, f') <- checkOneModExp f   case mtyMod f_mty of@@ -352,41 +400,49 @@   return (se_mty, ModAscript me' se' (Info match_subst) loc) checkOneModExp (ModLambda param maybe_fsig_e body_e loc) =   withModParam param $ \param' param_abs param_mod -> do-  (maybe_fsig_e', body_e', mty) <- checkModBody (fst <$> maybe_fsig_e) body_e loc-  return (MTy mempty $ ModFun $ FunSig param_abs param_mod mty,-          ModLambda param' maybe_fsig_e' body_e' loc)+    (maybe_fsig_e', body_e', mty) <- checkModBody (fst <$> maybe_fsig_e) body_e loc+    return+      ( MTy mempty $ ModFun $ FunSig param_abs param_mod mty,+        ModLambda param' maybe_fsig_e' body_e' loc+      )  checkOneModExpToEnv :: ModExpBase NoInfo Name -> TypeM (TySet, Env, ModExpBase Info VName) checkOneModExpToEnv e = do   (MTy abs mod, e') <- checkOneModExp e   case mod of     ModEnv env -> return (abs, env, e')-    ModFun{}   -> unappliedFunctor $ srclocOf e+    ModFun {} -> unappliedFunctor $ srclocOf e -withModParam :: ModParamBase NoInfo Name-             -> (ModParamBase Info VName -> TySet -> Mod -> TypeM a)-             -> TypeM a+withModParam ::+  ModParamBase NoInfo Name ->+  (ModParamBase Info VName -> TySet -> Mod -> TypeM a) ->+  TypeM a withModParam (ModParam pname psig_e NoInfo loc) m = do   (MTy p_abs p_mod, psig_e') <- checkSigExp psig_e   bindSpaced [(Term, pname)] $ do     pname' <- checkName Term pname loc-    let in_body_env = mempty { envModTable = M.singleton pname' p_mod }+    let in_body_env = mempty {envModTable = M.singleton pname' p_mod}     localEnv in_body_env $       m (ModParam pname' psig_e' (Info $ map qualLeaf $ M.keys p_abs) loc) p_abs p_mod -withModParams :: [ModParamBase NoInfo Name]-              -> ([(ModParamBase Info VName, TySet, Mod)] -> TypeM a)-              -> TypeM a+withModParams ::+  [ModParamBase NoInfo Name] ->+  ([(ModParamBase Info VName, TySet, Mod)] -> TypeM a) ->+  TypeM a withModParams [] m = m []-withModParams (p:ps) m =+withModParams (p : ps) m =   withModParam p $ \p' pabs pmod ->-  withModParams ps $ \ps' -> m $ (p',pabs,pmod) : ps'+    withModParams ps $ \ps' -> m $ (p', pabs, pmod) : ps' -checkModBody :: Maybe (SigExpBase NoInfo Name)-             -> ModExpBase NoInfo Name-             -> SrcLoc-             -> TypeM (Maybe (SigExp, Info (M.Map VName VName)),-                       ModExp, MTy)+checkModBody ::+  Maybe (SigExpBase NoInfo Name) ->+  ModExpBase NoInfo Name ->+  SrcLoc ->+  TypeM+    ( Maybe (SigExp, Info (M.Map VName VName)),+      ModExp,+      MTy+    ) checkModBody maybe_fsig_e body_e loc = do   (body_mty, body_e') <- checkOneModExp body_e   case maybe_fsig_e of@@ -402,118 +458,137 @@   (maybe_fsig_e', e', mty) <- checkModBody (fst <$> maybe_fsig_e) e loc   bindSpaced [(Term, name)] $ do     name' <- checkName Term name loc-    return (mtyAbs mty,-            mempty { envModTable = M.singleton name' $ mtyMod mty-                   , envNameMap = M.singleton (Term, name) $ qualName name'-                   },-            ModBind name' [] maybe_fsig_e' e' doc loc)-checkModBind (ModBind name (p:ps) maybe_fsig_e body_e doc loc) = do+    return+      ( mtyAbs mty,+        mempty+          { envModTable = M.singleton name' $ mtyMod mty,+            envNameMap = M.singleton (Term, name) $ qualName name'+          },+        ModBind name' [] maybe_fsig_e' e' doc loc+      )+checkModBind (ModBind name (p : ps) maybe_fsig_e body_e doc loc) = do   (params', maybe_fsig_e', body_e', funsig) <-     withModParam p $ \p' p_abs p_mod ->-    withModParams ps $ \params_stuff -> do-    let (ps', ps_abs, ps_mod) = unzip3 params_stuff-    (maybe_fsig_e', body_e', mty) <- checkModBody (fst <$> maybe_fsig_e) body_e loc-    let addParam (x,y) mty' = MTy mempty $ ModFun $ FunSig x y mty'-    return (p' : ps', maybe_fsig_e', body_e',-            FunSig p_abs p_mod $ foldr addParam mty $ zip ps_abs ps_mod)+      withModParams ps $ \params_stuff -> do+        let (ps', ps_abs, ps_mod) = unzip3 params_stuff+        (maybe_fsig_e', body_e', mty) <- checkModBody (fst <$> maybe_fsig_e) body_e loc+        let addParam (x, y) mty' = MTy mempty $ ModFun $ FunSig x y mty'+        return+          ( p' : ps',+            maybe_fsig_e',+            body_e',+            FunSig p_abs p_mod $ foldr addParam mty $ zip ps_abs ps_mod+          )   bindSpaced [(Term, name)] $ do     name' <- checkName Term name loc-    return (mempty,-            mempty { envModTable =-                       M.singleton name' $ ModFun funsig-                   , envNameMap =-                       M.singleton (Term, name) $ qualName name'-                   },-            ModBind name' params' maybe_fsig_e' body_e' doc loc)+    return+      ( mempty,+        mempty+          { envModTable =+              M.singleton name' $ ModFun funsig,+            envNameMap =+              M.singleton (Term, name) $ qualName name'+          },+        ModBind name' params' maybe_fsig_e' body_e' doc loc+      )  checkForDuplicateSpecs :: [SpecBase NoInfo Name] -> TypeM () checkForDuplicateSpecs =   foldM_ (flip f) mempty-  where check namespace name loc known =-          case M.lookup (namespace, name) known of-            Just loc' ->-              dupDefinitionError namespace name loc loc'-            _ -> return $ M.insert (namespace, name) loc known--        f (ValSpec name _ _ _ loc) =-          check Term name loc--        f (TypeAbbrSpec (TypeBind name _ _ _ _ loc)) =-          check Type name loc--        f (TypeSpec _ name _ _ loc) =-          check Type name loc--        f (ModSpec name _ _ loc) =-          check Term name loc+  where+    check namespace name loc known =+      case M.lookup (namespace, name) known of+        Just loc' ->+          dupDefinitionError namespace name loc loc'+        _ -> return $ M.insert (namespace, name) loc known -        f IncludeSpec{} =-          return+    f (ValSpec name _ _ _ loc) =+      check Term name loc+    f (TypeAbbrSpec (TypeBind name _ _ _ _ loc)) =+      check Type name loc+    f (TypeSpec _ name _ _ loc) =+      check Type name loc+    f (ModSpec name _ _ loc) =+      check Term name loc+    f IncludeSpec {} =+      return -checkTypeBind :: TypeBindBase NoInfo Name-              -> TypeM (Env, TypeBindBase Info VName)+checkTypeBind ::+  TypeBindBase NoInfo Name ->+  TypeM (Env, TypeBindBase Info VName) checkTypeBind (TypeBind name l tps td doc loc) =   checkTypeParams tps $ \tps' -> do     (td', l') <- bindingTypeParams tps' $ checkTypeDecl td      let used_dims = typeDimNames $ unInfo $ expandedType td'     case filter ((`S.notMember` used_dims) . typeParamName) $-         filter isSizeParam tps' of+      filter isSizeParam tps' of       [] -> return ()-      tp:_ -> typeError loc mempty $-              "Size parameter" <+> pquote (ppr tp) <+> "unused."+      tp : _ ->+        typeError loc mempty $+          "Size parameter" <+> pquote (ppr tp) <+> "unused."      case (l, l') of       (_, Lifted)         | l < Lifted ->           typeError loc mempty $-          "Non-lifted type abbreviations may not contain functions." </>-          "Hint: consider using 'type^'."+            "Non-lifted type abbreviations may not contain functions."+              </> "Hint: consider using 'type^'."       (_, SizeLifted)         | l < SizeLifted ->           typeError loc mempty $-          "Non-size-lifted type abbreviations may not contain size-lifted types." </>-          "Hint: consider using 'type~'."+            "Non-size-lifted type abbreviations may not contain size-lifted types."+              </> "Hint: consider using 'type~'."       (Unlifted, _)         | emptyDimParam $ unInfo $ expandedType td' ->-            typeError loc mempty $-            "Non-lifted type abbreviations may not use anonymous sizes in their definition." </>-            "Hint: use 'type~' or add size parameters to" <+>-            pquote (pprName name) <> "."+          typeError loc mempty $+            "Non-lifted type abbreviations may not use anonymous sizes in their definition."+              </> "Hint: use 'type~' or add size parameters to"+              <+> pquote (pprName name) <> "."       _ -> return ()      bindSpaced [(Type, name)] $ do       name' <- checkName Type name loc-      return (mempty { envTypeTable =-                         M.singleton name' $ TypeAbbr l tps' $ unInfo $ expandedType td',-                       envNameMap =-                         M.singleton (Type, name) $ qualName name'-                     },-               TypeBind name' l tps' td' doc loc)-+      return+        ( mempty+            { envTypeTable =+                M.singleton name' $ TypeAbbr l tps' $ unInfo $ expandedType td',+              envNameMap =+                M.singleton (Type, name) $ qualName name'+            },+          TypeBind name' l tps' td' doc loc+        )  entryPoint :: [Pattern] -> Maybe (TypeExp VName) -> StructType -> EntryPoint entryPoint params orig_ret_te orig_ret =   EntryPoint (map patternEntry params ++ more_params) rettype'-  where (more_params, rettype') =-          onRetType orig_ret_te orig_ret+  where+    (more_params, rettype') =+      onRetType orig_ret_te orig_ret -        patternEntry (PatternParens p _) =-          patternEntry p-        patternEntry (PatternAscription _ tdecl _) =-          EntryType (unInfo (expandedType tdecl)) (Just (declaredType tdecl))-        patternEntry p =-          EntryType (patternStructType p) Nothing+    patternEntry (PatternParens p _) =+      patternEntry p+    patternEntry (PatternAscription _ tdecl _) =+      EntryType (unInfo (expandedType tdecl)) (Just (declaredType tdecl))+    patternEntry p =+      EntryType (patternStructType p) Nothing -        onRetType (Just (TEArrow _ t1_te t2_te _)) (Scalar (Arrow _ _ t1 t2)) =-          let (xs, y) = onRetType (Just t2_te) t2-          in (EntryType t1 (Just t1_te) : xs, y)-        onRetType _ (Scalar (Arrow _ _ t1 t2)) =-          let (xs, y) = onRetType Nothing t2-          in (EntryType t1 Nothing : xs, y)-        onRetType te t =-          ([], EntryType t te)+    onRetType (Just (TEArrow _ t1_te t2_te _)) (Scalar (Arrow _ _ t1 t2)) =+      let (xs, y) = onRetType (Just t2_te) t2+       in (EntryType t1 (Just t1_te) : xs, y)+    onRetType _ (Scalar (Arrow _ _ t1 t2)) =+      let (xs, y) = onRetType Nothing t2+       in (EntryType t1 Nothing : xs, y)+    onRetType te t =+      ([], EntryType t te) +entryPointNameIsAcceptable :: Name -> Bool+entryPointNameIsAcceptable = check . nameToString+  where+    check [] = True -- academic+    check (c : cs) = isAlpha c && all constituent cs+    constituent c = isAlphaNum c || c == '_'+ checkValBind :: ValBindBase NoInfo Name -> TypeM (Env, ValBind) checkValBind (ValBind entry fname maybe_tdecl NoInfo tparams params body doc attrs loc) = do   (fname', tparams', params', maybe_tdecl', rettype, retext, body') <-@@ -524,73 +599,79 @@    case entry' of     Just _+      | not $ entryPointNameIsAcceptable fname ->+        typeError loc mempty "Entry point names must start with a letter and contain only letters, digits, and underscores."       | any isTypeParam tparams' ->-          typeError loc mempty "Entry point functions may not be polymorphic."-+        typeError loc mempty "Entry point functions may not be polymorphic."       | not (all patternOrderZero params')-        || not (all orderZero rettype_params)-        || not (orderZero rettype') ->-          typeError loc mempty "Entry point functions may not be higher-order."-+          || not (all orderZero rettype_params)+          || not (orderZero rettype') ->+        typeError loc mempty "Entry point functions may not be higher-order."       | sizes_only_in_ret <--          S.fromList (map typeParamName tparams') `S.intersection`-          typeDimNames rettype' `S.difference`-          foldMap typeDimNames (map patternStructType params' ++ rettype_params),+          S.fromList (map typeParamName tparams')+            `S.intersection` typeDimNames rettype'+            `S.difference` foldMap typeDimNames (map patternStructType params' ++ rettype_params),         not $ S.null sizes_only_in_ret ->-          typeError loc mempty "Entry point functions must not be size-polymorphic in their return type."-+        typeError loc mempty "Entry point functions must not be size-polymorphic in their return type."       | p : _ <- filter nastyParameter params' ->-          warn loc $ pretty $ "Entry point parameter\n" </>-          indent 2 (ppr p) </>-          "\nwill have an opaque type, so the entry point will likely not be callable."-+        warn loc $+          pretty $+            "Entry point parameter\n"+              </> indent 2 (ppr p)+              </> "\nwill have an opaque type, so the entry point will likely not be callable."       | nastyReturnType maybe_tdecl' rettype ->-          warn loc $ pretty $ "Entry point return type\n" </>-          indent 2 (ppr rettype) </>-          "\nwill have an opaque type, so the result will likely not be usable."-+        warn loc $+          pretty $+            "Entry point return type\n"+              </> indent 2 (ppr rettype)+              </> "\nwill have an opaque type, so the result will likely not be usable."     _ -> return ()    let arrow (xp, xt) yt = Scalar $ Arrow () xp xt yt-  return (mempty { envVtable =-                     M.singleton fname' $-                     BoundV tparams' $ foldr (arrow . patternParam) rettype params'-                 , envNameMap =-                     M.singleton (Term, fname) $ qualName fname'-                 },-           ValBind entry' fname' maybe_tdecl' (Info (rettype, retext)) tparams' params' body' doc attrs loc)+  return+    ( mempty+        { envVtable =+            M.singleton fname' $+              BoundV tparams' $ foldr (arrow . patternParam) rettype params',+          envNameMap =+            M.singleton (Term, fname) $ qualName fname'+        },+      ValBind entry' fname' maybe_tdecl' (Info (rettype, retext)) tparams' params' body' doc attrs loc+    )  nastyType :: Monoid als => TypeBase dim als -> Bool-nastyType (Scalar Prim{}) = False-nastyType t@Array{} = nastyType $ stripArray 1 t+nastyType (Scalar Prim {}) = False+nastyType t@Array {} = nastyType $ stripArray 1 t nastyType _ = True  nastyReturnType :: Monoid als => Maybe (TypeExp VName) -> TypeBase dim als -> Bool nastyReturnType Nothing (Scalar (Arrow _ _ t1 t2)) =   nastyType t1 || nastyReturnType Nothing t2 nastyReturnType (Just (TEArrow _ te1 te2 _)) (Scalar (Arrow _ _ t1 t2)) =-  (not (niceTypeExp te1) && nastyType t1) ||-  nastyReturnType (Just te2) t2+  (not (niceTypeExp te1) && nastyType t1)+    || nastyReturnType (Just te2) t2 nastyReturnType (Just te) _   | niceTypeExp te = False nastyReturnType te t   | Just ts <- isTupleRecord t =-      case te of-        Just (TETuple tes _) -> or $ zipWith nastyType' (map Just tes) ts-        _ -> any nastyType ts+    case te of+      Just (TETuple tes _) -> or $ zipWith nastyType' (map Just tes) ts+      _ -> any nastyType ts   | otherwise = nastyType' te t-  where nastyType' (Just te') _ | niceTypeExp te' = False-        nastyType' _ t' = nastyType t'+  where+    nastyType' (Just te') _ | niceTypeExp te' = False+    nastyType' _ t' = nastyType t'  nastyParameter :: Pattern -> Bool nastyParameter p = nastyType (patternType p) && not (ascripted p)-  where ascripted (PatternAscription _ (TypeDecl te _) _) = niceTypeExp te-        ascripted (PatternParens p' _) = ascripted p'-        ascripted _ = False+  where+    ascripted (PatternAscription _ (TypeDecl te _) _) = niceTypeExp te+    ascripted (PatternParens p' _) = ascripted p'+    ascripted _ = False  niceTypeExp :: TypeExp VName -> Bool niceTypeExp (TEVar (QualName [] _) _) = True-niceTypeExp (TEApply te TypeArgExpDim{} _) = niceTypeExp te+niceTypeExp (TEApply te TypeArgExpDim {} _) = niceTypeExp te niceTypeExp (TEArray te _ _) = niceTypeExp te niceTypeExp _ = False @@ -598,47 +679,43 @@ checkOneDec (ModDec struct) = do   (abs, modenv, struct') <- checkModBind struct   return (abs, modenv, ModDec struct')- checkOneDec (SigDec sig) = do   (sigenv, sig') <- checkSigBind sig   return (mempty, sigenv, SigDec sig')- checkOneDec (TypeDec tdec) = do   (tenv, tdec') <- checkTypeBind tdec   return (mempty, tenv, TypeDec tdec')- checkOneDec (OpenDec x loc) = do   (x_abs, x_env, x') <- checkOneModExpToEnv x   return (x_abs, x_env, OpenDec x' loc)- checkOneDec (LocalDec d loc) = do   (abstypes, env, d') <- checkOneDec d   return (abstypes, env, LocalDec d' loc)- checkOneDec (ImportDec name NoInfo loc) = do   (name', env) <- lookupImport loc name   when ("/prelude" `isPrefixOf` name) $     typeError loc mempty $ ppr name <+> "may not be explicitly imported."   return (mempty, env, ImportDec name (Info name') loc)- checkOneDec (ValDec vb) = do   (env, vb') <- checkValBind vb   return (mempty, env, ValDec vb')  checkDecs :: [DecBase NoInfo Name] -> TypeM (TySet, Env, [DecBase Info VName])-checkDecs (LocalDec d loc:ds) = do+checkDecs (LocalDec d loc : ds) = do   (d_abstypes, d_env, d') <- checkOneDec d   (ds_abstypes, ds_env, ds') <- localEnv d_env $ checkDecs ds-  return (d_abstypes <> ds_abstypes,-          ds_env,-          LocalDec d' loc : ds')--checkDecs (d:ds) = do+  return+    ( d_abstypes <> ds_abstypes,+      ds_env,+      LocalDec d' loc : ds'+    )+checkDecs (d : ds) = do   (d_abstypes, d_env, d') <- checkOneDec d   (ds_abstypes, ds_env, ds') <- localEnv d_env $ checkDecs ds-  return (d_abstypes <> ds_abstypes,-          ds_env <> d_env,-          d' : ds')-+  return+    ( d_abstypes <> ds_abstypes,+      ds_env <> d_env,+      d' : ds'+    ) checkDecs [] =   return (mempty, mempty, [])
src/Language/Futhark/TypeChecker/Modules.hs view
@@ -1,32 +1,32 @@-{-# LANGUAGE TupleSections #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Safe #-}+{-# LANGUAGE TupleSections #-}+ -- | Implementation of the Futhark module system (at least most of it; -- some is scattered elsewhere in the type checker). module Language.Futhark.TypeChecker.Modules-  ( matchMTys-  , newNamesForMTy-  , refineEnv-  , applyFunctor-  ) where+  ( matchMTys,+    newNamesForMTy,+    refineEnv,+    applyFunctor,+  )+where  import Control.Monad.Except import Control.Monad.Writer hiding (Sum)+import Data.Either import Data.List (intersect)+import qualified Data.Map.Strict as M import Data.Maybe-import Data.Either import Data.Ord-import qualified Data.Map.Strict as M import qualified Data.Set as S--import Prelude hiding (abs, mod)-+import Futhark.Util.Pretty import Language.Futhark import Language.Futhark.Semantic import Language.Futhark.TypeChecker.Monad-import Language.Futhark.TypeChecker.Unify (doUnification) import Language.Futhark.TypeChecker.Types-import Futhark.Util.Pretty+import Language.Futhark.TypeChecker.Unify (doUnification)+import Prelude hiding (abs, mod)  substituteTypesInMod :: TypeSubs -> Mod -> Mod substituteTypesInMod substs (ModEnv e) =@@ -39,13 +39,15 @@  substituteTypesInEnv :: TypeSubs -> Env -> Env substituteTypesInEnv substs env =-  env { envVtable    = M.map (substituteTypesInBoundV substs) $ envVtable env-      , envTypeTable = M.mapWithKey subT $ envTypeTable env-      , envModTable  = M.map (substituteTypesInMod substs) $ envModTable env-      }-  where subT name _-          | Just (TypeSub (TypeAbbr l ps t)) <- M.lookup name substs = TypeAbbr l ps t-        subT _ (TypeAbbr l ps t) = TypeAbbr l ps $ substituteTypes substs t+  env+    { envVtable = M.map (substituteTypesInBoundV substs) $ envVtable env,+      envTypeTable = M.mapWithKey subT $ envTypeTable env,+      envModTable = M.map (substituteTypesInMod substs) $ envModTable env+    }+  where+    subT name _+      | Just (TypeSub (TypeAbbr l ps t)) <- M.lookup name substs = TypeAbbr l ps t+    subT _ (TypeAbbr l ps t) = TypeAbbr l ps $ substituteTypes substs t  substituteTypesInBoundV :: TypeSubs -> BoundV -> BoundV substituteTypesInBoundV substs (BoundV tps t) =@@ -54,16 +56,22 @@ -- | All names defined anywhere in the 'Env'. allNamesInEnv :: Env -> S.Set VName allNamesInEnv (Env vtable ttable stable modtable _names) =-  S.fromList (M.keys vtable ++ M.keys ttable ++-              M.keys stable ++ M.keys modtable) <>-  mconcat (map allNamesInMTy (M.elems stable) ++-           map allNamesInMod (M.elems modtable) ++-           map allNamesInType (M.elems ttable))-  where allNamesInType (TypeAbbr _ ps _) = S.fromList $ map typeParamName ps+  S.fromList+    ( M.keys vtable ++ M.keys ttable+        ++ M.keys stable+        ++ M.keys modtable+    )+    <> mconcat+      ( map allNamesInMTy (M.elems stable)+          ++ map allNamesInMod (M.elems modtable)+          ++ map allNamesInType (M.elems ttable)+      )+  where+    allNamesInType (TypeAbbr _ ps _) = S.fromList $ map typeParamName ps  allNamesInMod :: Mod -> S.Set VName allNamesInMod (ModEnv env) = allNamesInEnv env-allNamesInMod ModFun{} = mempty+allNamesInMod ModFun {} = mempty  allNamesInMTy :: MTy -> S.Set VName allNamesInMTy (MTy abs mod) =@@ -80,7 +88,6 @@       rev_substs = M.fromList $ map (uncurry $ flip (,)) pairs    return (substituteInMTy substs orig_mty, rev_substs)-   where     substituteInMTy :: M.Map VName VName -> MTy -> MTy     substituteInMTy substs (MTy mty_abs mty_mod) =@@ -90,21 +97,23 @@           let vtable' = substituteInMap substituteInBinding vtable               ttable' = substituteInMap substituteInTypeBinding ttable               mtable' = substituteInMap substituteInMod modtable-          in Env { envVtable = vtable'-                 , envTypeTable = ttable'-                 , envSigTable = mempty-                 , envModTable = mtable'-                 , envNameMap = M.map (fmap substitute) names-                 }+           in Env+                { envVtable = vtable',+                  envTypeTable = ttable',+                  envSigTable = mempty,+                  envModTable = mtable',+                  envNameMap = M.map (fmap substitute) names+                }          substitute v =           fromMaybe v $ M.lookup v substs          substituteInMap f m =           let (ks, vs) = unzip $ M.toList m-          in M.fromList $-             zip (map (\k -> fromMaybe k $ M.lookup k substs) ks)-                 (map f vs)+           in M.fromList $+                zip+                  (map (\k -> fromMaybe k $ M.lookup k substs) ks)+                  (map f vs)          substituteInBinding (BoundV ps t) =           BoundV (map substituteInTypeParam ps) (substituteInType t)@@ -115,8 +124,10 @@           ModFun $ substituteInFunSig funsig          substituteInFunSig (FunSig abs mod mty) =-          FunSig (M.mapKeys (fmap substitute) abs)-          (substituteInMod mod) (substituteInMTy substs mty)+          FunSig+            (M.mapKeys (fmap substitute) abs)+            (substituteInMod mod)+            (substituteInMTy substs mty)          substituteInTypeBinding (TypeAbbr l ps t) =           TypeAbbr l (map substituteInTypeParam ps) $ substituteInType t@@ -128,8 +139,9 @@          substituteInType :: StructType -> StructType         substituteInType (Scalar (TypeVar () u (TypeName qs v) targs)) =-          Scalar $ TypeVar () u (TypeName (map substitute qs) $ substitute v) $-          map substituteInTypeArg targs+          Scalar $+            TypeVar () u (TypeName (map substitute qs) $ substitute v) $+              map substituteInTypeArg targs         substituteInType (Scalar (Prim t)) =           Scalar $ Prim t         substituteInType (Scalar (Record ts)) =@@ -167,230 +179,307 @@  envTypeAbbrs :: Env -> M.Map VName TypeBinding envTypeAbbrs env =-  envTypeTable env <>-  (mconcat . map modTypeAbbrs . M.elems . envModTable) env+  envTypeTable env+    <> (mconcat . map modTypeAbbrs . M.elems . envModTable) env  -- | Refine the given type name in the given env.-refineEnv :: SrcLoc -> TySet -> Env -> QualName Name -> [TypeParam] -> StructType-          -> TypeM (QualName VName, TySet, Env)+refineEnv ::+  SrcLoc ->+  TySet ->+  Env ->+  QualName Name ->+  [TypeParam] ->+  StructType ->+  TypeM (QualName VName, TySet, Env) refineEnv loc tset env tname ps t   | Just (tname', TypeAbbr l cur_ps (Scalar (TypeVar () _ (TypeName qs v) _))) <-       findTypeDef tname (ModEnv env),     QualName (qualQuals tname') v `M.member` tset =-      if paramsMatch cur_ps ps then-        return (tname',-                QualName qs v `M.delete` tset,-                substituteTypesInEnv-                (M.fromList [(qualLeaf tname',-                              TypeSub $ TypeAbbr l cur_ps t),-                              (v, TypeSub $ TypeAbbr l ps t)])-                env)-      else typeError loc mempty $ "Cannot refine a type having" <+>-           tpMsg ps <> " with a type having " <> tpMsg cur_ps <> "."+    if paramsMatch cur_ps ps+      then+        return+          ( tname',+            QualName qs v `M.delete` tset,+            substituteTypesInEnv+              ( M.fromList+                  [ ( qualLeaf tname',+                      TypeSub $ TypeAbbr l cur_ps t+                    ),+                    (v, TypeSub $ TypeAbbr l ps t)+                  ]+              )+              env+          )+      else+        typeError loc mempty $+          "Cannot refine a type having"+            <+> tpMsg ps <> " with a type having " <> tpMsg cur_ps <> "."   | otherwise =-      typeError loc mempty $ ppr tname <+> "is not an abstract type in the module type."-  where tpMsg [] = "no type parameters"-        tpMsg xs = "type parameters" <+> spread (map ppr xs)+    typeError loc mempty $ ppr tname <+> "is not an abstract type in the module type."+  where+    tpMsg [] = "no type parameters"+    tpMsg xs = "type parameters" <+> spread (map ppr xs)  paramsMatch :: [TypeParam] -> [TypeParam] -> Bool paramsMatch ps1 ps2 = length ps1 == length ps2 && all match (zip ps1 ps2)-  where match (TypeParamType l1 _ _, TypeParamType l2 _ _) = l1 <= l2-        match (TypeParamDim _ _, TypeParamDim _ _) = True-        match _ = False+  where+    match (TypeParamType l1 _ _, TypeParamType l2 _ _) = l1 <= l2+    match (TypeParamDim _ _, TypeParamDim _ _) = True+    match _ = False -findBinding :: (Env -> M.Map VName v)-            -> Namespace -> Name-            -> Env-            -> Maybe (VName, v)+findBinding ::+  (Env -> M.Map VName v) ->+  Namespace ->+  Name ->+  Env ->+  Maybe (VName, v) findBinding table namespace name the_env = do   QualName _ name' <- M.lookup (namespace, name) $ envNameMap the_env   (name',) <$> M.lookup name' (table the_env)  findTypeDef :: QualName Name -> Mod -> Maybe (QualName VName, TypeBinding)-findTypeDef _ ModFun{} = Nothing+findTypeDef _ ModFun {} = Nothing findTypeDef (QualName [] name) (ModEnv the_env) = do   (name', tb) <- findBinding envTypeTable Type name the_env   return (qualName name', tb)-findTypeDef (QualName (q:qs) name) (ModEnv the_env) = do+findTypeDef (QualName (q : qs) name) (ModEnv the_env) = do   (q', q_mod) <- findBinding envModTable Term q the_env   (QualName qs' name', tb) <- findTypeDef (QualName qs name) q_mod-  return (QualName (q':qs') name', tb)+  return (QualName (q' : qs') name', tb) -resolveAbsTypes :: TySet -> Mod -> TySet -> SrcLoc-                -> Either TypeError (M.Map VName (QualName VName, TypeBinding))+resolveAbsTypes ::+  TySet ->+  Mod ->+  TySet ->+  SrcLoc ->+  Either TypeError (M.Map VName (QualName VName, TypeBinding)) resolveAbsTypes mod_abs mod sig_abs loc = do-  let abs_mapping = M.fromList $ zip-                    (map (fmap baseName . fst) $ M.toList mod_abs) (M.toList mod_abs)-  fmap M.fromList $ forM (M.toList sig_abs) $ \(name, name_l) ->-    case findTypeDef (fmap baseName name) mod of-      Just (name', TypeAbbr mod_l ps t)-        | mod_l > name_l ->-            mismatchedLiftedness name_l-            (map qualLeaf $ M.keys mod_abs) (qualLeaf name) (mod_l, ps, t)-        | name_l < SizeLifted,-          emptyDims t ->-            anonymousSizes (map qualLeaf $ M.keys mod_abs)-            (qualLeaf name) (mod_l, ps, t)-        | Just (abs_name, _) <- M.lookup (fmap baseName name) abs_mapping ->+  let abs_mapping =+        M.fromList $+          zip+            (map (fmap baseName . fst) $ M.toList mod_abs)+            (M.toList mod_abs)+  fmap M.fromList $+    forM (M.toList sig_abs) $ \(name, name_l) ->+      case findTypeDef (fmap baseName name) mod of+        Just (name', TypeAbbr mod_l ps t)+          | mod_l > name_l ->+            mismatchedLiftedness+              name_l+              (map qualLeaf $ M.keys mod_abs)+              (qualLeaf name)+              (mod_l, ps, t)+          | name_l < SizeLifted,+            emptyDims t ->+            anonymousSizes+              (map qualLeaf $ M.keys mod_abs)+              (qualLeaf name)+              (mod_l, ps, t)+          | Just (abs_name, _) <- M.lookup (fmap baseName name) abs_mapping ->             return (qualLeaf name, (abs_name, TypeAbbr name_l ps t))-        | otherwise ->+          | otherwise ->             return (qualLeaf name, (name', TypeAbbr name_l ps t))-      _ ->-        missingType loc $ fmap baseName name-  where mismatchedLiftedness name_l abs name mod_t =-          Left $ TypeError loc mempty $-          "Module defines" </>-          indent 2 (ppTypeAbbr abs name mod_t) </>-          "but module type requires" <+> text what <> "."-          where what = case name_l of Unlifted -> "a non-lifted type"-                                      SizeLifted -> "a size-lifted type"-                                      Lifted -> "a lifted type"+        _ ->+          missingType loc $ fmap baseName name+  where+    mismatchedLiftedness name_l abs name mod_t =+      Left $+        TypeError loc mempty $+          "Module defines"+            </> indent 2 (ppTypeAbbr abs name mod_t)+            </> "but module type requires" <+> text what <> "."+      where+        what = case name_l of+          Unlifted -> "a non-lifted type"+          SizeLifted -> "a size-lifted type"+          Lifted -> "a lifted type" -        anonymousSizes abs name mod_t =-          Left $ TypeError loc mempty $-          "Module defines" </>-          indent 2 (ppTypeAbbr abs name mod_t) </>-          "which contains anonymous sizes, but module type requires non-lifted type."+    anonymousSizes abs name mod_t =+      Left $+        TypeError loc mempty $+          "Module defines"+            </> indent 2 (ppTypeAbbr abs name mod_t)+            </> "which contains anonymous sizes, but module type requires non-lifted type." -        emptyDims :: StructType -> Bool-        emptyDims = isNothing . traverseDims onDim-          where onDim _ PosImmediate AnyDim = Nothing-                onDim _ _ d = Just d+    emptyDims :: StructType -> Bool+    emptyDims = isNothing . traverseDims onDim+      where+        onDim _ PosImmediate AnyDim = Nothing+        onDim _ _ d = Just d -resolveMTyNames :: MTy -> MTy-                -> M.Map VName (QualName VName)+resolveMTyNames ::+  MTy ->+  MTy ->+  M.Map VName (QualName VName) resolveMTyNames = resolveMTyNames'-  where resolveMTyNames' (MTy _mod_abs mod) (MTy _sig_abs sig) =-          resolveModNames mod sig+  where+    resolveMTyNames' (MTy _mod_abs mod) (MTy _sig_abs sig) =+      resolveModNames mod sig -        resolveModNames (ModEnv mod_env) (ModEnv sig_env) =-          resolveEnvNames mod_env sig_env-        resolveModNames (ModFun mod_fun) (ModFun sig_fun) =-          resolveModNames (funSigMod mod_fun) (funSigMod sig_fun) <>-          resolveMTyNames' (funSigMty mod_fun) (funSigMty sig_fun)-        resolveModNames _ _ =-          mempty+    resolveModNames (ModEnv mod_env) (ModEnv sig_env) =+      resolveEnvNames mod_env sig_env+    resolveModNames (ModFun mod_fun) (ModFun sig_fun) =+      resolveModNames (funSigMod mod_fun) (funSigMod sig_fun)+        <> resolveMTyNames' (funSigMty mod_fun) (funSigMty sig_fun)+    resolveModNames _ _ =+      mempty -        resolveEnvNames mod_env sig_env =-          let mod_substs = resolve Term mod_env $ envModTable sig_env-              onMod (modname, mod_env_mod) =-                case M.lookup modname mod_substs of-                  Just (QualName _ modname')-                    | Just sig_env_mod <--                        M.lookup modname' $ envModTable mod_env ->-                      resolveModNames sig_env_mod mod_env_mod-                  _ -> mempty-          in mconcat [ resolve Term mod_env $ envVtable sig_env-                     , resolve Type mod_env $ envVtable sig_env-                     , resolve Signature mod_env $ envVtable sig_env-                     , mod_substs-                     , mconcat $ map onMod $ M.toList $ envModTable sig_env-                     ]+    resolveEnvNames mod_env sig_env =+      let mod_substs = resolve Term mod_env $ envModTable sig_env+          onMod (modname, mod_env_mod) =+            case M.lookup modname mod_substs of+              Just (QualName _ modname')+                | Just sig_env_mod <-+                    M.lookup modname' $ envModTable mod_env ->+                  resolveModNames sig_env_mod mod_env_mod+              _ -> mempty+       in mconcat+            [ resolve Term mod_env $ envVtable sig_env,+              resolve Type mod_env $ envVtable sig_env,+              resolve Signature mod_env $ envVtable sig_env,+              mod_substs,+              mconcat $ map onMod $ M.toList $ envModTable sig_env+            ] -        resolve namespace mod_env = M.mapMaybeWithKey resolve'-          where resolve' name _ =-                  M.lookup (namespace, baseName name) $ envNameMap mod_env+    resolve namespace mod_env = M.mapMaybeWithKey resolve'+      where+        resolve' name _ =+          M.lookup (namespace, baseName name) $ envNameMap mod_env  missingType :: Pretty a => SrcLoc -> a -> Either TypeError b missingType loc name =-  Left $ TypeError loc mempty $-  "Module does not define a type named" <+> ppr name <> "."+  Left $+    TypeError loc mempty $+      "Module does not define a type named" <+> ppr name <> "."  missingVal :: Pretty a => SrcLoc -> a -> Either TypeError b missingVal loc name =-  Left $ TypeError loc mempty $-  "Module does not define a value named" <+> ppr name <> "."+  Left $+    TypeError loc mempty $+      "Module does not define a value named" <+> ppr name <> "."  missingMod :: Pretty a => SrcLoc -> a -> Either TypeError b missingMod loc name =-  Left $ TypeError loc mempty $-  "Module does not define a module named" <+> ppr name <> "."+  Left $+    TypeError loc mempty $+      "Module does not define a module named" <+> ppr name <> "." -mismatchedType :: SrcLoc-               -> [VName]-               -> VName-               -> (Liftedness, [TypeParam], StructType)-               -> (Liftedness, [TypeParam], StructType)-               -> Either TypeError b+mismatchedType ::+  SrcLoc ->+  [VName] ->+  VName ->+  (Liftedness, [TypeParam], StructType) ->+  (Liftedness, [TypeParam], StructType) ->+  Either TypeError b mismatchedType loc abs name spec_t env_t =-  Left $ TypeError loc mempty $-  "Module defines" </>-  indent 2 (ppTypeAbbr abs name env_t) </>-  "but module type requires" </>-  indent 2 (ppTypeAbbr abs name spec_t)+  Left $+    TypeError loc mempty $+      "Module defines"+        </> indent 2 (ppTypeAbbr abs name env_t)+        </> "but module type requires"+        </> indent 2 (ppTypeAbbr abs name spec_t)  ppTypeAbbr :: [VName] -> VName -> (Liftedness, [TypeParam], StructType) -> Doc ppTypeAbbr abs name (l, ps, Scalar (TypeVar () _ tn args))   | typeLeaf tn `elem` abs,     map typeParamToArg ps == args =-      "type" <> ppr l <+> pprName name <+>-      spread (map ppr ps)+    "type" <> ppr l <+> pprName name+      <+> spread (map ppr ps) ppTypeAbbr _ name (l, ps, t) =-  "type" <> ppr l <+> pprName name <+>-  spread (map ppr ps) <+> equals <+/>-  nest 2 (align (ppr t))+  "type" <> ppr l <+> pprName name+    <+> spread (map ppr ps)+    <+> equals+    <+/> nest 2 (align (ppr t))  -- | Return new renamed/abstracted env, as well as a mapping from -- names in the signature to names in the new env.  This is used for -- functor application.  The first env is the module env, and the -- second the env it must match.-matchMTys :: MTy -> MTy -> SrcLoc-          -> Either TypeError (M.Map VName VName)+matchMTys ::+  MTy ->+  MTy ->+  SrcLoc ->+  Either TypeError (M.Map VName VName) matchMTys orig_mty orig_mty_sig =-  matchMTys' (M.map (DimSub . NamedDim) $-              resolveMTyNames orig_mty orig_mty_sig)-  orig_mty orig_mty_sig+  matchMTys'+    ( M.map (DimSub . NamedDim) $+        resolveMTyNames orig_mty orig_mty_sig+    )+    orig_mty+    orig_mty_sig   where-    matchMTys' :: TypeSubs -> MTy -> MTy -> SrcLoc-               -> Either TypeError (M.Map VName VName)--    matchMTys' _ (MTy _ ModFun{}) (MTy _ ModEnv{}) loc =-      Left $ TypeError loc mempty-      "Cannot match parametric module with non-parametric module type."--    matchMTys' _ (MTy _ ModEnv{}) (MTy _ ModFun{}) loc =-      Left $ TypeError loc mempty-      "Cannot match non-parametric module with paramatric module type."+    matchMTys' ::+      TypeSubs ->+      MTy ->+      MTy ->+      SrcLoc ->+      Either TypeError (M.Map VName VName) +    matchMTys' _ (MTy _ ModFun {}) (MTy _ ModEnv {}) loc =+      Left $+        TypeError+          loc+          mempty+          "Cannot match parametric module with non-parametric module type."+    matchMTys' _ (MTy _ ModEnv {}) (MTy _ ModFun {}) loc =+      Left $+        TypeError+          loc+          mempty+          "Cannot match non-parametric module with paramatric module type."     matchMTys' old_abs_subst_to_type (MTy mod_abs mod) (MTy sig_abs sig) loc = do       -- Check that abstract types in 'sig' have an implementation in       -- 'mod'.  This also gives us a substitution that we use to check       -- the types of values.       abs_substs <- resolveAbsTypes mod_abs mod sig_abs loc -      let abs_subst_to_type = old_abs_subst_to_type <>-                              M.map (TypeSub . snd) abs_substs-          abs_name_substs   = M.map (qualLeaf . fst) abs_substs+      let abs_subst_to_type =+            old_abs_subst_to_type+              <> M.map (TypeSub . snd) abs_substs+          abs_name_substs = M.map (qualLeaf . fst) abs_substs       substs <- matchMods abs_subst_to_type mod sig loc       return (substs <> abs_name_substs) -    matchMods :: TypeSubs -> Mod -> Mod -> SrcLoc-              -> Either TypeError (M.Map VName VName)-    matchMods _ ModEnv{} ModFun{} loc =-      Left $ TypeError loc mempty-      "Cannot match non-parametric module with parametric module type."-    matchMods _ ModFun{} ModEnv{} loc =-      Left $ TypeError loc mempty-      "Cannot match parametric module with non-parametric module type."-+    matchMods ::+      TypeSubs ->+      Mod ->+      Mod ->+      SrcLoc ->+      Either TypeError (M.Map VName VName)+    matchMods _ ModEnv {} ModFun {} loc =+      Left $+        TypeError+          loc+          mempty+          "Cannot match non-parametric module with parametric module type."+    matchMods _ ModFun {} ModEnv {} loc =+      Left $+        TypeError+          loc+          mempty+          "Cannot match parametric module with non-parametric module type."     matchMods abs_subst_to_type (ModEnv mod) (ModEnv sig) loc =       matchEnvs abs_subst_to_type mod sig loc--    matchMods old_abs_subst_to_type-              (ModFun (FunSig mod_abs mod_pmod mod_mod))-              (ModFun (FunSig sig_abs sig_pmod sig_mod))-              loc = do-      abs_substs <- resolveAbsTypes mod_abs mod_pmod sig_abs loc-      let abs_subst_to_type = old_abs_subst_to_type <>-                              M.map (TypeSub . snd) abs_substs-          abs_name_substs   = M.map (qualLeaf . fst) abs_substs-      pmod_substs <- matchMods abs_subst_to_type mod_pmod sig_pmod loc-      mod_substs <- matchMTys' abs_subst_to_type mod_mod sig_mod loc-      return (pmod_substs <> mod_substs <> abs_name_substs)+    matchMods+      old_abs_subst_to_type+      (ModFun (FunSig mod_abs mod_pmod mod_mod))+      (ModFun (FunSig sig_abs sig_pmod sig_mod))+      loc = do+        abs_substs <- resolveAbsTypes mod_abs mod_pmod sig_abs loc+        let abs_subst_to_type =+              old_abs_subst_to_type+                <> M.map (TypeSub . snd) abs_substs+            abs_name_substs = M.map (qualLeaf . fst) abs_substs+        pmod_substs <- matchMods abs_subst_to_type mod_pmod sig_pmod loc+        mod_substs <- matchMTys' abs_subst_to_type mod_mod sig_mod loc+        return (pmod_substs <> mod_substs <> abs_name_substs) -    matchEnvs :: TypeSubs-              -> Env -> Env -> SrcLoc-              -> Either TypeError (M.Map VName VName)+    matchEnvs ::+      TypeSubs ->+      Env ->+      Env ->+      SrcLoc ->+      Either TypeError (M.Map VName VName)     matchEnvs abs_subst_to_type env sig loc = do       -- XXX: we only want to create substitutions for visible names.       -- This must be wrong in some cases.  Probably we need to@@ -400,35 +489,49 @@        -- Check that all type abbreviations are correctly defined.       abbr_name_substs <- fmap M.fromList $-                          forM (filter (isVisible . fst) $ M.toList $-                                envTypeTable sig) $ \(name, TypeAbbr spec_l spec_ps spec_t) ->-        case findBinding envTypeTable Type (baseName name) env of-          Just (name', TypeAbbr l ps t) ->-            matchTypeAbbr loc abs_subst_to_type name spec_l spec_ps spec_t name' l ps t-          Nothing -> missingType loc $ baseName name+        forM+          ( filter (isVisible . fst) $+              M.toList $+                envTypeTable sig+          )+          $ \(name, TypeAbbr spec_l spec_ps spec_t) ->+            case findBinding envTypeTable Type (baseName name) env of+              Just (name', TypeAbbr l ps t) ->+                matchTypeAbbr loc abs_subst_to_type name spec_l spec_ps spec_t name' l ps t+              Nothing -> missingType loc $ baseName name        -- Check that all values are defined correctly, substituting the       -- abstract types first.-      val_substs <- fmap M.fromList $ forM (M.toList $ envVtable sig) $ \(name, spec_bv) -> do-        let spec_bv' = substituteTypesInBoundV abs_subst_to_type spec_bv-        case findBinding envVtable Term (baseName name) env of-          Just (name', bv) -> matchVal loc name spec_bv' name' bv-          _ -> missingVal loc (baseName name)+      val_substs <- fmap M.fromList $+        forM (M.toList $ envVtable sig) $ \(name, spec_bv) -> do+          let spec_bv' = substituteTypesInBoundV abs_subst_to_type spec_bv+          case findBinding envVtable Term (baseName name) env of+            Just (name', bv) -> matchVal loc name spec_bv' name' bv+            _ -> missingVal loc (baseName name)        -- Check for correct modules.-      mod_substs <- fmap M.unions $ forM (M.toList $ envModTable sig) $ \(name, modspec) ->-        case findBinding envModTable Term (baseName name) env of-          Just (name', mod) ->-            M.insert name name' <$> matchMods abs_subst_to_type mod modspec loc-          Nothing ->-            missingMod loc $ baseName name+      mod_substs <- fmap M.unions $+        forM (M.toList $ envModTable sig) $ \(name, modspec) ->+          case findBinding envModTable Term (baseName name) env of+            Just (name', mod) ->+              M.insert name name' <$> matchMods abs_subst_to_type mod modspec loc+            Nothing ->+              missingMod loc $ baseName name        return $ val_substs <> mod_substs <> abbr_name_substs -    matchTypeAbbr :: SrcLoc -> TypeSubs-                  -> VName -> Liftedness -> [TypeParam] -> StructType-                  -> VName -> Liftedness -> [TypeParam] -> StructType-                  -> Either TypeError (VName, VName)+    matchTypeAbbr ::+      SrcLoc ->+      TypeSubs ->+      VName ->+      Liftedness ->+      [TypeParam] ->+      StructType ->+      VName ->+      Liftedness ->+      [TypeParam] ->+      StructType ->+      Either TypeError (VName, VName)     matchTypeAbbr loc abs_subst_to_type spec_name spec_l spec_ps spec_t name l ps t = do       -- We have to create substitutions for the type parameters, too.       unless (length spec_ps == length ps) $ nomatch spec_t@@ -440,44 +543,63 @@       when (M.member spec_name abs_subst_to_type) $         case S.toList (mustBeExplicitInType t) `intersect` map typeParamName ps of           [] -> return ()-          d:_ -> Left $ TypeError loc mempty $-                 "Type" </>-                 indent 2 (ppTypeAbbr [] name (l, ps, t)) </>-                 textwrap "cannot be made abstract because size parameter" <+/> pquote (pprName d) <+/>-                 textwrap "is not used as an array size in the definition."+          d : _ ->+            Left $+              TypeError loc mempty $+                "Type"+                  </> indent 2 (ppTypeAbbr [] name (l, ps, t))+                  </> textwrap "cannot be made abstract because size parameter"+                  <+/> pquote (pprName d)+                  <+/> textwrap "is not used as an array size in the definition." -      let spec_t' = substituteTypes (param_substs<>abs_subst_to_type) spec_t+      let spec_t' = substituteTypes (param_substs <> abs_subst_to_type) spec_t       if spec_t' == t         then return (spec_name, name)         else nomatch spec_t'-        where nomatch spec_t' = mismatchedType loc (M.keys abs_subst_to_type)-                                spec_name (spec_l, spec_ps, spec_t') (l, ps, t)+      where+        nomatch spec_t' =+          mismatchedType+            loc+            (M.keys abs_subst_to_type)+            spec_name+            (spec_l, spec_ps, spec_t')+            (l, ps, t) -              matchTypeParam (TypeParamDim x _) (TypeParamDim y _) =-                pure $ M.singleton x $ DimSub $ NamedDim $ qualName y-              matchTypeParam (TypeParamType Unlifted x _) (TypeParamType Unlifted y _) =-                pure $ M.singleton x $ TypeSub $ TypeAbbr Unlifted [] $-                Scalar $ TypeVar () Nonunique (typeName y) []-              matchTypeParam (TypeParamType _ x _) (TypeParamType Lifted y _) =-                pure $ M.singleton x $ TypeSub $ TypeAbbr Lifted [] $-                Scalar $ TypeVar () Nonunique (typeName y) []-              matchTypeParam _ _ =-                nomatch spec_t+        matchTypeParam (TypeParamDim x _) (TypeParamDim y _) =+          pure $ M.singleton x $ DimSub $ NamedDim $ qualName y+        matchTypeParam (TypeParamType Unlifted x _) (TypeParamType Unlifted y _) =+          pure $+            M.singleton x $+              TypeSub $+                TypeAbbr Unlifted [] $+                  Scalar $ TypeVar () Nonunique (typeName y) []+        matchTypeParam (TypeParamType _ x _) (TypeParamType Lifted y _) =+          pure $+            M.singleton x $+              TypeSub $+                TypeAbbr Lifted [] $+                  Scalar $ TypeVar () Nonunique (typeName y) []+        matchTypeParam _ _ =+          nomatch spec_t -    matchVal :: SrcLoc-             -> VName -> BoundV-             -> VName -> BoundV-             -> Either TypeError (VName, VName)+    matchVal ::+      SrcLoc ->+      VName ->+      BoundV ->+      VName ->+      BoundV ->+      Either TypeError (VName, VName)     matchVal loc spec_name spec_v name v =       case matchValBinding loc spec_v v of         Nothing -> return (spec_name, name)         Just problem ->-          Left $ TypeError loc mempty $-          "Module type specifies" </>-          indent 2 (ppValBind spec_name spec_v) </>-          "but module provides" </>-          indent 2 (ppValBind spec_name v) </>-          fromMaybe mempty problem+          Left $+            TypeError loc mempty $+              "Module type specifies"+                </> indent 2 (ppValBind spec_name spec_v)+                </> "but module provides"+                </> indent 2 (ppValBind spec_name v)+                </> fromMaybe mempty problem      matchValBinding :: SrcLoc -> BoundV -> BoundV -> Maybe (Maybe Doc)     matchValBinding loc (BoundV _ orig_spec_t) (BoundV tps orig_t) =@@ -486,27 +608,34 @@           Just $ Just $ msg <> ppr notes         -- Even if they unify, we still have to verify the uniqueness         -- properties.-        Right t | noSizes t `subtypeOf`-                  noSizes orig_spec_t -> Nothing-                | otherwise -> Just Nothing+        Right t+          | noSizes t+              `subtypeOf` noSizes orig_spec_t ->+            Nothing+          | otherwise -> Just Nothing      ppValBind v (BoundV tps t) =-      "val" <+> pprName v <+> spread (map ppr tps) <+> colon </>-      indent 2 (align (ppr t))+      "val" <+> pprName v <+> spread (map ppr tps) <+> colon+        </> indent 2 (align (ppr t))  -- | Apply a parametric module to an argument.-applyFunctor :: SrcLoc -> FunSig -> MTy-             -> TypeM (MTy,-                       M.Map VName VName,-                       M.Map VName VName)+applyFunctor ::+  SrcLoc ->+  FunSig ->+  MTy ->+  TypeM+    ( MTy,+      M.Map VName VName,+      M.Map VName VName+    ) applyFunctor applyloc (FunSig p_abs p_mod body_mty) a_mty = do   p_subst <- badOnLeft $ matchMTys a_mty (MTy p_abs p_mod) applyloc    -- Apply type abbreviations from a_mty to body_mty.   let a_abbrs = mtyTypeAbbrs a_mty       isSub v = case M.lookup v a_abbrs of-                  Just abbr -> Just $ TypeSub abbr-                  _  -> Just $ DimSub $ NamedDim $ qualName v+        Just abbr -> Just $ TypeSub abbr+        _ -> Just $ DimSub $ NamedDim $ qualName v       type_subst = M.mapMaybe isSub p_subst       body_mty' = substituteTypesInMTy type_subst body_mty   (body_mty'', body_subst) <- newNamesForMTy body_mty'
src/Language/Futhark/TypeChecker/Monad.hs view
@@ -1,78 +1,67 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TupleSections #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TupleSections #-}+ -- | Main monad in which the type checker runs, as well as ancillary -- data definitions. module Language.Futhark.TypeChecker.Monad-  ( TypeM-  , runTypeM-  , askEnv-  , askImportName-  , bindSpaced-  , qualifyTypeVars-  , lookupMTy-  , lookupImport-  , localEnv--  , TypeError(..)-  , unappliedFunctor-  , unknownVariable-  , unknownType-  , underscoreUse-  , Notes-  , aNote--  , MonadTypeChecker(..)-  , checkName-  , badOnLeft--  , module Language.Futhark.Warnings--  , Env(..)-  , TySet-  , FunSig(..)-  , ImportTable-  , NameMap-  , BoundV(..)-  , Mod(..)-  , TypeBinding(..)-  , MTy(..)--  , anySignedType-  , anyUnsignedType-  , anyIntType-  , anyFloatType-  , anyNumberType-  , anyPrimType--  , Namespace(..)-  , intrinsicsNameMap-  , topLevelNameMap+  ( TypeM,+    runTypeM,+    askEnv,+    askImportName,+    bindSpaced,+    qualifyTypeVars,+    lookupMTy,+    lookupImport,+    localEnv,+    TypeError (..),+    unappliedFunctor,+    unknownVariable,+    unknownType,+    underscoreUse,+    Notes,+    aNote,+    MonadTypeChecker (..),+    checkName,+    badOnLeft,+    module Language.Futhark.Warnings,+    Env (..),+    TySet,+    FunSig (..),+    ImportTable,+    NameMap,+    BoundV (..),+    Mod (..),+    TypeBinding (..),+    MTy (..),+    anySignedType,+    anyUnsignedType,+    anyFloatType,+    anyNumberType,+    anyPrimType,+    Namespace (..),+    intrinsicsNameMap,+    topLevelNameMap,   ) where  import Control.Monad.Except-import Control.Monad.Reader-import Control.Monad.Writer hiding (Sum)-import Control.Monad.State import Control.Monad.RWS.Strict hiding (Sum)-import Data.List (isPrefixOf, find)-import Data.Maybe import Data.Either+import Data.List (find, isPrefixOf) import qualified Data.Map.Strict as M+import Data.Maybe import qualified Data.Set as S--import Prelude hiding (mapM, mod)--import Language.Futhark-import Language.Futhark.Semantic-import Language.Futhark.Warnings import Futhark.FreshNames hiding (newName) import qualified Futhark.FreshNames-import Futhark.Util.Pretty hiding (space) import Futhark.Util.Console+import Futhark.Util.Pretty hiding (space)+import Language.Futhark+import Language.Futhark.Semantic+import Language.Futhark.Warnings+import Prelude hiding (mapM, mod)  -- | A note with extra information regarding a type error. newtype Note = Note Doc@@ -85,7 +74,7 @@   ppr (Note msg) = "Note:" <+> align msg  instance Pretty Notes where-  ppr (Notes notes) = foldMap (((line<>line)<>) . ppr) notes+  ppr (Notes notes) = foldMap (((line <> line) <>) . ppr) notes  -- | A single note. aNote :: Pretty a => a -> Notes@@ -96,8 +85,8 @@  instance Pretty TypeError where   ppr (TypeError loc notes msg) =-    text (inRed $ "Error at " <> locStr loc <> ":") </>-    msg <> ppr notes+    text (inRed $ "Error at " <> locStr loc <> ":")+      </> msg <> ppr notes  -- | An unexpected functor appeared! unappliedFunctor :: MonadTypeChecker m => SrcLoc -> m a@@ -105,11 +94,15 @@   typeError loc mempty "Cannot have parametric module here."  -- | An unknown variable was referenced.-unknownVariable :: MonadTypeChecker m =>-                   Namespace -> QualName Name -> SrcLoc -> m a+unknownVariable ::+  MonadTypeChecker m =>+  Namespace ->+  QualName Name ->+  SrcLoc ->+  m a unknownVariable space name loc =   typeError loc mempty $-  "Unknown" <+> ppr space <+> pquote (ppr name)+    "Unknown" <+> ppr space <+> pquote (ppr name)  -- | An unknown type was referenced. unknownType :: MonadTypeChecker m => SrcLoc -> QualName Name -> m a@@ -117,38 +110,54 @@   typeError loc mempty $ "Unknown type" <+> ppr name <> "."  -- | A name prefixed with an underscore was used.-underscoreUse :: MonadTypeChecker m =>-                 SrcLoc -> QualName Name -> m a+underscoreUse ::+  MonadTypeChecker m =>+  SrcLoc ->+  QualName Name ->+  m a underscoreUse loc name =-  typeError loc mempty $ "Use of" <+> pquote (ppr name) <>-  ": variables prefixed with underscore may not be accessed."+  typeError loc mempty $+    "Use of" <+> pquote (ppr name)+      <> ": variables prefixed with underscore may not be accessed."  -- | A mapping from import strings to 'Env's.  This is used to resolve -- @import@ declarations. type ImportTable = M.Map String Env -data Context = Context { contextEnv :: Env-                       , contextImportTable :: ImportTable-                       , contextImportName :: ImportName-                       }+data Context = Context+  { contextEnv :: Env,+    contextImportTable :: ImportTable,+    contextImportName :: ImportName+  }  -- | The type checker runs in this monad.-newtype TypeM a = TypeM (RWST-                         Context            -- Reader-                         Warnings           -- Writer-                         VNameSource        -- State-                         (Except TypeError) -- Inner monad-                         a)-  deriving (Monad, Functor, Applicative,-            MonadReader Context,-            MonadWriter Warnings,-            MonadState VNameSource,-            MonadError TypeError)+newtype TypeM a+  = TypeM+      ( RWST+          Context -- Reader+          Warnings -- Writer+          VNameSource -- State+          (Except TypeError) -- Inner monad+          a+      )+  deriving+    ( Monad,+      Functor,+      Applicative,+      MonadReader Context,+      MonadWriter Warnings,+      MonadState VNameSource,+      MonadError TypeError+    )  -- | Run a 'TypeM' computation.-runTypeM :: Env -> ImportTable -> ImportName -> VNameSource-         -> TypeM a-         -> Either TypeError (a, Warnings, VNameSource)+runTypeM ::+  Env ->+  ImportTable ->+  ImportName ->+  VNameSource ->+  TypeM a ->+  Either TypeError (a, Warnings, VNameSource) runTypeM env imports fpath src (TypeM m) = do   (x, src', ws) <- runExcept $ runRWST m (Context env imports fpath) src   return (x, ws, src')@@ -166,7 +175,8 @@ lookupMTy loc qn = do   (scope, qn'@(QualName _ name)) <- checkQualNameWithEnv Signature qn loc   (qn',) <$> maybe explode return (M.lookup name $ envSigTable scope)-  where explode = unknownVariable Signature qn loc+  where+    explode = unknownVariable Signature qn loc  -- | Look up an import. lookupImport :: SrcLoc -> FilePath -> TypeM (FilePath, Env)@@ -175,9 +185,10 @@   my_path <- asks contextImportName   let canonical_import = includeToString $ mkImportFrom my_path file loc   case M.lookup canonical_import imports of-    Nothing    -> typeError loc mempty $-                  "Unknown import" <+> dquotes (text canonical_import) </>-                  "Known:" <+> commasep (map text (M.keys imports))+    Nothing ->+      typeError loc mempty $+        "Unknown import" <+> dquotes (text canonical_import)+          </> "Known:" <+> commasep (map text (M.keys imports))     Just scope -> return (canonical_import, scope)  -- | Evaluate a 'TypeM' computation within an extended (/not/@@ -185,7 +196,7 @@ localEnv :: Env -> TypeM a -> TypeM a localEnv env = local $ \ctx ->   let env' = env <> contextEnv ctx-  in ctx { contextEnv = env' }+   in ctx {contextEnv = env'}  -- | Monads that support type checking.  The reason we have this -- internal interface is because we use distinct monads for checking@@ -209,9 +220,10 @@   checkNamedDim loc v = do     (v', t) <- lookupVar loc v     case t of-      Scalar (Prim (Signed Int32)) -> return v'-      _ -> typeError loc mempty $-           "Dimension declaration" <+> ppr v <+> "should be of type i32."+      Scalar (Prim (Signed Int64)) -> return v'+      _ ->+        typeError loc mempty $+          "Dimension declaration" <+> ppr v <+> "should be of type i64."    typeError :: Located loc => loc -> Notes -> Doc -> m a @@ -231,20 +243,25 @@ instance MonadTypeChecker TypeM where   warn loc problem = tell $ singleWarning (srclocOf loc) problem -  newName s = do src <- get-                 let (s', src') = Futhark.FreshNames.newName src s-                 put src'-                 return s'+  newName s = do+    src <- get+    let (s', src') = Futhark.FreshNames.newName src s+    put src'+    return s'    newID s = newName $ VName s 0    bindNameMap m = local $ \ctx ->     let env = contextEnv ctx-    in ctx { contextEnv = env { envNameMap = m <> envNameMap env } }+     in ctx {contextEnv = env {envNameMap = m <> envNameMap env}}    bindVal v t = local $ \ctx ->-    ctx { contextEnv = (contextEnv ctx)-                       { envVtable = M.insert v t $ envVtable $ contextEnv ctx } }+    ctx+      { contextEnv =+          (contextEnv ctx)+            { envVtable = M.insert v t $ envVtable $ contextEnv ctx+            }+      }    checkQualName space name loc = snd <$> checkQualNameWithEnv space name loc @@ -259,7 +276,7 @@     (scope, qn'@(QualName _ name)) <- checkQualNameWithEnv Term qn loc     case M.lookup name $ envModTable scope of       Nothing -> unknownVariable Term qn loc-      Just m  -> return (qn', m)+      Just m -> return (qn', m)    lookupVar loc qn = do     outer_env <- askEnv@@ -269,19 +286,26 @@       Just (BoundV _ t)         | "_" `isPrefixOf` baseString name -> underscoreUse loc qn         | otherwise ->-            case getType t of-              Left{} -> typeError loc mempty $-                        "Attempt to use function" <+> pprName name <+> "as value."-              Right t' -> return (qn', fromStruct $-                                       qualifyTypeVars outer_env mempty qs t')+          case getType t of+            Left {} ->+              typeError loc mempty $+                "Attempt to use function" <+> pprName name <+> "as value."+            Right t' ->+              return+                ( qn',+                  fromStruct $+                    qualifyTypeVars outer_env mempty qs t'+                )    typeError loc notes s = throwError $ TypeError (srclocOf loc) notes s  -- | Extract from a type either a function type comprising a list of -- parameter types and a return type, or a first-order type.-getType :: TypeBase dim as-        -> Either ([(PName, TypeBase dim as)], TypeBase dim as)-                  (TypeBase dim as)+getType ::+  TypeBase dim as ->+  Either+    ([(PName, TypeBase dim as)], TypeBase dim as)+    (TypeBase dim as) getType (Scalar (Arrow _ v t1 t2)) =   case getType t2 of     Left (ps, r) -> Left ((v, t1) : ps, r)@@ -292,79 +316,89 @@ checkQualNameWithEnv space qn@(QualName quals name) loc = do   env <- askEnv   descend env quals-  where descend scope []-          | Just name' <- M.lookup (space, name) $ envNameMap scope =-              return (scope, name')-          | otherwise =-              unknownVariable space qn loc--        descend scope (q:qs)-          | Just (QualName _ q') <- M.lookup (Term, q) $ envNameMap scope,-            Just res <- M.lookup q' $ envModTable scope =-              case res of-                ModEnv q_scope -> do-                  (scope', QualName qs' name') <- descend q_scope qs-                  return (scope', QualName (q':qs') name')-                ModFun{} -> unappliedFunctor loc-          | otherwise =-              unknownVariable space qn loc+  where+    descend scope []+      | Just name' <- M.lookup (space, name) $ envNameMap scope =+        return (scope, name')+      | otherwise =+        unknownVariable space qn loc+    descend scope (q : qs)+      | Just (QualName _ q') <- M.lookup (Term, q) $ envNameMap scope,+        Just res <- M.lookup q' $ envModTable scope =+        case res of+          ModEnv q_scope -> do+            (scope', QualName qs' name') <- descend q_scope qs+            return (scope', QualName (q' : qs') name')+          ModFun {} -> unappliedFunctor loc+      | otherwise =+        unknownVariable space qn loc  -- | Try to prepend qualifiers to the type names such that they -- represent how to access the type in some scope.-qualifyTypeVars :: Env -> [VName] -> [VName] -> TypeBase (DimDecl VName) as-                -> TypeBase (DimDecl VName) as+qualifyTypeVars ::+  Env ->+  [VName] ->+  [VName] ->+  TypeBase (DimDecl VName) as ->+  TypeBase (DimDecl VName) as qualifyTypeVars outer_env orig_except ref_qs = onType (S.fromList orig_except)-  where onType :: S.Set VName -> TypeBase (DimDecl VName) as-               -> TypeBase (DimDecl VName) as-        onType except (Array as u et shape) =-          Array as u (onScalar except et) (fmap (onDim except) shape)-        onType except (Scalar t) =-          Scalar $ onScalar except t--        onScalar _ (Prim t) = Prim t-        onScalar except (TypeVar as u tn targs) =-          TypeVar as u tn' $ map (onTypeArg except) targs-          where tn' = typeNameFromQualName $ qual except $ qualNameFromTypeName tn-        onScalar except (Record m) =-          Record $ M.map (onType except) m-        onScalar except (Sum m) =-          Sum $ M.map (map $ onType except) m-        onScalar except (Arrow as p t1 t2) =-          Arrow as p (onType except' t1) (onType except' t2)-          where except' = case p of Named p' -> S.insert p' except-                                    Unnamed -> except+  where+    onType ::+      S.Set VName ->+      TypeBase (DimDecl VName) as ->+      TypeBase (DimDecl VName) as+    onType except (Array as u et shape) =+      Array as u (onScalar except et) (fmap (onDim except) shape)+    onType except (Scalar t) =+      Scalar $ onScalar except t -        onTypeArg except (TypeArgDim d loc) =-          TypeArgDim (onDim except d) loc-        onTypeArg except (TypeArgType t loc) =-          TypeArgType (onType except t) loc+    onScalar _ (Prim t) = Prim t+    onScalar except (TypeVar as u tn targs) =+      TypeVar as u tn' $ map (onTypeArg except) targs+      where+        tn' = typeNameFromQualName $ qual except $ qualNameFromTypeName tn+    onScalar except (Record m) =+      Record $ M.map (onType except) m+    onScalar except (Sum m) =+      Sum $ M.map (map $ onType except) m+    onScalar except (Arrow as p t1 t2) =+      Arrow as p (onType except' t1) (onType except' t2)+      where+        except' = case p of+          Named p' -> S.insert p' except+          Unnamed -> except -        onDim except (NamedDim qn) = NamedDim $ qual except qn-        onDim _ d = d+    onTypeArg except (TypeArgDim d loc) =+      TypeArgDim (onDim except d) loc+    onTypeArg except (TypeArgType t loc) =+      TypeArgType (onType except t) loc -        qual except (QualName orig_qs name)-          | name `elem` except || reachable orig_qs name outer_env =-              QualName orig_qs name-          | otherwise =-              prependAsNecessary [] ref_qs $ QualName orig_qs name+    onDim except (NamedDim qn) = NamedDim $ qual except qn+    onDim _ d = d -        prependAsNecessary qs rem_qs (QualName orig_qs name)-          | reachable (qs++orig_qs) name outer_env = QualName (qs++orig_qs) name-          | otherwise = case rem_qs of-                          q:rem_qs' -> prependAsNecessary (qs++[q]) rem_qs' (QualName orig_qs name)-                          []       -> QualName (qs++orig_qs) name+    qual except (QualName orig_qs name)+      | name `elem` except || reachable orig_qs name outer_env =+        QualName orig_qs name+      | otherwise =+        prependAsNecessary [] ref_qs $ QualName orig_qs name -        reachable [] name env =-          name `M.member` envVtable env ||-          isJust (find matches $ M.elems (envTypeTable env))-          where matches (TypeAbbr _ _ (Scalar (TypeVar _ _ (TypeName x_qs name') _))) =-                  null x_qs && name == name'-                matches _ = False+    prependAsNecessary qs rem_qs (QualName orig_qs name)+      | reachable (qs ++ orig_qs) name outer_env = QualName (qs ++ orig_qs) name+      | otherwise = case rem_qs of+        q : rem_qs' -> prependAsNecessary (qs ++ [q]) rem_qs' (QualName orig_qs name)+        [] -> QualName (qs ++ orig_qs) name -        reachable (q:qs') name env-          | Just (ModEnv env') <- M.lookup q $ envModTable env =-              reachable qs' name env'-          | otherwise = False+    reachable [] name env =+      name `M.member` envVtable env+        || isJust (find matches $ M.elems (envTypeTable env))+      where+        matches (TypeAbbr _ _ (Scalar (TypeVar _ _ (TypeName x_qs name') _))) =+          null x_qs && name == name'+        matches _ = False+    reachable (q : qs') name env+      | Just (ModEnv env') <- M.lookup q $ envModTable env =+        reachable qs' name env'+      | otherwise = False  -- | Turn a 'Left' 'TypeError' into an actual error. badOnLeft :: Either TypeError a -> TypeM a@@ -399,18 +433,24 @@ -- | The 'NameMap' corresponding to the intrinsics module. intrinsicsNameMap :: NameMap intrinsicsNameMap = M.fromList $ map mapping $ M.toList intrinsics-  where mapping (v, IntrinsicType{}) = ((Type, baseName v), QualName [] v)-        mapping (v, _)               = ((Term, baseName v), QualName [] v)+  where+    mapping (v, IntrinsicType {}) = ((Type, baseName v), QualName [] v)+    mapping (v, _) = ((Term, baseName v), QualName [] v)  -- | The names that are available in the initial environment. topLevelNameMap :: NameMap topLevelNameMap = M.filterWithKey (\k _ -> atTopLevel k) intrinsicsNameMap-  where atTopLevel :: (Namespace, Name) -> Bool-        atTopLevel (Type, _) = True-        atTopLevel (Term, v) = v `S.member` (type_names <> binop_names <> unop_names <> fun_names)-          where type_names = S.fromList $ map (nameFromString . pretty) anyPrimType-                binop_names = S.fromList $ map (nameFromString . pretty)-                              [minBound..(maxBound::BinOp)]-                unop_names = S.fromList $ map nameFromString ["!"]-                fun_names = S.fromList $ map nameFromString ["shape"]-        atTopLevel _         = False+  where+    atTopLevel :: (Namespace, Name) -> Bool+    atTopLevel (Type, _) = True+    atTopLevel (Term, v) = v `S.member` (type_names <> binop_names <> unop_names <> fun_names)+      where+        type_names = S.fromList $ map (nameFromString . pretty) anyPrimType+        binop_names =+          S.fromList $+            map+              (nameFromString . pretty)+              [minBound .. (maxBound :: BinOp)]+        unop_names = S.fromList $ map nameFromString ["!"]+        fun_names = S.fromList $ map nameFromString ["shape"]+    atTopLevel _ = False
src/Language/Futhark/TypeChecker/Terms.hs view
@@ -1,3024 +1,3311 @@-{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances, DeriveFunctor #-}-{-# Language TupleSections #-}-{-# Language OverloadedStrings #-}-{-# LANGUAGE Trustworthy #-}--- | Facilities for type-checking Futhark terms.  Checking a term--- requires a little more context to track uniqueness and such.------ Type inference is implemented through a variation of--- Hindley-Milner.  The main complication is supporting the rich--- number of built-in language constructs, as well as uniqueness--- types.  This is mostly done in an ad hoc way, and many programs--- will require the programmer to fall back on type annotations.-module Language.Futhark.TypeChecker.Terms-  ( checkOneExp-  , checkFunDef-  )-where--import Control.Monad.Identity-import Control.Monad.Except-import Control.Monad.State-import Control.Monad.RWS hiding (Sum)-import Control.Monad.Writer hiding (Sum)-import Data.Bifunctor-import Data.Char (isAscii)-import Data.Either-import Data.List (isPrefixOf, foldl', find, (\\), nub, transpose, sort, group)-import qualified Data.List.NonEmpty as NE-import Data.Maybe-import qualified Data.Map.Strict as M-import qualified Data.Set as S--import Prelude hiding (mod)--import Language.Futhark hiding (unscopeType)-import Language.Futhark.Semantic (includeToString)-import Language.Futhark.Traversals-import Language.Futhark.TypeChecker.Monad hiding (BoundV)-import Language.Futhark.TypeChecker.Types hiding (checkTypeDecl)-import Language.Futhark.TypeChecker.Unify hiding (Usage)-import qualified Language.Futhark.TypeChecker.Types as Types-import qualified Language.Futhark.TypeChecker.Monad as TypeM-import Futhark.IR.Primitive (intByteSize)-import Futhark.Util.Pretty hiding (space, bool, group)----- Uniqueness--data Usage = Consumed SrcLoc-           | Observed SrcLoc-           deriving (Eq, Ord, Show)--type Names = S.Set VName---- | The consumption set is a Maybe so we can distinguish whether a--- consumption took place, but the variable went out of scope since,--- or no consumption at all took place.-data Occurence = Occurence { observed :: Names-                           , consumed :: Maybe Names-                           , location :: SrcLoc-                           }-             deriving (Eq, Show)--instance Located Occurence where-  locOf = locOf . location--observation :: Aliasing -> SrcLoc -> Occurence-observation = flip Occurence Nothing . S.map aliasVar--consumption :: Aliasing -> SrcLoc -> Occurence-consumption = Occurence S.empty . Just . S.map aliasVar---- | A null occurence is one that we can remove without affecting--- anything.-nullOccurence :: Occurence -> Bool-nullOccurence occ = S.null (observed occ) && isNothing (consumed occ)---- | A seminull occurence is one that does not contain references to--- any variables in scope.  The big difference is that a seminull--- occurence may denote a consumption, as long as the array that was--- consumed is now out of scope.-seminullOccurence :: Occurence -> Bool-seminullOccurence occ = S.null (observed occ) && maybe True S.null (consumed occ)--type Occurences = [Occurence]--type UsageMap = M.Map VName [Usage]--usageMap :: Occurences -> UsageMap-usageMap = foldl comb M.empty-  where comb m (Occurence obs cons loc) =-          let m' = S.foldl' (ins $ Observed loc) m obs-          in S.foldl' (ins $ Consumed loc) m' $ fromMaybe mempty cons-        ins v m k = M.insertWith (++) k [v] m--combineOccurences :: VName -> Usage -> Usage -> TermTypeM Usage-combineOccurences _ (Observed loc) (Observed _) = return $ Observed loc-combineOccurences name (Consumed wloc) (Observed rloc) =-  useAfterConsume (baseName name) rloc wloc-combineOccurences name (Observed rloc) (Consumed wloc) =-  useAfterConsume (baseName name) rloc wloc-combineOccurences name (Consumed loc1) (Consumed loc2) =-  consumeAfterConsume (baseName name) (max loc1 loc2) (min loc1 loc2)--checkOccurences :: Occurences -> TermTypeM ()-checkOccurences = void . M.traverseWithKey comb . usageMap-  where comb _    []     = return ()-        comb name (u:us) = foldM_ (combineOccurences name) u us--allObserved :: Occurences -> Names-allObserved = S.unions . map observed--allConsumed :: Occurences -> Names-allConsumed = S.unions . map (fromMaybe mempty . consumed)--allOccuring :: Occurences -> Names-allOccuring occs = allConsumed occs <> allObserved occs--anyConsumption :: Occurences -> Maybe Occurence-anyConsumption = find (isJust . consumed)--seqOccurences :: Occurences -> Occurences -> Occurences-seqOccurences occurs1 occurs2 =-  filter (not . nullOccurence) $ map filt occurs1 ++ occurs2-  where filt occ =-          occ { observed = observed occ `S.difference` postcons }-        postcons = allConsumed occurs2--altOccurences :: Occurences -> Occurences -> Occurences-altOccurences occurs1 occurs2 =-  filter (not . nullOccurence) $ map filt1 occurs1 ++ map filt2 occurs2-  where filt1 occ =-          occ { consumed = S.difference <$> consumed occ <*> pure cons2-              , observed = observed occ `S.difference` cons2 }-        filt2 occ =-          occ { consumed = consumed occ-              , observed = observed occ `S.difference` cons1 }-        cons1 = allConsumed occurs1-        cons2 = allConsumed occurs2----- Scope management--data Checking-  = CheckingApply (Maybe (QualName VName)) Exp StructType StructType-  | CheckingReturn StructType StructType-  | CheckingAscription StructType StructType-  | CheckingLetGeneralise Name-  | CheckingParams (Maybe Name)-  | CheckingPattern UncheckedPattern InferredType-  | CheckingLoopBody StructType StructType-  | CheckingLoopInitial StructType StructType-  | CheckingRecordUpdate [Name] StructType StructType-  | CheckingRequired [StructType] StructType-  | CheckingBranches StructType StructType--instance Pretty Checking where-  ppr (CheckingApply f e expected actual) =-    header </>-    "Expected:" <+> align (ppr expected) </>-    "Actual:  " <+> align (ppr actual)-    where header =-            case f of-              Nothing ->-                "Cannot apply function to" <+>-                pquote (shorten $ pretty $ flatten $ ppr e) <> " (invalid type)."-              Just fname ->-                "Cannot apply" <+> pquote (ppr fname) <+> "to" <+>-                pquote (shorten $ pretty $ flatten $ ppr e) <> " (invalid type)."--  ppr (CheckingReturn expected actual) =-    "Function body does not have expected type." </>-    "Expected:" <+> align (ppr expected) </>-    "Actual:  " <+> align (ppr actual)--  ppr (CheckingAscription expected actual) =-    "Expression does not have expected type from explicit ascription." </>-    "Expected:" <+> align (ppr expected) </>-    "Actual:  " <+> align (ppr actual)--  ppr (CheckingLetGeneralise fname) =-    "Cannot generalise type of" <+> pquote (ppr fname) <> "."--  ppr (CheckingParams fname) =-    "Invalid use of parameters in" <+> pquote fname' <> "."-    where fname' = maybe "anonymous function" ppr fname--  ppr (CheckingPattern pat NoneInferred) =-    "Invalid pattern" <+> pquote (ppr pat) <> "."--  ppr (CheckingPattern pat (Ascribed t)) =-    "Pattern" <+> pquote (ppr pat) <+>-    "cannot match value of type" </>-    indent 2 (ppr t)--  ppr (CheckingLoopBody expected actual) =-    "Loop body does not have expected type." </>-    "Expected:" <+> align (ppr expected) </>-    "Actual:  " <+> align (ppr actual)--  ppr (CheckingLoopInitial expected actual) =-    "Initial loop values do not have expected type." </>-    "Expected:" <+> align (ppr expected) </>-    "Actual:  " <+> align (ppr actual)--  ppr (CheckingRecordUpdate fs expected actual) =-    "Type mismatch when updating record field" <+> pquote fs' <> "." </>-    "Existing:" <+> align (ppr expected) </>-    "New:     " <+> align (ppr actual)-    where fs' = mconcat $ punctuate "." $ map ppr fs--  ppr (CheckingRequired [expected] actual) =-    "Expression must must have type" <+> ppr expected <> "." </>-    "Actual type:" <+> align (ppr actual)--  ppr (CheckingRequired expected actual) =-    "Type of expression must must be one of " <+> expected' <> "." </>-    "Actual type:" <+> align (ppr actual)-    where expected' = commasep (map ppr expected)--  ppr (CheckingBranches t1 t2) =-    "Conditional branches differ in type." </>-    "Former:" <+> ppr t1 </>-    "Latter:" <+> ppr t2---- | Whether something is a global or a local variable.-data Locality = Local | Global-              deriving (Show)--data ValBinding = BoundV Locality [TypeParam] PatternType-                -- ^ Aliases in parameters indicate the lexical-                -- closure.-                | OverloadedF [PrimType] [Maybe PrimType] (Maybe PrimType)-                | EqualityF-                | WasConsumed SrcLoc-                deriving (Show)---- | Type checking happens with access to this environment.  The--- 'TermScope' will be extended during type-checking as bindings come into--- scope.-data TermEnv = TermEnv { termScope :: TermScope-                       , termChecking :: Maybe Checking-                       , termLevel :: Level-                       }--data TermScope = TermScope { scopeVtable  :: M.Map VName ValBinding-                           , scopeTypeTable :: M.Map VName TypeBinding-                           , scopeModTable :: M.Map VName Mod-                           , scopeNameMap :: NameMap-                           } deriving (Show)--instance Semigroup TermScope where-  TermScope vt1 tt1 mt1 nt1 <> TermScope vt2 tt2 mt2 nt2 =-    TermScope (vt2 `M.union` vt1) (tt2 `M.union` tt1) (mt1 `M.union` mt2) (nt2 `M.union` nt1)--envToTermScope :: Env -> TermScope-envToTermScope env = TermScope { scopeVtable = vtable-                               , scopeTypeTable = envTypeTable env-                               , scopeNameMap = envNameMap env-                               , scopeModTable = envModTable env-                               }-  where vtable = M.mapWithKey valBinding $ envVtable env-        valBinding k (TypeM.BoundV tps v) =-          BoundV Global tps $ v `setAliases`-          (if arrayRank v > 0 then S.singleton (AliasBound k) else mempty)--withEnv :: TermEnv -> Env -> TermEnv-withEnv tenv env = tenv { termScope = termScope tenv <> envToTermScope env }--overloadedTypeVars :: Constraints -> Names-overloadedTypeVars = mconcat . map f . M.elems-  where f (_, HasFields fs _) = mconcat $ map typeVars $ M.elems fs-        f _ = mempty---- | Get the type of an expression, with top level type variables--- substituted.  Never call 'typeOf' directly (except in a few--- carefully inspected locations)!-expType :: Exp -> TermTypeM PatternType-expType = normPatternType . typeOf---- | Get the type of an expression, with all type variables--- substituted.  Slower than 'expType', but sometimes necessary.--- Never call 'typeOf' directly (except in a few carefully inspected--- locations)!-expTypeFully :: Exp -> TermTypeM PatternType-expTypeFully = normTypeFully . typeOf---- Wrap a function name to give it a vacuous Eq instance for SizeSource.-newtype FName = FName (Maybe (QualName VName))-              deriving (Show)--instance Eq FName where-  _ == _ = True--instance Ord FName where-  compare _ _ = EQ---- | What was the source of some existential size?  This is used for--- using the same existential variable if the same source is--- encountered in multiple locations.-data SizeSource = SourceArg FName (ExpBase NoInfo VName)-                | SourceBound (ExpBase NoInfo VName)-                | SourceSlice-                  (Maybe (DimDecl VName))-                  (Maybe (ExpBase NoInfo VName))-                  (Maybe (ExpBase NoInfo VName))-                  (Maybe (ExpBase NoInfo VName))-                deriving (Eq, Ord, Show)---- | The state is a set of constraints and a counter for generating--- type names.  This is distinct from the usual counter we use for--- generating unique names, as these will be user-visible.-data TermTypeState = TermTypeState-                     { stateConstraints :: Constraints-                     , stateCounter :: !Int-                     , stateDimTable :: M.Map SizeSource VName-                       -- ^ Mapping function arguments encountered to-                       -- the sizes they ended up generating (when-                       -- they could not be substituted directly).-                       -- This happens for function arguments that are-                       -- not constants or names.-                     }--newtype TermTypeM a = TermTypeM (RWST-                                 TermEnv-                                 Occurences-                                 TermTypeState-                                 TypeM-                                 a)-  deriving (Monad, Functor, Applicative,-            MonadReader TermEnv,-            MonadWriter Occurences,-            MonadState TermTypeState,-            MonadError TypeError)--instance MonadUnify TermTypeM where-  getConstraints = gets stateConstraints-  putConstraints x = modify $ \s -> s { stateConstraints = x }--  newTypeVar loc desc = do-    i <- incCounter-    v <- newID $ mkTypeVarName desc i-    constrain v $ NoConstraint Lifted $ mkUsage' loc-    return $ Scalar $ TypeVar mempty Nonunique (typeName v) []--  curLevel = asks termLevel--  newDimVar loc rigidity name = do-    i <- incCounter-    dim <- newID $ mkTypeVarName name i-    case rigidity of-      Rigid rsrc -> constrain dim $ UnknowableSize loc rsrc-      Nonrigid -> constrain dim $ Size Nothing $ mkUsage' loc-    return dim--  unifyError loc notes bcs doc = do-    checking <- asks termChecking-    case checking of-      Just checking' ->-        throwError $ TypeError (srclocOf loc) notes $-        ppr checking' <> line </> doc <> ppr bcs-      Nothing ->-        throwError $ TypeError (srclocOf loc) notes $ doc <> ppr bcs--  matchError loc notes bcs t1 t2 = do-    checking <- asks termChecking-    case checking of-      Just checking'-        | hasNoBreadCrumbs bcs ->-            throwError $ TypeError (srclocOf loc) notes $-            ppr checking'-        | otherwise ->-            throwError $ TypeError (srclocOf loc) notes $-            ppr checking' <> line </> doc <> ppr bcs-      Nothing ->-        throwError $ TypeError (srclocOf loc) notes $ doc <> ppr bcs-    where doc = "Types" </>-                indent 2 (ppr t1) </>-                "and" </>-                indent 2 (ppr t2) </>-                "do not match."--onFailure :: Checking -> TermTypeM a -> TermTypeM a-onFailure c = local $ \env -> env { termChecking = Just c }--runTermTypeM :: TermTypeM a -> TypeM (a, Occurences)-runTermTypeM (TermTypeM m) = do-  initial_scope <- (initialTermScope <>) . envToTermScope <$> askEnv-  let initial_tenv = TermEnv { termScope = initial_scope-                             , termChecking = Nothing-                             , termLevel = 0-                             }-  evalRWST m initial_tenv $ TermTypeState mempty 0 mempty--liftTypeM :: TypeM a -> TermTypeM a-liftTypeM = TermTypeM . lift--localScope :: (TermScope -> TermScope) -> TermTypeM a -> TermTypeM a-localScope f = local $ \tenv -> tenv { termScope = f $ termScope tenv }--incCounter :: TermTypeM Int-incCounter = do s <- get-                put s { stateCounter = stateCounter s + 1 }-                return $ stateCounter s--extSize :: SrcLoc -> SizeSource -> TermTypeM (DimDecl VName, Maybe VName)-extSize loc e = do-  prev <- gets $ M.lookup e . stateDimTable-  case prev of-    Nothing -> do-      let rsrc = case e of-                   SourceArg (FName fname) e' ->-                     RigidArg fname $ prettyOneLine e'-                   SourceBound e' ->-                     RigidBound $ prettyOneLine e'-                   SourceSlice d i j s  ->-                     RigidSlice d $ prettyOneLine $ DimSlice i j s-      d <- newDimVar loc (Rigid rsrc) "argdim"-      modify $ \s -> s { stateDimTable = M.insert e d $ stateDimTable s }-      return (NamedDim $ qualName d,-              Just d)-    Just d -> return (NamedDim $ qualName d,-                      Nothing)---- Any argument sizes created with 'extSize' inside the given action--- will be removed once the action finishes.  This is to ensure that--- just because e.g. @n+1@ appears as a size in one branch of a--- conditional, that doesn't mean it's also available in the other branch.-noSizeEscape :: TermTypeM a -> TermTypeM a-noSizeEscape m = do-  dimtable <- gets stateDimTable-  x <- m-  modify $ \s -> s { stateDimTable = dimtable }-  return x--constrain :: VName -> Constraint -> TermTypeM ()-constrain v c = do-  lvl <- curLevel-  modifyConstraints $ M.insert v (lvl, c)--incLevel :: TermTypeM a -> TermTypeM a-incLevel = local $ \env -> env { termLevel = termLevel env + 1 }--initialTermScope :: TermScope-initialTermScope = TermScope { scopeVtable = initialVtable-                             , scopeTypeTable = mempty-                             , scopeNameMap = topLevelNameMap-                             , scopeModTable = mempty-                             }-  where initialVtable = M.fromList $ mapMaybe addIntrinsicF $ M.toList intrinsics--        prim = Scalar . Prim-        arrow x y = Scalar $ Arrow mempty Unnamed x y--        addIntrinsicF (name, IntrinsicMonoFun pts t) =-          Just (name, BoundV Global [] $ arrow pts' $ prim t)-          where pts' = case pts of [pt] -> prim pt-                                   _    -> tupleRecord $ map prim pts--        addIntrinsicF (name, IntrinsicOverloadedFun ts pts rts) =-          Just (name, OverloadedF ts pts rts)-        addIntrinsicF (name, IntrinsicPolyFun tvs pts rt) =-          Just (name, BoundV Global tvs $-                      fromStruct $ Scalar $ Arrow mempty Unnamed pts' rt)-          where pts' = case pts of [pt] -> pt-                                   _    -> tupleRecord pts-        addIntrinsicF (name, IntrinsicEquality) =-          Just (name, EqualityF)-        addIntrinsicF _ = Nothing--instance MonadTypeChecker TermTypeM where-  warn loc problem = liftTypeM $ warn loc problem-  newName = liftTypeM . newName-  newID = liftTypeM . newID--  checkQualName space name loc = snd <$> checkQualNameWithEnv space name loc--  bindNameMap m = localScope $ \scope ->-    scope { scopeNameMap = m <> scopeNameMap scope }--  bindVal v (TypeM.BoundV tps t) = localScope $ \scope ->-    scope { scopeVtable = M.insert v vb $ scopeVtable scope }-    where vb = BoundV Local tps $ fromStruct t--  lookupType loc qn = do-    outer_env <- liftTypeM askEnv-    (scope, qn'@(QualName qs name)) <- checkQualNameWithEnv Type qn loc-    case M.lookup name $ scopeTypeTable scope of-      Nothing -> unknownType loc qn-      Just (TypeAbbr l ps def) ->-        return (qn', ps, qualifyTypeVars outer_env (map typeParamName ps) qs def, l)--  lookupMod loc qn = do-    (scope, qn'@(QualName _ name)) <- checkQualNameWithEnv Term qn loc-    case M.lookup name $ scopeModTable scope of-      Nothing -> unknownVariable Term qn loc-      Just m  -> return (qn', m)--  lookupVar loc qn = do-    outer_env <- liftTypeM askEnv-    (scope, qn'@(QualName qs name)) <- checkQualNameWithEnv Term qn loc-    let usage = mkUsage loc $ "use of " ++ quote (pretty qn)--    t <- case M.lookup name $ scopeVtable scope of-      Nothing -> typeError loc mempty $-                 "Unknown variable" <+> pquote (ppr qn) <> "."--      Just (WasConsumed wloc) -> useAfterConsume (baseName name) loc wloc--      Just (BoundV _ tparams t)-        | "_" `isPrefixOf` baseString name -> underscoreUse loc qn-        | otherwise -> do-            (tnames, t') <- instantiateTypeScheme loc tparams t-            return $ qualifyTypeVars outer_env tnames qs t'--      Just EqualityF -> do-        argtype <- newTypeVar loc "t"-        equalityType usage argtype-        return $-          Scalar $ Arrow mempty Unnamed argtype $-          Scalar $ Arrow mempty Unnamed argtype $ Scalar $ Prim Bool--      Just (OverloadedF ts pts rt) -> do-        argtype <- newTypeVar loc "t"-        mustBeOneOf ts usage argtype-        let (pts', rt') = instOverloaded argtype pts rt-            arrow xt yt = Scalar $ Arrow mempty Unnamed xt yt-        return $ fromStruct $ foldr arrow rt' pts'--    observe $ Ident name (Info t) loc-    return (qn', t)--      where instOverloaded argtype pts rt =-              (map (maybe (toStruct argtype) (Scalar . Prim)) pts,-               maybe (toStruct argtype) (Scalar . Prim) rt)--  checkNamedDim loc v = do-    (v', t) <- lookupVar loc v-    onFailure (CheckingRequired [Scalar $ Prim $ Signed Int32] (toStruct t)) $-      unify (mkUsage loc "use as array size") (toStruct t) $-      Scalar $ Prim $ Signed Int32-    return v'--  typeError loc notes s = do-    checking <- asks termChecking-    case checking of-      Just checking' ->-        throwError $ TypeError (srclocOf loc) notes (ppr checking' <> line </> s)-      Nothing ->-        throwError $ TypeError (srclocOf loc) notes s--checkQualNameWithEnv :: Namespace -> QualName Name -> SrcLoc -> TermTypeM (TermScope, QualName VName)-checkQualNameWithEnv space qn@(QualName quals name) loc = do-  scope <- asks termScope-  descend scope quals-  where descend scope []-          | Just name' <- M.lookup (space, name) $ scopeNameMap scope =-              return (scope, name')-          | otherwise =-              unknownVariable space qn loc--        descend scope (q:qs)-          | Just (QualName _ q') <- M.lookup (Term, q) $ scopeNameMap scope,-            Just res <- M.lookup q' $ scopeModTable scope =-              case res of-                -- Check if we are referring to the magical intrinsics-                -- module.-                _ | baseTag q' <= maxIntrinsicTag ->-                      checkIntrinsic space qn loc-                ModEnv q_scope -> do-                  (scope', QualName qs' name') <- descend (envToTermScope q_scope) qs-                  return (scope', QualName (q':qs') name')-                ModFun{} -> unappliedFunctor loc-          | otherwise =-              unknownVariable space qn loc--checkIntrinsic :: Namespace -> QualName Name -> SrcLoc -> TermTypeM (TermScope, QualName VName)-checkIntrinsic space qn@(QualName _ name) loc-  | Just v <- M.lookup (space, name) intrinsicsNameMap = do-      me <- liftTypeM askImportName-      unless ("/prelude" `isPrefixOf` includeToString me) $-        warn loc "Using intrinsic functions directly can easily crash the compiler or result in wrong code generation."-      scope <- asks termScope-      return (scope, v)-  | otherwise =-      unknownVariable space qn loc---- | Wrap 'Types.checkTypeDecl' to also perform an observation of--- every size in the type.-checkTypeDecl :: TypeDeclBase NoInfo Name -> TermTypeM (TypeDeclBase Info VName)-checkTypeDecl tdecl = do-  (tdecl', _) <- Types.checkTypeDecl tdecl-  mapM_ observeDim $ nestedDims $ unInfo $ expandedType tdecl'-  return tdecl'-  where observeDim (NamedDim v) =-          observe $ Ident (qualLeaf v) (Info $ Scalar $ Prim $ Signed Int32) mempty-        observeDim _ = return ()---- | Instantiate a type scheme with fresh type variables for its type--- parameters. Returns the names of the fresh type variables, the--- instance list, and the instantiated type.-instantiateTypeScheme :: SrcLoc -> [TypeParam] -> PatternType-                      -> TermTypeM ([VName], PatternType)-instantiateTypeScheme loc tparams t = do-  let tnames = map typeParamName tparams-  (tparam_names, tparam_substs) <- unzip <$> mapM (instantiateTypeParam loc) tparams-  let substs = M.fromList $ zip tnames tparam_substs-      t' = applySubst (`M.lookup` substs) t-  return (tparam_names, t')---- | Create a new type name and insert it (unconstrained) in the--- substitution map.-instantiateTypeParam :: Monoid as => SrcLoc -> TypeParam -> TermTypeM (VName, Subst (TypeBase dim as))-instantiateTypeParam loc tparam = do-  i <- incCounter-  v <- newID $ mkTypeVarName (takeWhile isAscii (baseString (typeParamName tparam))) i-  case tparam of TypeParamType x _ _ -> do-                   constrain v $ NoConstraint x $ mkUsage' loc-                   return (v, Subst $ Scalar $ TypeVar mempty Nonunique (typeName v) [])-                 TypeParamDim{} -> do-                   constrain v $ Size Nothing $ mkUsage' loc-                   return (v, SizeSubst $ NamedDim $ qualName v)--newArrayType :: SrcLoc -> String -> Int -> TermTypeM (StructType, StructType)-newArrayType loc desc r = do-  v <- newID $ nameFromString desc-  constrain v $ NoConstraint Unlifted $ mkUsage' loc-  dims <- replicateM r $ newDimVar loc Nonrigid "dim"-  let rowt = TypeVar () Nonunique (typeName v) []-  return (Array () Nonunique rowt (ShapeDecl $ map (NamedDim . qualName) dims),-          Scalar rowt)----- Errors--useAfterConsume :: Name -> SrcLoc -> SrcLoc -> TermTypeM a-useAfterConsume name rloc wloc =-  typeError rloc mempty $-  "Variable" <+> pquote (pprName name) <+> "previously consumed at" <+>-  text (locStrRel rloc wloc) <> ".  (Possibly through aliasing.)"--consumeAfterConsume :: Name -> SrcLoc -> SrcLoc -> TermTypeM a-consumeAfterConsume name loc1 loc2 =-  typeError loc2 mempty $-  "Variable" <+> pprName name <+> "previously consumed at" <+>-  text (locStrRel loc2 loc1) <> "."--badLetWithValue :: SrcLoc -> TermTypeM a-badLetWithValue loc =-  typeError loc mempty-  "New value for elements in let-with shares data with source array.  This is illegal, as it prevents in-place modification."--returnAliased :: Name -> Name -> SrcLoc -> TermTypeM ()-returnAliased fname name loc =-  typeError loc mempty $-  "Unique return value of" <+> pquote (pprName fname) <+>-  "is aliased to" <+> pquote (pprName name) <> ", which is not consumed."--uniqueReturnAliased :: Name -> SrcLoc -> TermTypeM a-uniqueReturnAliased fname loc =-  typeError loc mempty $-  "A unique tuple element of return value of" <+>-  pquote (pprName fname) <+> "is aliased to some other tuple component."--unexpectedType :: MonadTypeChecker m => SrcLoc -> StructType -> [StructType] -> m a-unexpectedType loc _ [] =-  typeError loc mempty $-  "Type of expression at" <+> text (locStr loc) <+>-  "cannot have any type - possibly a bug in the type checker."-unexpectedType loc t ts =-  typeError loc mempty $-  "Type of expression at" <+> text (locStr loc) <+> "must be one of" <+>-  commasep (map ppr ts) <> ", but is" <+>-  ppr t <> "."----- Basic checking---- | Determine if the two types of identical, ignoring uniqueness.--- Mismatched dimensions are turned into fresh rigid type variables.--- Causes a 'TypeError' if they fail to match, and otherwise returns--- one of them.-unifyBranchTypes :: SrcLoc -> PatternType -> PatternType -> TermTypeM (PatternType, [VName])-unifyBranchTypes loc t1 t2 =-  onFailure (CheckingBranches (toStruct t1) (toStruct t2)) $-  unifyMostCommon (mkUsage loc "unification of branch results") t1 t2--unifyBranches :: SrcLoc -> Exp -> Exp -> TermTypeM (PatternType, [VName])-unifyBranches loc e1 e2 = do-  e1_t <- expTypeFully e1-  e2_t <- expTypeFully e2-  unifyBranchTypes loc e1_t e2_t----- General binding.--doNotShadow :: [String]-doNotShadow = ["&&", "||"]--data InferredType = NoneInferred-                  | Ascribed PatternType---checkPattern' :: UncheckedPattern -> InferredType-              -> TermTypeM Pattern--checkPattern' (PatternParens p loc) t =-  PatternParens <$> checkPattern' p t <*> pure loc--checkPattern' (Id name _ loc) _-  | name' `elem` doNotShadow =-      typeError loc mempty $ "The" <+> text name' <+> "operator may not be redefined."-  where name' = nameToString name--checkPattern' (Id name NoInfo loc) (Ascribed t) = do-  name' <- newID name-  return $ Id name' (Info t) loc-checkPattern' (Id name NoInfo loc) NoneInferred = do-  name' <- newID name-  t <- newTypeVar loc "t"-  return $ Id name' (Info t) loc--checkPattern' (Wildcard _ loc) (Ascribed t) =-  return $ Wildcard (Info $ t `setUniqueness` Nonunique) loc-checkPattern' (Wildcard NoInfo loc) NoneInferred = do-  t <- newTypeVar loc "t"-  return $ Wildcard (Info t) loc--checkPattern' (TuplePattern ps loc) (Ascribed t)-  | Just ts <- isTupleRecord t, length ts == length ps =-      TuplePattern <$> zipWithM checkPattern' ps (map Ascribed ts) <*> pure loc-checkPattern' p@(TuplePattern ps loc) (Ascribed t) = do-  ps_t <- replicateM (length ps) (newTypeVar loc "t")-  unify (mkUsage loc "matching a tuple pattern") (tupleRecord ps_t) $ toStruct t-  t' <- normTypeFully t-  checkPattern' p $ Ascribed t'-checkPattern' (TuplePattern ps loc) NoneInferred =-  TuplePattern <$> mapM (`checkPattern'` NoneInferred) ps <*> pure loc--checkPattern' (RecordPattern p_fs _) _-  | Just (f, fp) <- find (("_" `isPrefixOf`) . nameToString . fst) p_fs =-      typeError fp mempty $-      "Underscore-prefixed fields are not allowed." </>-      "Did you mean" <> dquotes (text (drop 1 (nameToString f)) <> "=_") <> "?"--checkPattern' (RecordPattern p_fs loc) (Ascribed (Scalar (Record t_fs)))-  | sort (map fst p_fs) == sort (M.keys t_fs) =-    RecordPattern . M.toList <$> check <*> pure loc-    where check = traverse (uncurry checkPattern') $ M.intersectionWith (,)-                  (M.fromList p_fs) (fmap Ascribed t_fs)-checkPattern' p@(RecordPattern fields loc) (Ascribed t) = do-  fields' <- traverse (const $ newTypeVar loc "t") $ M.fromList fields--  when (sort (M.keys fields') /= sort (map fst fields)) $-    typeError loc mempty $ "Duplicate fields in record pattern" <+> ppr p <> "."--  unify (mkUsage loc "matching a record pattern") (Scalar (Record fields')) $ toStruct t-  t' <- normTypeFully t-  checkPattern' p $ Ascribed t'-checkPattern' (RecordPattern fs loc) NoneInferred =-  RecordPattern . M.toList <$> traverse (`checkPattern'` NoneInferred) (M.fromList fs) <*> pure loc--checkPattern' (PatternAscription p (TypeDecl t NoInfo) loc) maybe_outer_t = do-  (t', st_nodims, _) <- checkTypeExp t-  (st, _) <- instantiateEmptyArrayDims loc "impl" Nonrigid st_nodims--  let st' = fromStruct st-  case maybe_outer_t of-    Ascribed outer_t -> do-      unify (mkUsage loc "explicit type ascription") (toStruct st) (toStruct outer_t)--      -- We also have to make sure that uniqueness matches.  This is-      -- done explicitly, because it is ignored by unification.-      st'' <- normTypeFully st'-      outer_t' <- normTypeFully outer_t-      case unifyTypesU unifyUniqueness st'' outer_t' of-        Just outer_t'' ->-          PatternAscription <$> checkPattern' p (Ascribed outer_t'') <*>-          pure (TypeDecl t' (Info st)) <*> pure loc-        Nothing ->-          typeError loc mempty $-          "Cannot match type" <+> pquote (ppr outer_t') <+> "with expected type" <+>-          pquote (ppr st'') <> "."--    NoneInferred ->-      PatternAscription <$> checkPattern' p (Ascribed st') <*>-      pure (TypeDecl t' (Info st)) <*> pure loc- where unifyUniqueness u1 u2 = if u2 `subuniqueOf` u1 then Just u1 else Nothing--checkPattern' (PatternLit e NoInfo loc) (Ascribed t) = do-  e' <- checkExp e-  t' <- expTypeFully e'-  unify (mkUsage loc "matching against literal") (toStruct t') (toStruct t)-  return $ PatternLit e' (Info t') loc--checkPattern' (PatternLit e NoInfo loc) NoneInferred = do-  e' <- checkExp e-  t' <- expTypeFully e'-  return $ PatternLit e' (Info t') loc--checkPattern' (PatternConstr n NoInfo ps loc) (Ascribed (Scalar (Sum cs)))-  | Just ts <- M.lookup n cs = do-      ps' <- zipWithM checkPattern' ps $ map Ascribed ts-      return $ PatternConstr n (Info (Scalar (Sum cs))) ps' loc--checkPattern' (PatternConstr n NoInfo ps loc) (Ascribed t) = do-  t' <- newTypeVar loc "t"-  ps' <- mapM (`checkPattern'` NoneInferred) ps-  mustHaveConstr usage n t' (patternStructType <$> ps')-  unify usage t' (toStruct t)-  t'' <- normTypeFully t-  return $ PatternConstr n (Info t'') ps' loc-  where usage = mkUsage loc "matching against constructor"--checkPattern' (PatternConstr n NoInfo ps loc) NoneInferred = do-  ps' <- mapM (`checkPattern'` NoneInferred) ps-  t <- newTypeVar loc "t"-  mustHaveConstr usage n t (patternStructType <$> ps')-  return $ PatternConstr n (Info $ fromStruct t) ps' loc-  where usage = mkUsage loc "matching against constructor"--patternNameMap :: Pattern -> NameMap-patternNameMap = M.fromList . map asTerm . S.toList . patternNames-  where asTerm v = ((Term, baseName v), qualName v)--checkPattern :: UncheckedPattern -> InferredType -> (Pattern -> TermTypeM a)-             -> TermTypeM a-checkPattern p t m = do-  checkForDuplicateNames [p]-  p' <- onFailure (CheckingPattern p t) $ checkPattern' p t-  bindNameMap (patternNameMap p') $ m p'--binding :: [Ident] -> TermTypeM a -> TermTypeM a-binding bnds = check . handleVars-  where handleVars m =-          localScope (`bindVars` bnds) $ do--          -- Those identifiers that can potentially also be sizes are-          -- added as type constraints.  This is necessary so that we-          -- can properly detect scope violations during unification.-          -- We do this for *all* identifiers, not just those that are-          -- integers, because they may become integers later due to-          -- inference...-          forM_ bnds $ \ident ->-            constrain (identName ident) $ ParamSize $ srclocOf ident-          m--        bindVars :: TermScope -> [Ident] -> TermScope-        bindVars = foldl bindVar--        bindVar :: TermScope -> Ident -> TermScope-        bindVar scope (Ident name (Info tp) _) =-          let inedges = boundAliases $ aliases tp-              update (BoundV l tparams in_t)-                -- If 'name' is record or sum-typed, don't alias the-                -- components to 'name', because these no identity-                -- beyond their components.-                | Array{} <- tp = BoundV l tparams (in_t `addAliases` S.insert (AliasBound name))-                | otherwise = BoundV l tparams in_t-              update b = b--              tp' = tp `addAliases` S.insert (AliasBound name)-          in scope { scopeVtable = M.insert name (BoundV Local [] tp') $-                                   adjustSeveral update inedges $-                                   scopeVtable scope-                   }--        adjustSeveral f = flip $ foldl $ flip $ M.adjust f--        -- Check whether the bound variables have been used correctly-        -- within their scope.-        check m = do-          (a, usages) <- collectBindingsOccurences m-          checkOccurences usages--          mapM_ (checkIfUsed usages) bnds--          return a--        -- Collect and remove all occurences in @bnds@.  This relies-        -- on the fact that no variables shadow any other.-        collectBindingsOccurences m = pass $ do-          (x, usage) <- listen m-          let (relevant, rest) = split usage-          return ((x, relevant), const rest)-          where split = unzip .-                        map (\occ ->-                             let (obs1, obs2) = divide $ observed occ-                                 occ_cons = divide <$> consumed occ-                                 con1 = fst <$> occ_cons-                                 con2 = snd <$> occ_cons-                             in (occ { observed = obs1, consumed = con1 },-                                 occ { observed = obs2, consumed = con2 }))-                names = S.fromList $ map identName bnds-                divide s = (s `S.intersection` names, s `S.difference` names)--bindingTypes :: [Either (VName, TypeBinding) (VName, Constraint)]-             -> TermTypeM a -> TermTypeM a-bindingTypes types m = do-  lvl <- curLevel-  modifyConstraints (<>M.map (lvl,) (M.fromList constraints))-  localScope extend m-  where (tbinds, constraints) = partitionEithers types-        extend scope = scope {-          scopeTypeTable = M.fromList tbinds <> scopeTypeTable scope-          }--bindingTypeParams :: [TypeParam] -> TermTypeM a -> TermTypeM a-bindingTypeParams tparams = binding (mapMaybe typeParamIdent tparams) .-                            bindingTypes (concatMap typeParamType tparams)-  where typeParamType (TypeParamType l v loc) =-          [ Left (v, TypeAbbr l [] (Scalar (TypeVar () Nonunique (typeName v) [])))-          , Right (v, ParamType l loc) ]-        typeParamType (TypeParamDim v loc) =-          [ Right (v, ParamSize loc) ]--typeParamIdent :: TypeParam -> Maybe Ident-typeParamIdent (TypeParamDim v loc) =-  Just $ Ident v (Info $ Scalar $ Prim $ Signed Int32) loc-typeParamIdent _ = Nothing--bindingIdent :: IdentBase NoInfo Name -> PatternType -> (Ident -> TermTypeM a)-             -> TermTypeM a-bindingIdent (Ident v NoInfo vloc) t m =-  bindSpaced [(Term, v)] $ do-    v' <- checkName Term v vloc-    let ident = Ident v' (Info t) vloc-    binding [ident] $ m ident--bindingParams :: [UncheckedTypeParam]-              -> [UncheckedPattern]-              -> ([TypeParam] -> [Pattern] -> TermTypeM a) -> TermTypeM a-bindingParams tps orig_ps m = do-  checkForDuplicateNames orig_ps-  checkTypeParams tps $ \tps' -> bindingTypeParams tps' $ do-    let descend ps' (p:ps) =-          checkPattern p NoneInferred $ \p' ->-            binding (S.toList $ patternIdents p') $ descend (p':ps') ps-        descend ps' [] = do-          -- Perform an observation of every type parameter.  This-          -- prevents unused-name warnings for otherwise unused-          -- dimensions.-          mapM_ observe $ mapMaybe typeParamIdent tps'-          m tps' $ reverse ps'--    descend [] orig_ps--bindingPattern :: PatternBase NoInfo Name -> InferredType-               -> (Pattern -> TermTypeM a) -> TermTypeM a-bindingPattern p t m = do-  checkForDuplicateNames [p]-  checkPattern p t $ \p' -> binding (S.toList $ patternIdents p') $ do-    -- Perform an observation of every declared dimension.  This-    -- prevents unused-name warnings for otherwise unused dimensions.-    mapM_ observe $ patternDims p'--    m p'--patternDims :: Pattern -> [Ident]-patternDims (PatternParens p _) = patternDims p-patternDims (TuplePattern pats _) = concatMap patternDims pats-patternDims (PatternAscription p (TypeDecl _ (Info t)) _) =-  patternDims p <> mapMaybe (dimIdent (srclocOf p)) (nestedDims t)-  where dimIdent _ AnyDim            = Nothing-        dimIdent _ (ConstDim _)      = Nothing-        dimIdent _ NamedDim{}        = Nothing-patternDims _ = []--sliceShape :: Maybe (SrcLoc, Rigidity) -> [DimIndex] -> TypeBase (DimDecl VName) as-           -> TermTypeM (TypeBase (DimDecl VName) as, [VName])-sliceShape r slice t@(Array als u et (ShapeDecl orig_dims)) =-  runWriterT $ setDims <$> adjustDims slice orig_dims-  where setDims []    = stripArray (length orig_dims) t-        setDims dims' = Array als u et $ ShapeDecl dims'--        -- If the result is supposed to be AnyDim or a nonrigid size-        -- variable, then don't bother trying to create-        -- non-existential sizes.  This is necessary to make programs-        -- type-check without too much ceremony; see-        -- e.g. tests/inplace5.fut.-        isRigid Rigid{} = True-        isRigid _ = False-        refine_sizes = maybe False (isRigid . snd) r--        sliceSize orig_d i j stride =-          case r of-            Just (loc, Rigid _) -> do-              (d, ext) <--                lift $ extSize loc $-                SourceSlice orig_d' (bareExp <$> i) (bareExp <$> j) (bareExp <$> stride)-              tell $ maybeToList ext-              return d-            Just (loc, Nonrigid) ->-              lift $ NamedDim . qualName <$> newDimVar loc Nonrigid "slice_dim"-            Nothing ->-              pure AnyDim-          where-            -- The original size does not matter if the slice is fully specified.-            orig_d' | isJust i, isJust j = Nothing-                    | otherwise = Just orig_d--        adjustDims (DimFix{} : idxes') (_:dims) =-          adjustDims idxes' dims--        -- Pattern match some known slices to be non-existential.-        adjustDims (DimSlice i j stride : idxes') (_:dims)-          | refine_sizes,-            maybe True ((==Just 0) . isInt32) i,-            Just j' <- maybeDimFromExp =<< j,-            maybe True ((==Just 1) . isInt32) stride =-              (j':) <$> adjustDims idxes' dims--        adjustDims (DimSlice Nothing Nothing stride : idxes') (d:dims)-          | refine_sizes,-            maybe True (maybe False ((==1) . abs) . isInt32) stride =-              (d:) <$> adjustDims idxes' dims--        adjustDims (DimSlice i j stride : idxes') (d:dims) =-          (:) <$> sliceSize d i j stride <*> adjustDims idxes' dims--        adjustDims _ dims =-          pure dims--sliceShape _ _ t = pure (t, [])----- Main checkers---- | @require ts e@ causes a 'TypeError' if @expType e@ is not one of--- the types in @ts@.  Otherwise, simply returns @e@.-require :: String -> [PrimType] -> Exp -> TermTypeM Exp-require why ts e = do mustBeOneOf ts (mkUsage (srclocOf e) why) . toStruct =<< expType e-                      return e--unifies :: String -> StructType -> Exp -> TermTypeM Exp-unifies why t e = do-  unify (mkUsage (srclocOf e) why) t . toStruct =<< expType e-  return e---- The closure of a lambda or local function are those variables that--- it references, and which local to the current top-level function.-lexicalClosure :: [Pattern] -> Occurences -> TermTypeM Aliasing-lexicalClosure params closure = do-  vtable <- asks $ scopeVtable . termScope-  let isLocal v = case v `M.lookup` vtable of-                    Just (BoundV Local _ _) -> True-                    _ -> False-  return $ S.map AliasBound $ S.filter isLocal $-    allOccuring closure S.\\ mconcat (map patternNames params)--noAliasesIfOverloaded :: PatternType -> TermTypeM PatternType-noAliasesIfOverloaded t@(Scalar (TypeVar _ u tn [])) = do-  subst <- fmap snd . M.lookup (typeLeaf tn) <$> getConstraints-  case subst of-    Just Overloaded{} -> return $ Scalar $ TypeVar mempty u tn []-    _ -> return t-noAliasesIfOverloaded t =-  return t---- Check the common parts of ascription and coercion.-checkAscript :: SrcLoc-             -> UncheckedTypeDecl-             -> UncheckedExp-             -> (StructType -> StructType)-             -> TermTypeM (TypeDecl, Exp)-checkAscript loc decl e shapef = do-  decl' <- checkTypeDecl decl-  e' <- checkExp e-  t <- expTypeFully e'--  (decl_t_nonrigid, _) <--    instantiateEmptyArrayDims loc "impl" Nonrigid $ shapef $-    unInfo $ expandedType decl'--  onFailure (CheckingAscription (unInfo $ expandedType decl') (toStruct t)) $-    unify (mkUsage loc "type ascription") decl_t_nonrigid (toStruct t)--  -- We also have to make sure that uniqueness matches.  This is done-  -- explicitly, because uniqueness is ignored by unification.-  t' <- normTypeFully t-  decl_t' <- normTypeFully $ unInfo $ expandedType decl'-  unless (t' `subtypeOf` anySizes decl_t') $-    typeError loc mempty $ "Type" <+> pquote (ppr t') <+> "is not a subtype of" <+>-    pquote (ppr decl_t') <> "."--  return (decl', e')--unscopeType :: SrcLoc-            -> M.Map VName Ident-            -> PatternType-            -> TermTypeM (PatternType, [VName])-unscopeType tloc unscoped t = do-  (t', m) <- runStateT (traverseDims onDim t) mempty-  return (t' `addAliases` S.map unAlias, M.elems m)-  where onDim _ p (NamedDim d)-          | Just loc <- srclocOf <$> M.lookup (qualLeaf d) unscoped =-              if p == PosImmediate || p == PosParam-              then inst loc $ qualLeaf d-              else return AnyDim-        onDim _ _ d = return d--        inst loc d = do-          prev <- gets $ M.lookup d-          case prev of-            Just d' -> return $ NamedDim $ qualName d'-            Nothing -> do-              d' <- lift $ newDimVar tloc (Rigid $ RigidOutOfScope loc d) "d"-              modify $ M.insert d d'-              return $ NamedDim $ qualName d'--        unAlias (AliasBound v) | v `M.member` unscoped = AliasFree v-        unAlias a = a---- 'checkApplyExp' is like 'checkExp', but tries to find the "root--- function", for better error messages.-checkApplyExp :: UncheckedExp -> TermTypeM (Exp, ApplyOp)--checkApplyExp (Apply e1 e2 _ _ loc) = do-  (e1', (fname, i)) <- checkApplyExp e1-  arg <- checkArg e2-  t <- expType e1'-  (t1, rt, argext, exts) <- checkApply loc (fname, i) t arg-  return (Apply e1' (argExp arg) (Info (diet t1, argext)) (Info rt, Info exts) loc,-          (fname, i+1))--checkApplyExp e = do-  e' <- checkExp e-  return (e',-          (case e' of Var qn _ _ -> Just qn-                      _ -> Nothing,-           0))--checkExp :: UncheckedExp -> TermTypeM Exp--checkExp (Literal val loc) =-  return $ Literal val loc--checkExp (StringLit vs loc) =-  return $ StringLit vs loc--checkExp (IntLit val NoInfo loc) = do-  t <- newTypeVar loc "t"-  mustBeOneOf anyNumberType (mkUsage loc "integer literal") t-  return $ IntLit val (Info $ fromStruct t) loc--checkExp (FloatLit val NoInfo loc) = do-  t <- newTypeVar loc "t"-  mustBeOneOf anyFloatType (mkUsage loc "float literal") t-  return $ FloatLit val (Info $ fromStruct t) loc--checkExp (TupLit es loc) =-  TupLit <$> mapM checkExp es <*> pure loc--checkExp (RecordLit fs loc) = do-  fs' <- evalStateT (mapM checkField fs) mempty--  return $ RecordLit fs' loc-  where checkField (RecordFieldExplicit f e rloc) = do-          errIfAlreadySet f rloc-          modify $ M.insert f rloc-          RecordFieldExplicit f <$> lift (checkExp e) <*> pure rloc-        checkField (RecordFieldImplicit name NoInfo rloc) = do-          errIfAlreadySet name rloc-          (QualName _ name', t) <- lift $ lookupVar rloc $ qualName name-          modify $ M.insert name rloc-          return $ RecordFieldImplicit name' (Info t) rloc--        errIfAlreadySet f rloc = do-          maybe_sloc <- gets $ M.lookup f-          case maybe_sloc of-            Just sloc ->-              lift $ typeError rloc mempty $ "Field" <+> pquote (ppr f) <+>-              "previously defined at" <+> text (locStrRel rloc sloc) <> "."-            Nothing -> return ()--checkExp (ArrayLit all_es _ loc) =-  -- Construct the result type and unify all elements with it.  We-  -- only create a type variable for empty arrays; otherwise we use-  -- the type of the first element.  This significantly cuts down on-  -- the number of type variables generated for pathologically large-  -- multidimensional array literals.-  case all_es of-    [] -> do et <- newTypeVar loc "t"-             t <- arrayOfM loc et (ShapeDecl [ConstDim 0]) Unique-             return $ ArrayLit [] (Info t) loc-    e:es -> do-      e' <- checkExp e-      et <- expType e'-      es' <- mapM (unifies "type of first array element" (toStruct et) <=< checkExp) es-      et' <- normTypeFully et-      t <- arrayOfM loc et' (ShapeDecl [ConstDim $ length all_es]) Unique-      return $ ArrayLit (e':es') (Info t) loc--checkExp (Range start maybe_step end _ loc) = do-  start' <- require "use in range expression" anyIntType =<< checkExp start-  start_t <- toStruct <$> expTypeFully start'-  maybe_step' <- case maybe_step of-    Nothing -> return Nothing-    Just step -> do-      let warning = warn loc "First and second element of range are identical, this will produce an empty array."-      case (start, step) of-        (Literal x _, Literal y _) -> when (x == y) warning-        (Var x_name _ _, Var y_name _ _) -> when (x_name == y_name) warning-        _ -> return ()-      Just <$> (unifies "use in range expression" start_t =<< checkExp step)--  let unifyRange e = unifies "use in range expression" start_t =<< checkExp e-  end' <- case end of-    DownToExclusive e -> DownToExclusive <$> unifyRange e-    UpToExclusive e -> UpToExclusive <$> unifyRange e-    ToInclusive e -> ToInclusive <$> unifyRange e--  -- Special case some ranges to give them a known size.-  let dimFromBound = dimFromExp (SourceBound . bareExp)-  (dim, retext) <--    case (isInt32 start', isInt32 <$> maybe_step', end') of-      (Just 0, Just (Just 1), UpToExclusive end'') ->-        dimFromBound end''-      (Just 0, Nothing, UpToExclusive end'') ->-        dimFromBound end''-      (Just 1, Just (Just 2), ToInclusive end'') ->-        dimFromBound end''-      _ -> do-        d <- newDimVar loc (Rigid RigidRange) "range_dim"-        return (NamedDim $ qualName d, Just d)--  t <- arrayOfM loc start_t (ShapeDecl [dim]) Unique-  let ret = (Info (t `setAliases` mempty), Info $ maybeToList retext)--  return $ Range start' maybe_step' end' ret loc--checkExp (Ascript e decl loc) = do-  (decl', e') <- checkAscript loc decl e id-  return $ Ascript e' decl' loc--checkExp (Coerce e decl _ loc) = do-  -- We instantiate the declared types with all dimensions as nonrigid-  -- fresh type variables, which we then use to unify with the type of-  -- 'e'.  This lets 'e' have whatever sizes it wants, but the overall-  -- type must still match.  Eventually we will throw away those sizes-  -- (they will end up being unified with various sizes in 'e', which-  -- is fine).-  (decl', e') <- checkAscript loc decl e anySizes--  -- Now we instantiate the declared type again, but this time we keep-  -- around the sizes as existentials.  This is the result of the-  -- ascription as a whole.  We use matchDims to obtain the aliasing-  -- of 'e'.-  (decl_t_rigid, ext) <--    instantiateDimsInReturnType loc Nothing $ unInfo $ expandedType decl'--  t <- expTypeFully e'--  t' <- matchDims (const pure) t $ fromStruct decl_t_rigid--  return $ Coerce e' decl' (Info t', Info ext) loc--checkExp (BinOp (op, oploc) NoInfo (e1,_) (e2,_) NoInfo NoInfo loc) = do-  (op', ftype) <- lookupVar oploc op-  e1_arg <- checkArg e1-  e2_arg <- checkArg e2--  -- Note that the application to the first operand cannot fix any-  -- existential sizes, because it must by necessity be a function.-  (p1_t, rt, p1_ext, _) <- checkApply loc (Just op', 0) ftype e1_arg-  (p2_t, rt', p2_ext, retext) <- checkApply loc (Just op', 1) rt e2_arg--  return $ BinOp (op', oploc) (Info ftype)-    (argExp e1_arg, Info (toStruct p1_t, p1_ext))-    (argExp e2_arg, Info (toStruct p2_t, p2_ext))-    (Info rt') (Info retext) loc--checkExp (Project k e NoInfo loc) = do-  e' <- checkExp e-  t <- expType e'-  kt <- mustHaveField (mkUsage loc $ "projection of field " ++ quote (pretty k)) k t-  return $ Project k e' (Info kt) loc--checkExp (If e1 e2 e3 _ loc) =-  sequentially checkCond $ \e1' _ -> do-  ((e2', e3'), dflow) <- tapOccurences $ checkExp e2 `alternative` checkExp e3--  (brancht, retext) <- unifyBranches loc e2' e3'-  let t' = addAliases brancht (`S.difference` S.map AliasBound (allConsumed dflow))--  zeroOrderType (mkUsage loc "returning value of this type from 'if' expression")-    "type returned from branch" t'--  return $ If e1' e2' e3' (Info t', Info retext) loc--  where checkCond = do-          e1' <- checkExp e1-          let bool = Scalar $ Prim Bool-          e1_t <- toStruct <$> expType e1'-          onFailure (CheckingRequired [bool] e1_t) $-            unify (mkUsage (srclocOf e1') "use as 'if' condition") bool e1_t-          return e1'--checkExp (Parens e loc) =-  Parens <$> checkExp e <*> pure loc--checkExp (QualParens (modname, modnameloc) e loc) = do-  (modname',mod) <- lookupMod loc modname-  case mod of-    ModEnv env -> local (`withEnv` qualifyEnv modname' env) $ do-      e' <- checkExp e-      return $ QualParens (modname', modnameloc) e' loc-    ModFun{} ->-      typeError loc mempty $ "Module" <+> ppr modname <+> " is a parametric module."-  where qualifyEnv modname' env =-          env { envNameMap = M.map (qualify' modname') $ envNameMap env }-        qualify' modname' (QualName qs name) =-          QualName (qualQuals modname' ++ [qualLeaf modname'] ++ qs) name--checkExp (Var qn NoInfo loc) = do-  -- The qualifiers of a variable is divided into two parts: first a-  -- possibly-empty sequence of module qualifiers, followed by a-  -- possible-empty sequence of record field accesses.  We use scope-  -- information to perform the split, by taking qualifiers off the-  -- end until we find a module.--  (qn', t, fields) <- findRootVar (qualQuals qn) (qualLeaf qn)--  foldM checkField (Var qn' (Info t) loc) fields--  where findRootVar qs name =-          (whenFound <$> lookupVar loc (QualName qs name)) `catchError` notFound qs name--        whenFound (qn', t) = (qn', t, [])--        notFound qs name err-          | null qs = throwError err-          | otherwise = do-              (qn', t, fields) <- findRootVar (init qs) (last qs) `catchError`-                                  const (throwError err)-              return (qn', t, fields++[name])--        checkField e k = do-          t <- expType e-          let usage = mkUsage loc $ "projection of field " ++ quote (pretty k)-          kt <- mustHaveField usage k t-          return $ Project k e (Info kt) loc--checkExp (Negate arg loc) = do-  arg' <- require "numeric negation" anyNumberType =<< checkExp arg-  return $ Negate arg' loc--checkExp e@Apply{} = fst <$> checkApplyExp e--checkExp (LetPat pat e body _ loc) =-  sequentially (checkExp e) $ \e' e_occs -> do-    -- Not technically an ascription, but we want the pattern to have-    -- exactly the type of 'e'.-    t <- expType e'-    case anyConsumption e_occs of-      Just c ->-        let msg = "type computed with consumption at " ++ locStr (location c)-        in zeroOrderType (mkUsage loc "consumption in right-hand side of 'let'-binding") msg t-      _ -> return ()--    incLevel $ bindingPattern pat (Ascribed t) $ \pat' -> do-      body' <- checkExp body-      (body_t, retext) <--        unscopeType loc (patternMap pat') =<< expTypeFully body'--      return $ LetPat pat' e' body' (Info body_t, Info retext) loc--checkExp (LetFun name (tparams, params, maybe_retdecl, NoInfo, e) body NoInfo loc) =-  sequentially (checkBinding (name, maybe_retdecl, tparams, params, e, loc)) $-  \(tparams', params', maybe_retdecl', rettype, _, e') closure -> do--    closure' <- lexicalClosure params' closure--    bindSpaced [(Term, name)] $ do-      name' <- checkName Term name loc--      let arrow (xp, xt) yt = Scalar $ Arrow () xp xt yt-          ftype = foldr (arrow . patternParam) rettype params'-          entry = BoundV Local tparams' $ ftype `setAliases` closure'-          bindF scope = scope { scopeVtable =-                                  M.insert name' entry $ scopeVtable scope-                              , scopeNameMap =-                                  M.insert (Term, name) (qualName name') $-                                  scopeNameMap scope }-      body' <- localScope bindF $ checkExp body--      -- We fake an ident here, but it's OK as it can't be a size-      -- anyway.-      let fake_ident = Ident name' (Info $ fromStruct ftype) mempty-      (body_t, _) <--        unscopeType loc (M.singleton name' fake_ident) =<<-        expTypeFully body'--      return $ LetFun name' (tparams', params', maybe_retdecl', Info rettype, e')-        body' (Info body_t) loc--checkExp (LetWith dest src idxes ve body NoInfo loc) =-  sequentially (checkIdent src) $ \src' _ -> do-  (t, _) <- newArrayType (srclocOf src) "src" $ length idxes-  unify (mkUsage loc "type of target array") t $ toStruct $ unInfo $ identType src'--  -- Need the fully normalised type here to get the proper aliasing information.-  src_t <- normTypeFully $ unInfo $ identType src'--  idxes' <- mapM checkDimIndex idxes-  (elemt, _) <- sliceShape (Just (loc, Nonrigid)) idxes' =<< normTypeFully t--  unless (unique src_t) $-    typeError loc mempty $ "Source" <+> pquote (pprName (identName src)) <+>-    "has type" <+> ppr src_t <> ", which is not unique."-  vtable <- asks $ scopeVtable . termScope-  forM_ (aliases src_t) $ \v ->-    case aliasVar v `M.lookup` vtable of-      Just (BoundV Local _ v_t)-        | not $ unique v_t ->-            typeError loc mempty $ "Source" <+> pquote (pprName (identName src)) <+>-            "aliases" <+> pquote (pprName (aliasVar v)) <> ", which is not consumable."-      _ -> return ()--  sequentially (unifies "type of target array" (toStruct elemt) =<< checkExp ve) $ \ve' _ -> do-    ve_t <- expTypeFully ve'-    when (AliasBound (identName src') `S.member` aliases ve_t) $-      badLetWithValue loc--    bindingIdent dest (src_t `setAliases` S.empty) $ \dest' -> do-      body' <- consuming src' $ checkExp body-      (body_t, _) <--        unscopeType loc (M.singleton (identName dest') dest') =<<-        expTypeFully body'-      return $ LetWith dest' src' idxes' ve' body' (Info body_t) loc--checkExp (Update src idxes ve loc) = do-  (t, _) <- newArrayType (srclocOf src) "src" $ length idxes-  idxes' <- mapM checkDimIndex idxes-  (elemt, _) <- sliceShape (Just (loc, Nonrigid)) idxes' =<< normTypeFully t--  sequentially (checkExp ve >>= unifies "type of target array" elemt) $ \ve' _ ->-    sequentially (checkExp src >>= unifies "type of target array" t) $ \src' _ -> do--    src_t <- expTypeFully src'-    unless (unique src_t) $-      typeError loc mempty $ "Source" <+> pquote (ppr src) <+>-      "has type" <+> ppr src_t <> ", which is not unique."--    let src_als = aliases src_t-    ve_t <- expTypeFully ve'-    unless (S.null $ src_als `S.intersection` aliases ve_t) $ badLetWithValue loc--    consume loc src_als-    return $ Update src' idxes' ve' loc---- Record updates are a bit hacky, because we do not have row typing--- (yet?).  For now, we only permit record updates where we know the--- full type up to the field we are updating.-checkExp (RecordUpdate src fields ve NoInfo loc) = do-  src' <- checkExp src-  ve' <- checkExp ve-  a <- expTypeFully src'-  let usage = mkUsage loc "record update"-  r <- foldM (flip $ mustHaveField usage) a fields-  ve_t <- expType ve'-  let r' = anySizes $ toStruct r-      ve_t' = anySizes $ toStruct ve_t-  onFailure (CheckingRecordUpdate fields r' ve_t') $-    unify usage r' ve_t'-  maybe_a' <- onRecordField (const ve_t) fields <$> expTypeFully src'-  case maybe_a' of-    Just a' -> return $ RecordUpdate src' fields ve' (Info a') loc-    Nothing -> typeError loc mempty $-               "Full type of" </>-               indent 2 (ppr src) </>-               textwrap " is not known at this point.  Add a size annotation to the original record to disambiguate."--checkExp (Index e idxes _ loc) = do-  (t, _) <- newArrayType loc "e" $ length idxes-  e' <- unifies "being indexed at" t =<< checkExp e-  idxes' <- mapM checkDimIndex idxes-  -- XXX, the RigidSlice here will be overridden in sliceShape with a proper value.-  (t', retext) <--    sliceShape (Just (loc, Rigid (RigidSlice Nothing ""))) idxes' =<<-    expTypeFully e'--  -- Remove aliases if the result is an overloaded type, because that-  -- will certainly not be aliased.-  t'' <- noAliasesIfOverloaded t'--  return $ Index e' idxes' (Info t'', Info retext) loc--checkExp (Assert e1 e2 NoInfo loc) = do-  e1' <- require "being asserted" [Bool] =<< checkExp e1-  e2' <- checkExp e2-  return $ Assert e1' e2' (Info (pretty e1)) loc--checkExp (Lambda params body rettype_te NoInfo loc) =-  removeSeminullOccurences $ noUnique $ incLevel $-  bindingParams [] params $ \_ params' -> do-    rettype_checked <- traverse checkTypeExp rettype_te-    let declared_rettype =-          case rettype_checked of Just (_, st, _) -> Just st-                                  Nothing -> Nothing-    (body', closure) <--      tapOccurences $ checkFunBody params' body declared_rettype loc-    body_t <- expTypeFully body'--    params'' <- mapM updateTypes params'--    (rettype', rettype_st) <--      case rettype_checked of-        Just (te, st, _) ->-          return (Just te, st)-        Nothing -> do-          ret <- inferReturnSizes params'' $ toStruct $-                 inferReturnUniqueness params'' body_t-          return (Nothing, ret)--    checkGlobalAliases params' body_t loc-    verifyFunctionParams Nothing params'--    closure' <- lexicalClosure params'' closure--    return $ Lambda params'' body' rettype' (Info (closure', rettype_st)) loc--  where-    -- Inferring the sizes of the return type of a lambda is a lot-    -- like let-generalisation.  We wish to remove any rigid sizes-    -- that were created when checking the body, except for those that-    -- are visible in types that existed before we entered the body,-    -- are parameters, or are used in parameters.-    inferReturnSizes params' ret = do-      cur_lvl <- curLevel-      let named (Named x, _) = Just x-          named (Unnamed, _) = Nothing-          param_names = mapMaybe (named . patternParam) params'-          pos_sizes =-            typeDimNamesPos (foldFunType (map patternStructType params') ret)-          hide k (lvl, _) =-            lvl >= cur_lvl && k `notElem` param_names && k `S.notMember` pos_sizes--      hidden_sizes <--        S.fromList . M.keys . M.filterWithKey hide <$> getConstraints--      let onDim (NamedDim name)-            | not (qualLeaf name `S.member` hidden_sizes) = NamedDim name-            | otherwise = AnyDim-          onDim d = d--      return $ first onDim ret--checkExp (OpSection op _ loc) = do-  (op', ftype) <- lookupVar loc op-  return $ OpSection op' (Info ftype) loc--checkExp (OpSectionLeft op _ e _ _ loc) = do-  (op', ftype) <- lookupVar loc op-  e_arg <- checkArg e-  (t1, rt, argext, retext) <- checkApply loc (Just op', 0) ftype e_arg-  case rt of-    Scalar (Arrow _ _ t2 rettype) ->-      return $ OpSectionLeft op' (Info ftype) (argExp e_arg)-      (Info (toStruct t1, argext), Info $ toStruct t2) (Info rettype, Info retext) loc-    _ -> typeError loc mempty $-         "Operator section with invalid operator of type" <+> ppr ftype--checkExp (OpSectionRight op _ e _ NoInfo loc) = do-  (op', ftype) <- lookupVar loc op-  e_arg <- checkArg e-  case ftype of-    Scalar (Arrow as1 m1 t1 (Scalar (Arrow as2 m2 t2 ret))) -> do-      (t2', ret', argext, _) <--        checkApply loc (Just op', 1)-        (Scalar $ Arrow as2 m2 t2 $ Scalar $ Arrow as1 m1 t1 ret) e_arg-      return $ OpSectionRight op' (Info ftype) (argExp e_arg)-        (Info $ toStruct t1, Info (toStruct t2', argext))-        (Info $ addAliases ret (<>aliases ret')) loc-    _ -> typeError loc mempty $-         "Operator section with invalid operator of type" <+> ppr ftype--checkExp (ProjectSection fields NoInfo loc) = do-  a <- newTypeVar loc "a"-  let usage = mkUsage loc "projection at"-  b <- foldM (flip $ mustHaveField usage) a fields-  return $ ProjectSection fields (Info $ Scalar $ Arrow mempty Unnamed a b) loc--checkExp (IndexSection idxes NoInfo loc) = do-  (t, _) <- newArrayType loc "e" $ length idxes-  idxes' <- mapM checkDimIndex idxes-  (t', _) <- sliceShape Nothing idxes' t-  return $ IndexSection idxes' (Info $ fromStruct $ Scalar $ Arrow mempty Unnamed t t') loc--checkExp (DoLoop _ mergepat mergeexp form loopbody NoInfo loc) =-  sequentially (checkExp mergeexp) $ \mergeexp' _ -> do--  zeroOrderType (mkUsage (srclocOf mergeexp) "use as loop variable")-    "type used as loop variable" =<< expTypeFully mergeexp'--  -- The handling of dimension sizes is a bit intricate, but very-  -- similar to checking a function, followed by checking a call to-  -- it.  The overall procedure is as follows:-  ---  -- (1) All empty dimensions in the merge pattern are instantiated-  -- with nonrigid size variables.  All explicitly specified-  -- dimensions are preserved.-  ---  -- (2) The body of the loop is type-checked.  The result type is-  -- combined with the merge pattern type to determine which sizes are-  -- variant, and these are turned into size parameters for the merge-  -- pattern.-  ---  -- (3) We now conceptually have a function parameter type and return-  -- type.  We check that it can be called with the initial merge-  -- values as argument.  The result of this is the type of the loop-  -- as a whole.-  ---  -- (There is also a convergence loop for inferring uniqueness, but-  -- that's orthogonal to the size handling.)--  (merge_t, new_dims) <--    instantiateEmptyArrayDims loc "loop" Nonrigid . -- dim handling (1)-    anySizes-    =<< expTypeFully mergeexp'--  -- dim handling (2)-  let checkLoopReturnSize mergepat' loopbody' = do-        loopbody_t <- expTypeFully loopbody'-        pat_t <- normTypeFully $ patternType mergepat'-        -- We are ignoring the dimensions here, because any mismatches-        -- should be turned into fresh size variables.-        onFailure (CheckingLoopBody (toStruct (anySizes pat_t)) (toStruct loopbody_t)) $-          expect (mkUsage (srclocOf loopbody) "matching loop body to loop pattern")-          (toStruct (anySizes pat_t))-          (toStruct loopbody_t)-        pat_t' <- normTypeFully pat_t-        loopbody_t' <- normTypeFully loopbody_t--        -- For each new_dims, figure out what they are instantiated-        -- with in the initial value.  This is used to determine-        -- whether a size is invariant because it always matches the-        -- initial instantiation of that size.-        let initSubst (NamedDim v, d) = Just (v, d)-            initSubst _ = Nothing-        init_substs <- M.fromList . mapMaybe initSubst . snd .-                       anyDimOnMismatch pat_t' <$>-                       expTypeFully mergeexp'--        -- Figure out which of the 'new_dims' dimensions are variant.-        -- This works because we know that each dimension from-        -- new_dims in the pattern is unique and distinct.-        ---        -- Our logic here is a bit reversed: the *mismatches* (from-        -- new_dims) are what we want to extract and turn into size-        -- parameters.-        let mismatchSubst (NamedDim v, d)-              | qualLeaf v `elem` new_dims =-                  case M.lookup v init_substs of-                    Just d'-                      | d' == d ->-                          return $ Just (qualLeaf v, SizeSubst d)-                    _ -> do tell [qualLeaf v]-                            return Nothing-            mismatchSubst _ = return Nothing--            (init_substs', sparams) =-              runWriter $ M.fromList . catMaybes <$> mapM mismatchSubst-              (snd $ anyDimOnMismatch pat_t' loopbody_t')--        -- Make sure that any of new_dims that are invariant will be-        -- replaced with the invariant size in the loop body.  Failure-        -- to do this can cause type annotations to still refer to-        -- new_dims.-        let dimToInit (v, SizeSubst d) =-              constrain v $ Size (Just d) (mkUsage loc "size of loop parameter")-            dimToInit _ =-              return ()-        mapM_ dimToInit $ M.toList init_substs'--        mergepat'' <- applySubst (`M.lookup` init_substs') <$> updateTypes mergepat'-        return (nub sparams, mergepat'')--  -- First we do a basic check of the loop body to figure out which of-  -- the merge parameters are being consumed.  For this, we first need-  -- to check the merge pattern, which requires the (initial) merge-  -- expression.-  ---  -- Play a little with occurences to ensure it does not look like-  -- none of the merge variables are being used.-  ((sparams, mergepat', form', loopbody'), bodyflow) <--    case form of-      For i uboundexp -> do-        uboundexp' <- require "being the bound in a 'for' loop" anySignedType =<< checkExp uboundexp-        bound_t <- expTypeFully uboundexp'-        bindingIdent i bound_t $ \i' ->-          noUnique $ bindingPattern mergepat (Ascribed merge_t) $-          \mergepat' -> onlySelfAliasing $ tapOccurences $ do-            loopbody' <- noSizeEscape $ checkExp loopbody-            (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'-            return (sparams,-                    mergepat'',-                    For i' uboundexp',-                    loopbody')--      ForIn xpat e -> do-        (arr_t, _) <- newArrayType (srclocOf e) "e" 1-        e' <- unifies "being iterated in a 'for-in' loop" arr_t =<< checkExp e-        t <- expTypeFully e'-        case t of-          _ | Just t' <- peelArray 1 t ->-                bindingPattern xpat (Ascribed t') $ \xpat' ->-                noUnique $ bindingPattern mergepat (Ascribed merge_t) $-                \mergepat' -> onlySelfAliasing $ tapOccurences $ do-                  loopbody' <- noSizeEscape $ checkExp loopbody-                  (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'-                  return (sparams,-                          mergepat'',-                          ForIn xpat' e',-                          loopbody')-            | otherwise ->-                typeError (srclocOf e) mempty $-                "Iteratee of a for-in loop must be an array, but expression has type" <+>-                ppr t--      While cond ->-        noUnique $ bindingPattern mergepat (Ascribed merge_t) $ \mergepat' ->-        onlySelfAliasing $ tapOccurences $-        sequentially (checkExp cond >>=-                      unifies "being the condition of a 'while' loop" (Scalar $ Prim Bool)) $ \cond' _ -> do-          loopbody' <- noSizeEscape $ checkExp loopbody-          (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'-          return (sparams,-                  mergepat'',-                  While cond',-                  loopbody')--  mergepat'' <- do-    loopbody_t <- expTypeFully loopbody'-    convergePattern mergepat' (allConsumed bodyflow) loopbody_t $-      mkUsage (srclocOf loopbody') "being (part of) the result of the loop body"--  let consumeMerge (Id _ (Info pt) ploc) mt-        | unique pt = consume ploc $ aliases mt-      consumeMerge (TuplePattern pats _) t | Just ts <- isTupleRecord t =-        zipWithM_ consumeMerge pats ts-      consumeMerge (PatternParens pat _) t =-        consumeMerge pat t-      consumeMerge (PatternAscription pat _ _) t =-        consumeMerge pat t-      consumeMerge _ _ =-        return ()-  consumeMerge mergepat'' =<< expTypeFully mergeexp'--  -- dim handling (3)-  let sparams_anydim = M.fromList $ zip sparams $ repeat $ SizeSubst AnyDim-      loopt_anydims = applySubst (`M.lookup` sparams_anydim) $-                      patternType mergepat''-  (merge_t', _) <--    instantiateEmptyArrayDims loc "loopres" Nonrigid $ toStruct loopt_anydims-  mergeexp_t <- toStruct <$> expTypeFully mergeexp'-  onFailure (CheckingLoopInitial (toStruct loopt_anydims) mergeexp_t) $-    unify (mkUsage (srclocOf mergeexp') "matching initial loop values to pattern")-    merge_t' mergeexp_t--  (loopt, retext) <- instantiateDimsInType loc RigidLoop loopt_anydims-  -- We set all of the uniqueness to be unique.  This is intentional,-  -- and matches what happens for function calls.  Those arrays that-  -- really *cannot* be consumed will alias something unconsumable,-  -- and will be caught that way.-  let bound_here = patternNames mergepat'' <> S.fromList sparams <> form_bound-      form_bound =-        case form' of-          For v _ -> S.singleton $ identName v-          ForIn forpat _ -> patternNames forpat-          While{} -> mempty-      loopt' = second (`S.difference` S.map AliasBound bound_here) $-               loopt `setUniqueness` Unique---  -- Eliminate those new_dims that turned into sparams so it won't-  -- look like we have ambiguous sizes lying around.-  modifyConstraints $ M.filterWithKey $ \k _ -> k `notElem` sparams--  return $ DoLoop sparams mergepat'' mergeexp' form' loopbody' (Info (loopt', retext)) loc--  where-    convergePattern pat body_cons body_t body_loc = do-      let consumed_merge = patternNames pat `S.intersection` body_cons--          uniquePat (Wildcard (Info t) wloc) =-            Wildcard (Info $ t `setUniqueness` Nonunique) wloc-          uniquePat (PatternParens p ploc) =-            PatternParens (uniquePat p) ploc-          uniquePat (Id name (Info t) iloc)-            | name `S.member` consumed_merge =-                let t' = t `setUniqueness` Unique `setAliases` mempty-                in Id name (Info t') iloc-            | otherwise =-                let t' = t `setUniqueness` Nonunique-                in Id name (Info t') iloc-          uniquePat (TuplePattern pats ploc) =-            TuplePattern (map uniquePat pats) ploc-          uniquePat (RecordPattern fs ploc) =-            RecordPattern (map (fmap uniquePat) fs) ploc-          uniquePat (PatternAscription p t ploc) =-            PatternAscription p t ploc-          uniquePat p@PatternLit{} = p-          uniquePat (PatternConstr n t ps ploc) =-            PatternConstr n t (map uniquePat ps) ploc--          -- Make the pattern unique where needed.-          pat' = uniquePat pat--      pat_t <- normTypeFully $ patternType pat'-      unless (toStructural body_t `subtypeOf` toStructural pat_t) $-        unexpectedType (srclocOf body_loc) (toStruct body_t) [toStruct pat_t]--      -- Check that the new values of consumed merge parameters do not-      -- alias something bound outside the loop, AND that anything-      -- returned for a unique merge parameter does not alias anything-      -- else returned.  We also update the aliases for the pattern.-      bound_outside <- asks $ S.fromList . M.keys . scopeVtable . termScope-      let combAliases t1 t2 =-            case t1 of Scalar Record{} -> t1-                       _ -> t1 `addAliases` (<>aliases t2)--          checkMergeReturn (Id pat_v (Info pat_v_t) patloc) t-            | unique pat_v_t,-              v:_ <- S.toList $-                     S.map aliasVar (aliases t) `S.intersection` bound_outside =-                lift $ typeError loc mempty $-                "Return value for loop parameter" <+>-                pquote (pprName pat_v) <+> "aliases" <+> pprName v <> "."--            | otherwise = do-                (cons,obs) <- get-                unless (S.null $ aliases t `S.intersection` cons) $-                  lift $ typeError loc mempty $-                  "Return value for loop parameter" <+>-                  pquote (pprName pat_v) <+>-                  "aliases other consumed loop parameter."-                when (unique pat_v_t &&-                      not (S.null (aliases t `S.intersection` (cons<>obs)))) $-                  lift $ typeError loc mempty $-                  "Return value for consuming loop parameter" <+>-                  pquote (pprName pat_v) <+> "aliases previously returned value."-                if unique pat_v_t-                  then put (cons<>aliases t, obs)-                  else put (cons, obs<>aliases t)--                return $ Id pat_v (Info (combAliases pat_v_t t)) patloc--          checkMergeReturn (Wildcard (Info pat_v_t) patloc) t =-            return $ Wildcard (Info (combAliases pat_v_t t)) patloc--          checkMergeReturn (PatternParens p _) t =-            checkMergeReturn p t--          checkMergeReturn (PatternAscription p _ _) t =-            checkMergeReturn p t--          checkMergeReturn (RecordPattern pfs patloc) (Scalar (Record tfs)) =-            RecordPattern . M.toList <$> sequence pfs' <*> pure patloc-            where pfs' = M.intersectionWith checkMergeReturn-                         (M.fromList pfs) tfs--          checkMergeReturn (TuplePattern pats patloc) t-            | Just ts <- isTupleRecord t =-                TuplePattern-                <$> zipWithM checkMergeReturn pats ts-                <*> pure patloc--          checkMergeReturn p _ =-            return p--      (pat'', (pat_cons, _)) <--        runStateT (checkMergeReturn pat' body_t) (mempty, mempty)--      let body_cons' = body_cons <> S.map aliasVar pat_cons-      if body_cons' == body_cons && patternType pat'' == patternType pat-        then return pat'-        else convergePattern pat'' body_cons' body_t body_loc--checkExp (Constr name es NoInfo loc) = do-  t <- newTypeVar loc "t"-  es' <- mapM checkExp es-  ets <- mapM expTypeFully es'-  mustHaveConstr (mkUsage loc "use of constructor") name t (toStruct <$> ets)-  -- A sum value aliases *anything* that went into its construction.-  let als = foldMap aliases ets-  return $ Constr name es' (Info $ fromStruct t `addAliases` (<>als)) loc--checkExp (Match e cs _ loc) =-  sequentially (checkExp e) $ \e' _ -> do-    mt <- expTypeFully e'-    (cs', t, retext) <- checkCases mt cs-    zeroOrderType (mkUsage loc "being returned 'match'")-      "type returned from pattern match" t-    return $ Match e' cs' (Info t, Info retext) loc--checkExp (Attr info e loc) =-  Attr info <$> checkExp e <*> pure loc--checkCases :: PatternType-           -> NE.NonEmpty (CaseBase NoInfo Name)-           -> TermTypeM (NE.NonEmpty (CaseBase Info VName), PatternType, [VName])-checkCases mt rest_cs =-  case NE.uncons rest_cs of-    (c, Nothing) -> do-      (c', t, retext) <- checkCase mt c-      return (c' NE.:| [], t, retext)-    (c, Just cs) -> do-      (((c', c_t, _), (cs', cs_t, _)), dflow) <--        tapOccurences $ checkCase mt c `alternative` checkCases mt cs-      (brancht, retext) <- unifyBranchTypes (srclocOf c) c_t cs_t-      let t = addAliases brancht-              (`S.difference` S.map AliasBound (allConsumed dflow))-      return (NE.cons c' cs', t, retext)--checkCase :: PatternType -> CaseBase NoInfo Name-          -> TermTypeM (CaseBase Info VName, PatternType, [VName])-checkCase mt (CasePat p e loc) =-  bindingPattern p (Ascribed mt) $ \p' -> do-    e' <- checkExp e-    (t, retext) <- unscopeType loc (patternMap p') =<< expTypeFully e'-    return (CasePat p' e' loc, t, retext)---- | An unmatched pattern. Used in in the generation of--- unmatched pattern warnings by the type checker.-data Unmatched p = UnmatchedNum p [ExpBase Info VName]-                 | UnmatchedBool p-                 | UnmatchedConstr p-                 | Unmatched p-                 deriving (Functor, Show)--instance Pretty (Unmatched (PatternBase Info VName)) where-  ppr um = case um of-      (UnmatchedNum p nums) -> ppr' p <+> "where p is not one of" <+> ppr nums-      (UnmatchedBool p)     -> ppr' p-      (UnmatchedConstr p)     -> ppr' p-      (Unmatched p)         -> ppr' p-    where-      ppr' (PatternAscription p t _) = ppr p <> ":" <+> ppr t-      ppr' (PatternParens p _)       = parens $ ppr' p-      ppr' (Id v _ _)                = pprName v-      ppr' (TuplePattern pats _)     = parens $ commasep $ map ppr' pats-      ppr' (RecordPattern fs _)      = braces $ commasep $ map ppField fs-        where ppField (name, t)      = text (nameToString name) <> equals <> ppr' t-      ppr' Wildcard{}                = "_"-      ppr' (PatternLit e _ _)        = ppr e-      ppr' (PatternConstr n _ ps _)   = "#" <> ppr n <+> sep (map ppr' ps)--unpackPat :: Pattern -> [Maybe Pattern]-unpackPat Wildcard{} = [Nothing]-unpackPat (PatternParens p _) = unpackPat p-unpackPat Id{} = [Nothing]-unpackPat (TuplePattern ps _) = Just <$> ps-unpackPat (RecordPattern fs _) = Just . snd <$> sortFields (M.fromList fs)-unpackPat (PatternAscription p _ _) = unpackPat p-unpackPat p@PatternLit{} = [Just p]-unpackPat p@PatternConstr{} = [Just p]--wildPattern :: Pattern -> Int -> Unmatched Pattern -> Unmatched Pattern-wildPattern (TuplePattern ps loc) pos um = wildTuple <$> um-  where wildTuple p = TuplePattern (take (pos - 1) ps' ++ [p] ++ drop pos ps') loc-        ps' = map wildOut ps-        wildOut p = Wildcard (Info (patternType p)) (srclocOf p)-wildPattern (RecordPattern fs loc) pos um = wildRecord <$> um-  where wildRecord p =-          RecordPattern (take (pos - 1) fs' ++ [(fst (fs!!(pos - 1)), p)] ++ drop pos fs') loc-        fs' = map wildOut fs-        wildOut (f,p) = (f, Wildcard (Info (patternType p)) (srclocOf p))-wildPattern (PatternAscription p _ _) pos um = wildPattern p pos um-wildPattern (PatternParens p _) pos um = wildPattern p pos um-wildPattern (PatternConstr n t ps loc) pos um = wildConstr <$> um-  where wildConstr p = PatternConstr n t (take (pos - 1) ps' ++ [p] ++ drop pos ps') loc-        ps' = map wildOut ps-        wildOut p = Wildcard (Info (patternType p)) (srclocOf p)-wildPattern _ _ um = um--checkUnmatched :: Exp -> TermTypeM ()-checkUnmatched e = void $ checkUnmatched' e >> astMap tv e-  where checkUnmatched' (Match _ cs _ loc) =-          let ps = fmap (\(CasePat p _ _) -> p) cs-          in case unmatched id $ NE.toList ps of-              []  -> return ()-              ps' -> typeError loc mempty $-                     "Unmatched cases in match expression:" </>-                     indent 2 (stack (map ppr ps'))-        checkUnmatched' _ = return ()-        tv = ASTMapper { mapOnExp =-                           \e' -> checkUnmatched' e' >> return e'-                       , mapOnName        = pure-                       , mapOnQualName    = pure-                       , mapOnStructType  = pure-                       , mapOnPatternType = pure-                       }---- | A data type for constructor patterns.  This is used to make the--- code for detecting unmatched constructors cleaner, by separating--- the constructor-pattern cases from other cases.-data ConstrPat = ConstrPat { constrName :: Name-                           , constrType :: PatternType-                           , constrPayload :: [Pattern]-                           , constrSrcLoc :: SrcLoc-                           }---- Be aware of these fishy equality instances!--instance Eq ConstrPat where-  ConstrPat c1 _ _ _ == ConstrPat c2 _ _ _ = c1 == c2--instance Ord ConstrPat where-  ConstrPat c1 _ _ _ `compare` ConstrPat c2 _ _ _ = c1 `compare` c2--unmatched :: (Unmatched Pattern -> Unmatched Pattern) -> [Pattern] -> [Unmatched Pattern]-unmatched hole orig_ps-  | p:_ <- orig_ps,-    sameStructure labeledCols = do-    (i, cols) <- labeledCols-    let hole' = if isConstr p then hole else hole . wildPattern p i-    case sequence cols of-      Nothing -> []-      Just cs-        | all isPatternLit cs  -> map hole' $ localUnmatched cs-        | otherwise            -> unmatched hole' cs-  | otherwise = []--  where labeledCols = zip [1..] $ transpose $ map unpackPat orig_ps--        localUnmatched :: [Pattern] -> [Unmatched Pattern]-        localUnmatched [] = []-        localUnmatched ps'@(p':_) =-          case patternType p'  of-            Scalar (Sum cs'') ->-              -- We now know that we are matching a sum type, and thus-              -- that all patterns ps' are constructors (checked by-              -- 'all isPatternLit' before this function is called).-              let constrs   = M.keys cs''-                  matched   = mapMaybe constr ps'-                  unmatched' = map (UnmatchedConstr . buildConstr cs'') $-                               constrs \\ map constrName matched-             in case unmatched' of-                [] ->-                  let constrGroups   = group (sort matched)-                      removedConstrs = mapMaybe stripConstrs constrGroups-                      transposed     = (fmap . fmap) transpose removedConstrs-                      findUnmatched (pc, trans) = do-                        col <- trans-                        case col of-                          []           -> []-                          ((i, _):_) -> unmatched (wilder i pc) (map snd col)-                      wilder i pc s = (`PatternParens` mempty) <$> wildPattern pc i s-                  in concatMap findUnmatched transposed-                _ -> unmatched'-            Scalar (Prim t) | not (any idOrWild ps') ->-              -- We now know that we are matching a sum type, and thus-              -- that all patterns ps' are literals (checked by 'all-              -- isPatternLit' before this function is called).-                case t of-                  Bool ->-                    let matched = nub $ mapMaybe (pExp >=> bool) $ filter isPatternLit ps'-                    in map (UnmatchedBool . buildBool (Scalar (Prim t))) $ [True, False] \\ matched-                  _ ->-                    let matched = mapMaybe pExp $ filter isPatternLit ps'-                    in [UnmatchedNum (buildId (Info $ Scalar $ Prim t) "p") matched]-            _ -> []--        isConstr PatternConstr{} = True-        isConstr (PatternParens p _) = isConstr p-        isConstr _ = False---        stripConstrs :: [ConstrPat] -> Maybe (Pattern, [[(Int, Pattern)]])-        stripConstrs (pc@ConstrPat{} : cs') = Just (unConstr pc, stripConstr pc : map stripConstr cs')-        stripConstrs [] = Nothing--        stripConstr :: ConstrPat -> [(Int, Pattern)]-        stripConstr (ConstrPat _ _  ps' _) = zip [1..] ps'--        sameStructure [] = True-        sameStructure (x:xs) = all (\y -> length y == length x' ) xs'-          where (x':xs') = map snd (x:xs)--        pExp (PatternLit e' _ _) = Just e'-        pExp _ = Nothing--        constr (PatternConstr c (Info t) ps loc) = Just $ ConstrPat c t ps loc-        constr (PatternParens p _) = constr p-        constr (PatternAscription p' _ _)  = constr p'-        constr _ = Nothing--        unConstr p =-          PatternConstr (constrName p) (Info $ constrType p) (constrPayload p) (constrSrcLoc p)--        isPatternLit PatternLit{} = True-        isPatternLit (PatternAscription p' _ _) = isPatternLit p'-        isPatternLit (PatternParens p' _)  = isPatternLit p'-        isPatternLit PatternConstr{} = True-        isPatternLit _ = False--        idOrWild Id{} = True-        idOrWild Wildcard{} = True-        idOrWild (PatternAscription p' _ _) = idOrWild p'-        idOrWild (PatternParens p' _) = idOrWild p'-        idOrWild _ = False--        bool (Literal (BoolValue b) _ ) = Just b-        bool _ = Nothing--        buildConstr m c =-          let t      = Scalar $ Sum m-              cs     = m M.! c-              wildCS = map (\ct -> Wildcard (Info ct) mempty) cs-          in if null wildCS-               then PatternConstr c (Info t) [] mempty-               else PatternParens (PatternConstr c (Info t) wildCS mempty) mempty-        buildBool t b =-          PatternLit (Literal (BoolValue b) mempty) (Info (addSizes t)) mempty-        buildId t n =-          -- The VName tag here will never be used since the value-          -- exists exclusively for printing warnings.-          Id (VName (nameFromString n) (-1)) t mempty--checkIdent :: IdentBase NoInfo Name -> TermTypeM Ident-checkIdent (Ident name _ loc) = do-  (QualName _ name', vt) <- lookupVar loc (qualName name)-  return $ Ident name' (Info vt) loc--checkDimIndex :: DimIndexBase NoInfo Name -> TermTypeM DimIndex-checkDimIndex (DimFix i) =-  DimFix <$> (unifies "use as index" (Scalar $ Prim $ Signed Int32) =<< checkExp i)-checkDimIndex (DimSlice i j s) =-  DimSlice <$> check i <*> check j <*> check s-  where check = maybe (return Nothing) $-                fmap Just . unifies "use as index" (Scalar $ Prim $ Signed Int32) <=< checkExp--sequentially :: TermTypeM a -> (a -> Occurences -> TermTypeM b) -> TermTypeM b-sequentially m1 m2 = do-  (a, m1flow) <- collectOccurences m1-  (b, m2flow) <- collectOccurences $ m2 a m1flow-  occur $ m1flow `seqOccurences` m2flow-  return b--type Arg = (Exp, PatternType, Occurences, SrcLoc)--argExp :: Arg -> Exp-argExp (e, _, _, _) = e--argType :: Arg -> PatternType-argType (_, t, _, _) = t--checkArg :: UncheckedExp -> TermTypeM Arg-checkArg arg = do-  (arg', dflow) <- collectOccurences $ checkExp arg-  arg_t <- expType arg'-  return (arg', arg_t, dflow, srclocOf arg')--instantiateDimsInType :: SrcLoc -> RigidSource-                      -> TypeBase (DimDecl VName) als-                      -> TermTypeM (TypeBase (DimDecl VName) als, [VName])-instantiateDimsInType tloc rsrc =-  instantiateEmptyArrayDims tloc "d" $ Rigid rsrc--instantiateDimsInReturnType :: SrcLoc -> Maybe (QualName VName)-                            -> TypeBase (DimDecl VName) als-                            -> TermTypeM (TypeBase (DimDecl VName) als, [VName])-instantiateDimsInReturnType tloc fname =-  instantiateEmptyArrayDims tloc "ret" $ Rigid $ RigidRet fname---- Some information about the function/operator we are trying to--- apply, and how many arguments it has previously accepted.  Used for--- generating nicer type errors.-type ApplyOp = (Maybe (QualName VName), Int)--checkApply :: SrcLoc -> ApplyOp -> PatternType -> Arg-           -> TermTypeM (PatternType, PatternType, Maybe VName, [VName])-checkApply loc (fname, _)-           (Scalar (Arrow as pname tp1 tp2))-           (argexp, argtype, dflow, argloc) =-  onFailure (CheckingApply fname argexp (toStruct tp1) (toStruct argtype)) $ do-  expect (mkUsage argloc "use as function argument") (toStruct tp1) (toStruct argtype)--  -- Perform substitutions of instantiated variables in the types.-  tp1' <- normTypeFully tp1-  (tp2', ext) <- instantiateDimsInReturnType loc fname =<< normTypeFully tp2-  argtype' <- normTypeFully argtype--  -- Check whether this would produce an impossible return type.-  let (_, tp2_paramdims, _) = dimUses $ toStruct tp2'-  case filter (`S.member` tp2_paramdims) ext of-    [] -> return ()-    ext_paramdims -> do-      let onDim (NamedDim qn)-            | qualLeaf qn `elem` ext_paramdims = AnyDim-          onDim d = d-      typeError loc mempty $-        "Anonymous size would appear in function parameter of return type:" </>-        indent 2 (ppr (first onDim tp2')) </>-        textwrap "This is usually because a higher-order function is used with functional arguments that return anonymous sizes, which are then used as parameters of other function arguments."--  occur [observation as loc]--  checkOccurences dflow--  case anyConsumption dflow of-    Just c ->-      let msg = "type of expression with consumption at " ++ locStr (location c)-      in zeroOrderType (mkUsage argloc "potential consumption in expression") msg tp1-    _ -> return ()--  occurs <- (dflow `seqOccurences`) <$> consumeArg argloc argtype' (diet tp1')--  checkIfConsumable loc $ S.map AliasBound $ allConsumed occurs-  occur occurs--  (argext, parsubst) <--    case pname of-      Named pname' -> do-        (d, argext) <- sizeSubst tp1' argexp-        return (argext,-                (`M.lookup` M.singleton pname' (SizeSubst d)))-      _ -> return (Nothing, const Nothing)-  let tp2'' = applySubst parsubst $ returnType tp2' (diet tp1') argtype'--  return (tp1', tp2'', argext, ext)-  where sizeSubst (Scalar (Prim (Signed Int32))) e = dimFromArg fname e-        sizeSubst _ _ = return (AnyDim, Nothing)--checkApply loc fname tfun@(Scalar TypeVar{}) arg = do-  tv <- newTypeVar loc "b"-  unify (mkUsage loc "use as function") (toStruct tfun) $-    Scalar $ Arrow mempty Unnamed (toStruct (argType arg)) tv-  tfun' <- normPatternType tfun-  checkApply loc fname tfun' arg--checkApply loc (fname, prev_applied) ftype (argexp, _, _, _) = do-  let fname' = maybe "expression" (pquote . ppr) fname--  typeError loc mempty $-    if prev_applied == 0-    then "Cannot apply" <+> fname' <+> "as function, as it has type:" </>-         indent 2 (ppr ftype)-    else "Cannot apply" <+> fname' <+> "to argument #" <> ppr (prev_applied+1) <+>-         pquote (shorten $ pretty $ flatten $ ppr argexp) <> "," <+/>-         "as" <+> fname' <+> "only takes" <+> ppr prev_applied <+>-         arguments <> "."-  where arguments | prev_applied == 1 = "argument"-                  | otherwise = "arguments"--isInt32 :: Exp -> Maybe Int32-isInt32 (Literal (SignedValue (Int32Value k')) _) = Just $ fromIntegral k'-isInt32 (IntLit k' _ _) = Just $ fromInteger k'-isInt32 (Negate x _) = negate <$> isInt32 x-isInt32 _ = Nothing--maybeDimFromExp :: Exp -> Maybe (DimDecl VName)-maybeDimFromExp (Var v _ _) = Just $ NamedDim v-maybeDimFromExp (Parens e _) = maybeDimFromExp e-maybeDimFromExp (QualParens _ e _) = maybeDimFromExp e-maybeDimFromExp e = ConstDim . fromIntegral <$> isInt32 e--dimFromExp :: (Exp -> SizeSource) -> Exp -> TermTypeM (DimDecl VName, Maybe VName)-dimFromExp rf (Parens e _) = dimFromExp rf e-dimFromExp rf (QualParens _ e _) = dimFromExp rf e-dimFromExp rf e-  | Just d <- maybeDimFromExp e =-      return (d, Nothing)-  | otherwise =-      extSize (srclocOf e) $ rf e--dimFromArg :: Maybe (QualName VName) -> Exp -> TermTypeM (DimDecl VName, Maybe VName)-dimFromArg fname = dimFromExp $ SourceArg (FName fname) . bareExp---- | @returnType ret_type arg_diet arg_type@ gives result of applying--- an argument the given types to a function with the given return--- type, consuming the argument with the given diet.-returnType :: PatternType-           -> Diet-           -> PatternType-           -> PatternType-returnType (Array _ Unique et shape) _ _ =-  Array mempty Unique et shape-returnType (Array als Nonunique et shape) d arg =-  Array (als<>arg_als) Unique et shape -- Intentional!-  where arg_als = aliases $ maskAliases arg d-returnType (Scalar (Record fs)) d arg =-  Scalar $ Record $ fmap (\et -> returnType et d arg) fs-returnType (Scalar (Prim t)) _ _ =-  Scalar $ Prim t-returnType (Scalar (TypeVar _ Unique t targs)) _ _ =-  Scalar $ TypeVar mempty Unique t targs-returnType (Scalar (TypeVar als Nonunique t targs)) d arg =-  Scalar $ TypeVar (als<>arg_als) Unique t targs -- Intentional!-  where arg_als = aliases $ maskAliases arg d-returnType (Scalar (Arrow old_als v t1 t2)) d arg =-  Scalar $ Arrow als v (t1 `setAliases` mempty) (t2 `setAliases` als)-  -- Make sure to propagate the aliases of an existing closure.-  where als = old_als <> aliases (maskAliases arg d)-returnType (Scalar (Sum cs)) d arg =-  Scalar $ Sum $ (fmap . fmap) (\et -> returnType et d arg) cs---- | @t `maskAliases` d@ removes aliases (sets them to 'mempty') from--- the parts of @t@ that are denoted as consumed by the 'Diet' @d@.-maskAliases :: Monoid as =>-               TypeBase shape as-            -> Diet-            -> TypeBase shape as-maskAliases t Consume = t `setAliases` mempty-maskAliases t Observe = t-maskAliases (Scalar (Record ets)) (RecordDiet ds) =-  Scalar $ Record $ M.intersectionWith maskAliases ets ds-maskAliases t FuncDiet{} = t-maskAliases _ _ = error "Invalid arguments passed to maskAliases."--consumeArg :: SrcLoc -> PatternType -> Diet -> TermTypeM [Occurence]-consumeArg loc (Scalar (Record ets)) (RecordDiet ds) =-  concat . M.elems <$> traverse (uncurry $ consumeArg loc) (M.intersectionWith (,) ets ds)-consumeArg loc (Array _ Nonunique _ _) Consume =-  typeError loc mempty "Consuming parameter passed non-unique argument."-consumeArg loc (Scalar (TypeVar _ Nonunique _ _)) Consume =-  typeError loc mempty "Consuming parameter passed non-unique argument."-consumeArg loc (Scalar (Arrow _ _ t1 _)) (FuncDiet d _)-  | not $ contravariantArg t1 d =-      typeError loc mempty "Non-consuming higher-order parameter passed consuming argument."-  where contravariantArg (Array _ Unique _ _) Observe =-          False-        contravariantArg (Scalar (TypeVar _ Unique _ _)) Observe =-          False-        contravariantArg (Scalar (Record ets)) (RecordDiet ds) =-          and (M.intersectionWith contravariantArg ets ds)-        contravariantArg (Scalar (Arrow _ _ tp tr)) (FuncDiet dp dr) =-          contravariantArg tp dp && contravariantArg tr dr-        contravariantArg _ _ =-          True-consumeArg loc (Scalar (Arrow _ _ _ t2)) (FuncDiet _ pd) =-  consumeArg loc t2 pd-consumeArg loc at Consume = return [consumption (aliases at) loc]-consumeArg loc at _       = return [observation (aliases at) loc]---- | Type-check a single expression in isolation.  This expression may--- turn out to be polymorphic, in which case the list of type--- parameters will be non-empty.-checkOneExp :: UncheckedExp -> TypeM ([TypeParam], Exp)-checkOneExp e = fmap fst . runTermTypeM $ do-  e' <- checkExp e-  let t = toStruct $ typeOf e'-  (tparams, _, _, _) <--    letGeneralise (nameFromString "<exp>") (srclocOf e) [] [] t-  fixOverloadedTypes-  e'' <- updateTypes e'-  checkUnmatched e''-  causalityCheck e''-  literalOverflowCheck e''-  return (tparams, e'')---- Verify that all sum type constructors and empty array literals have--- a size that is known (rigid or a type parameter).  This is to--- ensure that we can actually determine their shape at run-time.-causalityCheck :: Exp -> TermTypeM ()-causalityCheck binding_body = do-  constraints <- getConstraints--  let checkCausality what known t loc-        | (d,dloc):_ <- mapMaybe (unknown constraints known) $-                        S.toList $ typeDimNames $ toStruct t =-            Just $ lift $ causality what loc d dloc t-        | otherwise = Nothing--      checkParamCausality known p =-        checkCausality (ppr p) known (patternType p) (srclocOf p)--      onExp :: S.Set VName -> Exp-            -> StateT (S.Set VName) (Either TypeError) Exp--      onExp known (Var v (Info t) loc)-        | Just bad <- checkCausality (pquote (ppr v)) known t loc =-            bad--      onExp known (ArrayLit [] (Info t) loc)-        | Just bad <- checkCausality "empty array" known t loc =-            bad--      onExp known (Lambda params _ _ _ _)-        | bad : _ <- mapMaybe (checkParamCausality known) params =-            bad--      onExp known e@(Coerce what _ (_, Info ext) _) = do-        modify (S.fromList ext<>)-        void $ onExp known what-        return e--      onExp known e@(LetPat _ bindee_e body_e (_, Info ext) _) = do-        sequencePoint known bindee_e body_e ext-        return e--      onExp known e@(Apply f arg (Info (_, p)) (_, Info ext) _) = do-        sequencePoint known arg f $ maybeToList p ++ ext-        return e--      onExp known e@(BinOp (f, floc) ft-                     (x, Info (_, xp)) (y, Info (_, yp)) _ (Info ext) _) = do-        args_known <- lift $-          execStateT (sequencePoint known x y $ catMaybes [xp, yp]) mempty-        void $ onExp (args_known<>known) (Var f ft floc)-        modify ((args_known<>S.fromList ext)<>)-        return e--      onExp known e = do-        recurse known e--        case e of-          DoLoop _ _ _ _ _ (Info (_, ext)) _ ->-            modify (<>S.fromList ext)-          If _ _ _ (_, Info ext) _ ->-            modify (<>S.fromList ext)-          Index _ _ (_, Info ext) _ ->-            modify (<>S.fromList ext)-          Match _ _ (_, Info ext) _ ->-            modify (<>S.fromList ext)-          Range _ _ _ (_, Info ext) _ ->-            modify (<>S.fromList ext)-          _ ->-            return ()--        return e--      recurse known = void . astMap mapper-        where mapper = identityMapper { mapOnExp = onExp known }--      sequencePoint known x y ext = do-        new_known <- lift $ execStateT (onExp known x) mempty-        void $ onExp (new_known<>known) y-        modify ((new_known<>S.fromList ext)<>)--  either throwError (const $ return ()) $-    evalStateT (onExp mempty binding_body) mempty-  where unknown constraints known v = do-          guard $ v `S.notMember` known-          loc <- unknowable constraints v-          return (v,loc)--        unknowable constraints v =-          case snd <$> M.lookup v constraints of-            Just (UnknowableSize loc _) -> Just loc-            _                           -> Nothing--        causality what loc d dloc t =-          Left $ TypeError loc mempty $-          "Causality check: size" <+/> pquote (pprName d) <+/>-          "needed for type of" <+> what <> colon </>-          indent 2 (ppr t) </>-          "But" <+> pquote (pprName d) <+> "is computed at" <+/>-          text (locStrRel loc dloc) <> "." </>-          "" </>-          "Hint:" <+>-          align (textwrap "Bind the expression producing" <+> pquote (pprName d) <+>-                 "with 'let' beforehand.")---- | Traverse the expression, emitting warnings if any of the literals overflow--- their inferred types------ Note: currently unable to detect float underflow (such as 1e-400 -> 0)-literalOverflowCheck :: Exp -> TermTypeM ()-literalOverflowCheck = void . check-  where check e@(IntLit x ty loc) = e <$ case ty of-          Info (Scalar (Prim t)) -> warnBounds (inBoundsI x t) x t loc-          _ -> error "Inferred type of int literal is not a number"-        check e@(FloatLit x ty loc) = e <$ case ty of-          Info (Scalar (Prim (FloatType t))) -> warnBounds (inBoundsF x t) x t loc-          _ -> error "Inferred type of float literal is not a float"-        check e@(Negate (IntLit x ty loc1) loc2) = e <$ case ty of-          Info (Scalar (Prim t)) -> warnBounds (inBoundsI (-x) t) (-x) t (loc1 <> loc2)-          _ -> error "Inferred type of int literal is not a number"-        check e = astMap identityMapper{mapOnExp = check} e-        bitWidth ty = 8 * intByteSize ty :: Int-        inBoundsI x (Signed t) = x >= -2^(bitWidth t - 1) && x < 2^(bitWidth t - 1)-        inBoundsI x (Unsigned t) = x >= 0 && x < 2^bitWidth t-        inBoundsI x (FloatType Float32) = not $ isInfinite (fromIntegral x :: Float)-        inBoundsI x (FloatType Float64) = not $ isInfinite (fromIntegral x :: Double)-        inBoundsI _ Bool = error "Inferred type of int literal is not a number"-        inBoundsF x Float32 = not $ isInfinite (realToFrac x :: Float)-        inBoundsF x Float64 = not $ isInfinite x-        warnBounds inBounds x ty loc = unless inBounds-          $ typeError loc mempty $ "Literal " <> ppr x <>-          " out of bounds for inferred type " <> ppr ty <> "."---- | Type-check a top-level (or module-level) function definition.--- Despite the name, this is also used for checking constant--- definitions, by treating them as 0-ary functions.-checkFunDef :: (Name, Maybe UncheckedTypeExp,-                [UncheckedTypeParam], [UncheckedPattern],-                UncheckedExp, SrcLoc)-            -> TypeM (VName, [TypeParam], [Pattern], Maybe (TypeExp VName),-                      StructType, [VName], Exp)-checkFunDef (fname, maybe_retdecl, tparams, params, body, loc) =-  fmap fst $ runTermTypeM $ do-  (tparams', params', maybe_retdecl', rettype', retext, body') <--    checkBinding (fname, maybe_retdecl, tparams, params, body, loc)--  -- Since this is a top-level function, we also resolve overloaded-  -- types, using either defaults or complaining about ambiguities.-  fixOverloadedTypes--  -- Then replace all inferred types in the body and parameters.-  body'' <- updateTypes body'-  params'' <- updateTypes params'-  maybe_retdecl'' <- traverse updateTypes maybe_retdecl'-  rettype'' <- normTypeFully rettype'--  -- Check if pattern matches are exhaustive and yield-  -- errors if not.-  checkUnmatched body''--  -- Check if the function body can actually be evaluated.-  causalityCheck body''--  literalOverflowCheck body''--  bindSpaced [(Term, fname)] $ do-    fname' <- checkName Term fname loc-    when (nameToString fname `elem` doNotShadow) $-      typeError loc mempty $-      "The" <+> pprName fname <+> "operator may not be redefined."--    return (fname', tparams', params'', maybe_retdecl'', rettype'', retext, body'')---- | This is "fixing" as in "setting them", not "correcting them".  We--- only make very conservative fixing.-fixOverloadedTypes :: TermTypeM ()-fixOverloadedTypes = getConstraints >>= mapM_ fixOverloaded . M.toList . M.map snd-  where fixOverloaded (v, Overloaded ots usage)-          | Signed Int32 `elem` ots = do-              unify usage (Scalar (TypeVar () Nonunique (typeName v) [])) $-                Scalar $ Prim $ Signed Int32-              warn usage "Defaulting ambiguous type to i32."-          | FloatType Float64 `elem` ots = do-              unify usage (Scalar (TypeVar () Nonunique (typeName v) [])) $-                Scalar $ Prim $ FloatType Float64-              warn usage "Defaulting ambiguous type to f64."-          | otherwise =-              typeError usage mempty $-              "Type is ambiguous (could be one of" <+> commasep (map ppr ots) <> ")." </>-              "Add a type annotation to disambiguate the type."--        fixOverloaded (_, NoConstraint _ usage) =-          typeError usage mempty $-          "Type of expression is ambiguous." </>-          "Add a type annotation to disambiguate the type."--        fixOverloaded (_, Equality usage) =-          typeError usage mempty $-          "Type is ambiguous (must be equality type)." </>-          "Add a type annotation to disambiguate the type."--        fixOverloaded (_, HasFields fs usage) =-          typeError usage mempty $-          "Type is ambiguous.  Must be record with fields:" </>-          indent 2 (stack $ map field $ M.toList fs) </>-          "Add a type annotation to disambiguate the type."-          where field (l, t) = ppr l <> colon <+> align (ppr t)--        fixOverloaded (_, HasConstrs cs usage) =-          typeError usage mempty $-          "Type is ambiguous (must be a sum type with constructors:" <+>-          ppr (Sum cs) <> ")." </>-          "Add a type annotation to disambiguate the type."--        fixOverloaded (_, Size Nothing usage) =-          typeError usage mempty "Size is ambiguous."--        fixOverloaded _ = return ()--hiddenParamNames :: [Pattern] -> Names-hiddenParamNames params = hidden-  where param_all_names = mconcat $ map patternNames params-        named (Named x, _) = Just x-        named (Unnamed, _) = Nothing-        param_names =-          S.fromList $ mapMaybe (named . patternParam) params-        hidden = param_all_names `S.difference` param_names--inferredReturnType :: SrcLoc -> [Pattern] -> PatternType -> TermTypeM StructType-inferredReturnType loc params t =-  -- The inferred type may refer to names that are bound by the-  -- parameter patterns, but which will not be visible in the type.-  -- These we must turn into fresh type variables, which will be-  -- existential in the return type.-  fmap (toStruct . fst) $-  unscopeType loc-  (M.filterWithKey (const . (`S.member` hidden)) $ foldMap patternMap params) $-  inferReturnUniqueness params t-  where hidden = hiddenParamNames params--checkBinding :: (Name, Maybe UncheckedTypeExp,-                 [UncheckedTypeParam], [UncheckedPattern],-                 UncheckedExp, SrcLoc)-             -> TermTypeM ([TypeParam], [Pattern], Maybe (TypeExp VName),-                           StructType, [VName], Exp)-checkBinding (fname, maybe_retdecl, tparams, params, body, loc) =-  noUnique $ incLevel $ bindingParams tparams params $ \tparams' params' -> do-    when (null params && any isSizeParam tparams) $-      typeError loc mempty-      "Size parameters are only allowed on bindings that also have value parameters."--    maybe_retdecl' <- forM maybe_retdecl $ \retdecl -> do-      (retdecl', ret_nodims, _) <- checkTypeExp retdecl-      (ret, _) <- instantiateEmptyArrayDims loc "funret" Nonrigid ret_nodims-      return (retdecl', ret)--    body' <- checkFunBody params' body-             (snd <$> maybe_retdecl')-             (maybe loc srclocOf maybe_retdecl)--    params'' <- mapM updateTypes params'-    body_t <- expTypeFully body'--    (maybe_retdecl'', rettype) <- case maybe_retdecl' of-      Just (retdecl', ret) -> do-        let rettype_structural = toStructural ret-        checkReturnAlias rettype_structural params'' body_t--        when (null params) $ nothingMustBeUnique loc rettype_structural--        ret' <- normTypeFully ret--        return (Just retdecl', ret')--      Nothing-        | null params ->-            return (Nothing, toStruct $ body_t `setUniqueness` Nonunique)-        | otherwise -> do-            body_t' <- inferredReturnType loc params'' body_t-            return (Nothing, body_t')--    verifyFunctionParams (Just fname) params''--    (tparams'', params''', rettype'', retext) <--      letGeneralise fname loc tparams' params'' rettype--    checkGlobalAliases params'' body_t loc--    return (tparams'', params''', maybe_retdecl'', rettype'', retext, body')--  where -- | Check that unique return values do not alias a-        -- non-consumed parameter.-        checkReturnAlias rettp params' =-          foldM_ (checkReturnAlias' params') S.empty . returnAliasing rettp-        checkReturnAlias' params' seen (Unique, names)-          | any (`S.member` S.map snd seen) $ S.toList names =-              uniqueReturnAliased fname loc-          | otherwise = do-              notAliasingParam params' names-              return $ seen `S.union` tag Unique names-        checkReturnAlias' _ seen (Nonunique, names)-          | any (`S.member` seen) $ S.toList $ tag Unique names =-            uniqueReturnAliased fname loc-          | otherwise = return $ seen `S.union` tag Nonunique names--        notAliasingParam params' names =-          forM_ params' $ \p ->-          let consumedNonunique p' =-                not (unique $ unInfo $ identType p') && (identName p' `S.member` names)-          in case find consumedNonunique $ S.toList $ patternIdents p of-               Just p' ->-                 returnAliased fname (baseName $ identName p') loc-               Nothing ->-                 return ()--        tag u = S.map (u,)--        returnAliasing (Scalar (Record ets1)) (Scalar (Record ets2)) =-          concat $ M.elems $ M.intersectionWith returnAliasing ets1 ets2-        returnAliasing expected got =-          [(uniqueness expected, S.map aliasVar $ aliases got)]---- | Extract all the shape names that occur in positive position--- (roughly, left side of an arrow) in a given type.-typeDimNamesPos :: TypeBase (DimDecl VName) als -> S.Set VName-typeDimNamesPos (Scalar (Arrow _ _ t1 t2)) = onParam t1 <> typeDimNamesPos t2-  where onParam :: TypeBase (DimDecl VName) als -> S.Set VName-        onParam (Scalar Arrow{}) = mempty-        onParam (Scalar (Record fs)) = mconcat $ map onParam $ M.elems fs-        onParam (Scalar (TypeVar _ _ _ targs)) = mconcat $ map onTypeArg targs-        onParam t = typeDimNames t-        onTypeArg (TypeArgDim (NamedDim d) _) = S.singleton $ qualLeaf d-        onTypeArg (TypeArgDim _ _) = mempty-        onTypeArg (TypeArgType t _) = onParam t-typeDimNamesPos _ = mempty--checkGlobalAliases :: [Pattern] -> PatternType -> SrcLoc -> TermTypeM ()-checkGlobalAliases params body_t loc = do-  vtable <- asks $ scopeVtable . termScope-  let isLocal v = case v `M.lookup` vtable of-                    Just (BoundV Local _ _) -> True-                    _ -> False-  let als = filter (not . isLocal) $ S.toList $-            boundArrayAliases body_t `S.difference`-            foldMap patternNames params-  case als of-    v:_ | not $ null params ->-      typeError loc mempty $-      "Function result aliases the free variable " <>-      pquote (pprName v) <> "." </>-      "Use" <+> pquote "copy" <+> "to break the aliasing."-    _ ->-      return ()--inferReturnUniqueness :: [Pattern] -> PatternType -> PatternType-inferReturnUniqueness params t =-  let forbidden = aliasesMultipleTimes t-      uniques = uniqueParamNames params-      delve (Scalar (Record fs)) =-        Scalar $ Record $ M.map delve fs-      delve t'-        | all (`S.member` uniques) (boundArrayAliases t'),-          not $ any ((`S.member` forbidden) . aliasVar) (aliases t') =-            t'-        | otherwise =-            t' `setUniqueness` Nonunique-  in delve t---- An alias inhibits uniqueness if it is used in disjoint values.-aliasesMultipleTimes :: PatternType -> Names-aliasesMultipleTimes = S.fromList . map fst . filter ((>1) . snd) . M.toList . delve-  where delve (Scalar (Record fs)) =-          foldl' (M.unionWith (+)) mempty $ map delve $ M.elems fs-        delve t =-          M.fromList $ zip (map aliasVar $ S.toList (aliases t)) $ repeat (1::Int)--uniqueParamNames :: [Pattern] -> Names-uniqueParamNames =-  S.map identName-  . S.filter (unique . unInfo . identType)-  . foldMap patternIdents--boundArrayAliases :: PatternType -> S.Set VName-boundArrayAliases (Array als _ _ _) = boundAliases als-boundArrayAliases (Scalar Prim{}) = mempty-boundArrayAliases (Scalar (Record fs)) = foldMap boundArrayAliases fs-boundArrayAliases (Scalar (TypeVar als _ _ _)) = boundAliases als-boundArrayAliases (Scalar Arrow{}) = mempty-boundArrayAliases (Scalar (Sum fs)) =-  mconcat $ concatMap (map boundArrayAliases) $ M.elems fs---- | The set of in-scope variables that are being aliased.-boundAliases :: Aliasing -> S.Set VName-boundAliases = S.map aliasVar . S.filter bound-  where bound AliasBound{} = True-        bound AliasFree{} = False--nothingMustBeUnique :: SrcLoc -> TypeBase () () -> TermTypeM ()-nothingMustBeUnique loc = check-  where check (Array _ Unique _ _) = bad-        check (Scalar (TypeVar _ Unique _ _)) = bad-        check (Scalar (Record fs)) = mapM_ check fs-        check (Scalar (Sum fs)) = mapM_ (mapM_ check) fs-        check _ = return ()-        bad = typeError loc mempty "A top-level constant cannot have a unique type."---- | Verify certain restrictions on function parameters, and bail out--- on dubious constructions.------ These restrictions apply to all functions (anonymous or otherwise).--- Top-level functions have further restrictions that are checked--- during let-generalisation.-verifyFunctionParams :: Maybe Name -> [Pattern] -> TermTypeM ()-verifyFunctionParams fname params =-  onFailure (CheckingParams fname) $-  verifyParams (foldMap patternNames params) =<< mapM updateTypes params-  where-    verifyParams forbidden (p:ps)-      | d:_ <- S.toList $ patternDimNames p `S.intersection` forbidden =-          typeError p mempty $-          "Parameter" <+> pquote (ppr p) <+/>-          "refers to size" <+> pquote (pprName d) <> comma <+/>-          textwrap "which will not be accessible to the caller" <> comma <+/>-          textwrap "possibly because it is nested in a tuple or record." <+/>-          textwrap "Consider ascribing an explicit type that does not reference " <>-          pquote (pprName d) <> "."-      | otherwise = verifyParams forbidden' ps-      where forbidden' =-              case patternParam p of-                (Named v, _) -> forbidden `S.difference` S.singleton v-                _            -> forbidden--    verifyParams _ [] = return ()---- Returns the sizes of the immediate type produced,--- the sizes of parameter types, and the sizes of return types.-dimUses :: StructType -> (Names, Names, Names)-dimUses = execWriter . traverseDims f-  where f _ PosImmediate (NamedDim v) = tell (S.singleton (qualLeaf v), mempty, mempty)-        f _ PosParam (NamedDim v) = tell (mempty, S.singleton (qualLeaf v), mempty)-        f _ PosReturn (NamedDim v) = tell (mempty, mempty, S.singleton (qualLeaf v))-        f _ _ _ = return ()---- | Find at all type variables in the given type that are covered by--- the constraints, and produce type parameters that close over them.------ The passed-in list of type parameters is always prepended to the--- produced list of type parameters.-closeOverTypes :: Name -> SrcLoc-               -> [TypeParam] -> [StructType] -> StructType-               -> Constraints -> TermTypeM ([TypeParam], StructType, [VName])-closeOverTypes defname defloc tparams paramts ret substs = do-  (more_tparams, retext) <- partitionEithers . catMaybes <$>-                            mapM closeOver (M.toList $ M.map snd to_close_over)-  let retToAnyDim v = do guard $ v `S.member` ret_sizes-                         UnknowableSize{} <- snd <$> M.lookup v substs-                         Just $ SizeSubst AnyDim-  return (tparams ++ more_tparams,-          applySubst retToAnyDim ret,-          retext)-  where t = foldFunType paramts ret-        to_close_over = M.filterWithKey (\k _ -> k `S.member` visible) substs-        visible = typeVars t <> typeDimNames t--        (produced_sizes, param_sizes, ret_sizes) = dimUses t--        -- Avoid duplicate type parameters.-        closeOver (k, _)-          | k `elem` map typeParamName tparams =-              return Nothing-        closeOver (k, NoConstraint l usage) =-          return $ Just $ Left $ TypeParamType l k $ srclocOf usage-        closeOver (k, ParamType l loc) =-          return $ Just $ Left $ TypeParamType l k loc-        closeOver (k, Size Nothing usage) =-          return $ Just $ Left $ TypeParamDim k $ srclocOf usage-        closeOver (k, UnknowableSize _ _)-          | k `S.member` param_sizes = do-              notes <- dimNotes defloc $ NamedDim $ qualName k-              typeError defloc notes $-                "Unknowable size" <+> pquote (pprName k) <+>-                "imposes constraint on type of" <+>-                pquote (pprName defname) <>-                ", which is inferred as:" </>-                indent 2 (ppr t)-          | k `S.member` produced_sizes =-              return $ Just $ Right k-        closeOver (_, _) =-          return Nothing--letGeneralise :: Name -> SrcLoc-              -> [TypeParam] -> [Pattern] -> StructType-              -> TermTypeM ([TypeParam], [Pattern], StructType, [VName])-letGeneralise defname defloc tparams params rettype =-  onFailure (CheckingLetGeneralise defname) $ do-  now_substs <- getConstraints--  -- Candidates for let-generalisation are those type variables that-  ---  -- (1) were not known before we checked this function, and-  ---  -- (2) are not used in the (new) definition of any type variables-  -- known before we checked this function.-  ---  -- (3) are not referenced from an overloaded type (for example,-  -- are the element types of an incompletely resolved record type).-  -- This is a bit more restrictive than I'd like, and SML for-  -- example does not have this restriction.-  ---  -- Criteria (1) and (2) is implemented by looking at the binding-  -- level of the type variables.-  let keep_type_vars = overloadedTypeVars now_substs--  cur_lvl <- curLevel-  let candidate k (lvl, _) = (k `S.notMember` keep_type_vars) && lvl >= cur_lvl-      new_substs = M.filterWithKey candidate now_substs--  (tparams', rettype', retext) <--    closeOverTypes defname defloc tparams-    (map patternStructType params) rettype new_substs--  rettype'' <- updateTypes rettype'--  let used_sizes = foldMap typeDimNames $-                   rettype'' : map patternStructType params-  case filter ((`S.notMember` used_sizes) . typeParamName) $-       filter isSizeParam tparams' of-    [] -> return ()-    tp:_ -> typeError defloc mempty $-            "Size parameter" <+> pquote (ppr tp) <+> "unused."--  -- We keep those type variables that were not closed over by-  -- let-generalisation.-  modifyConstraints $ M.filterWithKey $ \k _ -> k `notElem` map typeParamName tparams'--  return (tparams', params, rettype'', retext)--checkFunBody :: [Pattern]-             -> UncheckedExp-             -> Maybe StructType-             -> SrcLoc-             -> TermTypeM Exp-checkFunBody params body maybe_rettype loc = do-  body' <- noSizeEscape $ checkExp body--  -- Unify body return type with return annotation, if one exists.-  case maybe_rettype of-    Just rettype -> do-      (rettype_withdims, _) <- instantiateEmptyArrayDims loc "impl" Nonrigid rettype--      body_t <- expTypeFully body'-      -- We need to turn any sizes provided by "hidden" parameter-      -- names into existential sizes instead.-      let hidden = hiddenParamNames params-      (body_t', _) <- unscopeType loc-                      (M.filterWithKey (const . (`S.member` hidden)) $-                       foldMap patternMap params)-                      body_t--      let usage = mkUsage (srclocOf body) "return type annotation"-      onFailure (CheckingReturn rettype (toStruct body_t')) $-        expect usage rettype_withdims $ toStruct body_t'--      -- We also have to make sure that uniqueness matches.  This is done-      -- explicitly, because uniqueness is ignored by unification.-      rettype' <- normTypeFully rettype-      body_t'' <- normTypeFully rettype -- Substs may have changed.-      unless (body_t'' `subtypeOf` anySizes rettype') $-        typeError (srclocOf body) mempty $-        "Body type" </> indent 2 (ppr body_t'') </>-        "is not a subtype of annotated type" </>-        indent 2 (ppr rettype')--    Nothing -> return ()--  return body'----- Consumption--occur :: Occurences -> TermTypeM ()-occur = tell---- | Proclaim that we have made read-only use of the given variable.-observe :: Ident -> TermTypeM ()-observe (Ident nm (Info t) loc) =-  let als = AliasBound nm `S.insert` aliases t-  in occur [observation als loc]--checkIfConsumable :: SrcLoc -> Aliasing -> TermTypeM ()-checkIfConsumable loc als = do-  vtable <- asks $ scopeVtable . termScope-  let consumable v = case M.lookup v vtable of-                       Just (BoundV Local _ t)-                         | arrayRank t > 0 -> unique t-                         | Scalar TypeVar{} <- t -> unique t-                         | otherwise -> True-                       _ -> False-  case filter (not . consumable) $ map aliasVar $ S.toList als of-    v:_ -> typeError loc mempty $-           "Would consume variable" <+> pquote (pprName v)-           <> ", which is not allowed."-    [] -> return ()---- | Proclaim that we have written to the given variable.-consume :: SrcLoc -> Aliasing -> TermTypeM ()-consume loc als = do-  checkIfConsumable loc als-  occur [consumption als loc]---- | Proclaim that we have written to the given variable, and mark--- accesses to it and all of its aliases as invalid inside the given--- computation.-consuming :: Ident -> TermTypeM a -> TermTypeM a-consuming (Ident name (Info t) loc) m = do-  consume loc $ AliasBound name `S.insert` aliases t-  localScope consume' m-  where consume' scope =-          scope { scopeVtable = M.insert name (WasConsumed loc) $ scopeVtable scope }--collectOccurences :: TermTypeM a -> TermTypeM (a, Occurences)-collectOccurences m = pass $ do-  (x, dataflow) <- listen m-  return ((x, dataflow), const mempty)--tapOccurences :: TermTypeM a -> TermTypeM (a, Occurences)-tapOccurences = listen--removeSeminullOccurences :: TermTypeM a -> TermTypeM a-removeSeminullOccurences = censor $ filter $ not . seminullOccurence--checkIfUsed :: Occurences -> Ident -> TermTypeM ()-checkIfUsed occs v-  | not $ identName v `S.member` allOccuring occs,-    not $ "_" `isPrefixOf` prettyName (identName v) =-      warn (srclocOf v) $ "Unused variable " ++ quote (pretty $ baseName $ identName v) ++ "."-  | otherwise =-      return ()--alternative :: TermTypeM a -> TermTypeM b -> TermTypeM (a,b)-alternative m1 m2 = pass $ do-  (x, occurs1) <- listen $ noSizeEscape m1-  (y, occurs2) <- listen $ noSizeEscape m2-  checkOccurences occurs1-  checkOccurences occurs2-  let usage = occurs1 `altOccurences` occurs2-  return ((x, y), const usage)---- | Make all bindings nonunique.-noUnique :: TermTypeM a -> TermTypeM a-noUnique = localScope (\scope -> scope { scopeVtable = M.map set $ scopeVtable scope})-  where set (BoundV l tparams t)    = BoundV l tparams $ t `setUniqueness` Nonunique-        set (OverloadedF ts pts rt) = OverloadedF ts pts rt-        set EqualityF               = EqualityF-        set (WasConsumed loc)       = WasConsumed loc--onlySelfAliasing :: TermTypeM a -> TermTypeM a-onlySelfAliasing = localScope (\scope -> scope { scopeVtable = M.mapWithKey set $ scopeVtable scope})-  where set k (BoundV l tparams t)    = BoundV l tparams $-                                        t `addAliases` S.intersection (S.singleton (AliasBound k))-        set _ (OverloadedF ts pts rt) = OverloadedF ts pts rt-        set _ EqualityF               = EqualityF-        set _ (WasConsumed loc)       = WasConsumed loc--arrayOfM :: (Pretty (ShapeDecl dim), Monoid as) =>-            SrcLoc-         -> TypeBase dim as -> ShapeDecl dim -> Uniqueness-         -> TermTypeM (TypeBase dim as)-arrayOfM loc t shape u = do-  zeroOrderType (mkUsage loc "use as array element") "type used in array" t-  return $ arrayOf t shape u--updateTypes :: ASTMappable e => e -> TermTypeM e-updateTypes = astMap tv-  where tv = ASTMapper { mapOnExp         = astMap tv-                       , mapOnName        = pure-                       , mapOnQualName    = pure-                       , mapOnStructType  = normTypeFully-                       , mapOnPatternType = normTypeFully-                       }+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TupleSections #-}++-- | Facilities for type-checking Futhark terms.  Checking a term+-- requires a little more context to track uniqueness and such.+--+-- Type inference is implemented through a variation of+-- Hindley-Milner.  The main complication is supporting the rich+-- number of built-in language constructs, as well as uniqueness+-- types.  This is mostly done in an ad hoc way, and many programs+-- will require the programmer to fall back on type annotations.+module Language.Futhark.TypeChecker.Terms+  ( checkOneExp,+    checkFunDef,+  )+where++import Control.Monad.Except+import Control.Monad.RWS hiding (Sum)+import Control.Monad.State+import Control.Monad.Writer hiding (Sum)+import Data.Bifunctor+import Data.Char (isAscii)+import Data.Either+import Data.List (find, foldl', group, isPrefixOf, nub, sort, transpose, (\\))+import qualified Data.List.NonEmpty as NE+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Data.Set as S+import Futhark.IR.Primitive (intByteSize)+import Futhark.Util.Pretty hiding (bool, group, space)+import Language.Futhark hiding (unscopeType)+import Language.Futhark.Semantic (includeToString)+import Language.Futhark.Traversals+import Language.Futhark.TypeChecker.Monad hiding (BoundV)+import qualified Language.Futhark.TypeChecker.Monad as TypeM+import Language.Futhark.TypeChecker.Types hiding (checkTypeDecl)+import qualified Language.Futhark.TypeChecker.Types as Types+import Language.Futhark.TypeChecker.Unify hiding (Usage)+import Prelude hiding (mod)++--- Uniqueness++data Usage+  = Consumed SrcLoc+  | Observed SrcLoc+  deriving (Eq, Ord, Show)++type Names = S.Set VName++-- | The consumption set is a Maybe so we can distinguish whether a+-- consumption took place, but the variable went out of scope since,+-- or no consumption at all took place.+data Occurence = Occurence+  { observed :: Names,+    consumed :: Maybe Names,+    location :: SrcLoc+  }+  deriving (Eq, Show)++instance Located Occurence where+  locOf = locOf . location++observation :: Aliasing -> SrcLoc -> Occurence+observation = flip Occurence Nothing . S.map aliasVar++consumption :: Aliasing -> SrcLoc -> Occurence+consumption = Occurence S.empty . Just . S.map aliasVar++-- | A null occurence is one that we can remove without affecting+-- anything.+nullOccurence :: Occurence -> Bool+nullOccurence occ = S.null (observed occ) && isNothing (consumed occ)++-- | A seminull occurence is one that does not contain references to+-- any variables in scope.  The big difference is that a seminull+-- occurence may denote a consumption, as long as the array that was+-- consumed is now out of scope.+seminullOccurence :: Occurence -> Bool+seminullOccurence occ = S.null (observed occ) && maybe True S.null (consumed occ)++type Occurences = [Occurence]++type UsageMap = M.Map VName [Usage]++usageMap :: Occurences -> UsageMap+usageMap = foldl comb M.empty+  where+    comb m (Occurence obs cons loc) =+      let m' = S.foldl' (ins $ Observed loc) m obs+       in S.foldl' (ins $ Consumed loc) m' $ fromMaybe mempty cons+    ins v m k = M.insertWith (++) k [v] m++combineOccurences :: VName -> Usage -> Usage -> TermTypeM Usage+combineOccurences _ (Observed loc) (Observed _) = return $ Observed loc+combineOccurences name (Consumed wloc) (Observed rloc) =+  useAfterConsume (baseName name) rloc wloc+combineOccurences name (Observed rloc) (Consumed wloc) =+  useAfterConsume (baseName name) rloc wloc+combineOccurences name (Consumed loc1) (Consumed loc2) =+  consumeAfterConsume (baseName name) (max loc1 loc2) (min loc1 loc2)++checkOccurences :: Occurences -> TermTypeM ()+checkOccurences = void . M.traverseWithKey comb . usageMap+  where+    comb _ [] = return ()+    comb name (u : us) = foldM_ (combineOccurences name) u us++allObserved :: Occurences -> Names+allObserved = S.unions . map observed++allConsumed :: Occurences -> Names+allConsumed = S.unions . map (fromMaybe mempty . consumed)++allOccuring :: Occurences -> Names+allOccuring occs = allConsumed occs <> allObserved occs++anyConsumption :: Occurences -> Maybe Occurence+anyConsumption = find (isJust . consumed)++seqOccurences :: Occurences -> Occurences -> Occurences+seqOccurences occurs1 occurs2 =+  filter (not . nullOccurence) $ map filt occurs1 ++ occurs2+  where+    filt occ =+      occ {observed = observed occ `S.difference` postcons}+    postcons = allConsumed occurs2++altOccurences :: Occurences -> Occurences -> Occurences+altOccurences occurs1 occurs2 =+  filter (not . nullOccurence) $ map filt1 occurs1 ++ map filt2 occurs2+  where+    filt1 occ =+      occ+        { consumed = S.difference <$> consumed occ <*> pure cons2,+          observed = observed occ `S.difference` cons2+        }+    filt2 occ =+      occ+        { consumed = consumed occ,+          observed = observed occ `S.difference` cons1+        }+    cons1 = allConsumed occurs1+    cons2 = allConsumed occurs2++--- Scope management++data Checking+  = CheckingApply (Maybe (QualName VName)) Exp StructType StructType+  | CheckingReturn StructType StructType+  | CheckingAscription StructType StructType+  | CheckingLetGeneralise Name+  | CheckingParams (Maybe Name)+  | CheckingPattern UncheckedPattern InferredType+  | CheckingLoopBody StructType StructType+  | CheckingLoopInitial StructType StructType+  | CheckingRecordUpdate [Name] StructType StructType+  | CheckingRequired [StructType] StructType+  | CheckingBranches StructType StructType++instance Pretty Checking where+  ppr (CheckingApply f e expected actual) =+    header+      </> "Expected:" <+> align (ppr expected)+      </> "Actual:  " <+> align (ppr actual)+    where+      header =+        case f of+          Nothing ->+            "Cannot apply function to"+              <+> pquote (shorten $ pretty $ flatten $ ppr e) <> " (invalid type)."+          Just fname ->+            "Cannot apply" <+> pquote (ppr fname) <+> "to"+              <+> pquote (shorten $ pretty $ flatten $ ppr e) <> " (invalid type)."+  ppr (CheckingReturn expected actual) =+    "Function body does not have expected type."+      </> "Expected:" <+> align (ppr expected)+      </> "Actual:  " <+> align (ppr actual)+  ppr (CheckingAscription expected actual) =+    "Expression does not have expected type from explicit ascription."+      </> "Expected:" <+> align (ppr expected)+      </> "Actual:  " <+> align (ppr actual)+  ppr (CheckingLetGeneralise fname) =+    "Cannot generalise type of" <+> pquote (ppr fname) <> "."+  ppr (CheckingParams fname) =+    "Invalid use of parameters in" <+> pquote fname' <> "."+    where+      fname' = maybe "anonymous function" ppr fname+  ppr (CheckingPattern pat NoneInferred) =+    "Invalid pattern" <+> pquote (ppr pat) <> "."+  ppr (CheckingPattern pat (Ascribed t)) =+    "Pattern" <+> pquote (ppr pat)+      <+> "cannot match value of type"+      </> indent 2 (ppr t)+  ppr (CheckingLoopBody expected actual) =+    "Loop body does not have expected type."+      </> "Expected:" <+> align (ppr expected)+      </> "Actual:  " <+> align (ppr actual)+  ppr (CheckingLoopInitial expected actual) =+    "Initial loop values do not have expected type."+      </> "Expected:" <+> align (ppr expected)+      </> "Actual:  " <+> align (ppr actual)+  ppr (CheckingRecordUpdate fs expected actual) =+    "Type mismatch when updating record field" <+> pquote fs' <> "."+      </> "Existing:" <+> align (ppr expected)+      </> "New:     " <+> align (ppr actual)+    where+      fs' = mconcat $ punctuate "." $ map ppr fs+  ppr (CheckingRequired [expected] actual) =+    "Expression must must have type" <+> ppr expected <> "."+      </> "Actual type:" <+> align (ppr actual)+  ppr (CheckingRequired expected actual) =+    "Type of expression must must be one of " <+> expected' <> "."+      </> "Actual type:" <+> align (ppr actual)+    where+      expected' = commasep (map ppr expected)+  ppr (CheckingBranches t1 t2) =+    "Conditional branches differ in type."+      </> "Former:" <+> ppr t1+      </> "Latter:" <+> ppr t2++-- | Whether something is a global or a local variable.+data Locality = Local | Global+  deriving (Show)++data ValBinding+  = -- | Aliases in parameters indicate the lexical+    -- closure.+    BoundV Locality [TypeParam] PatternType+  | OverloadedF [PrimType] [Maybe PrimType] (Maybe PrimType)+  | EqualityF+  | WasConsumed SrcLoc+  deriving (Show)++-- | Type checking happens with access to this environment.  The+-- 'TermScope' will be extended during type-checking as bindings come into+-- scope.+data TermEnv = TermEnv+  { termScope :: TermScope,+    termChecking :: Maybe Checking,+    termLevel :: Level+  }++data TermScope = TermScope+  { scopeVtable :: M.Map VName ValBinding,+    scopeTypeTable :: M.Map VName TypeBinding,+    scopeModTable :: M.Map VName Mod,+    scopeNameMap :: NameMap+  }+  deriving (Show)++instance Semigroup TermScope where+  TermScope vt1 tt1 mt1 nt1 <> TermScope vt2 tt2 mt2 nt2 =+    TermScope (vt2 `M.union` vt1) (tt2 `M.union` tt1) (mt1 `M.union` mt2) (nt2 `M.union` nt1)++envToTermScope :: Env -> TermScope+envToTermScope env =+  TermScope+    { scopeVtable = vtable,+      scopeTypeTable = envTypeTable env,+      scopeNameMap = envNameMap env,+      scopeModTable = envModTable env+    }+  where+    vtable = M.mapWithKey valBinding $ envVtable env+    valBinding k (TypeM.BoundV tps v) =+      BoundV Global tps $+        v+          `setAliases` (if arrayRank v > 0 then S.singleton (AliasBound k) else mempty)++withEnv :: TermEnv -> Env -> TermEnv+withEnv tenv env = tenv {termScope = termScope tenv <> envToTermScope env}++overloadedTypeVars :: Constraints -> Names+overloadedTypeVars = mconcat . map f . M.elems+  where+    f (_, HasFields fs _) = mconcat $ map typeVars $ M.elems fs+    f _ = mempty++-- | Get the type of an expression, with top level type variables+-- substituted.  Never call 'typeOf' directly (except in a few+-- carefully inspected locations)!+expType :: Exp -> TermTypeM PatternType+expType = normPatternType . typeOf++-- | Get the type of an expression, with all type variables+-- substituted.  Slower than 'expType', but sometimes necessary.+-- Never call 'typeOf' directly (except in a few carefully inspected+-- locations)!+expTypeFully :: Exp -> TermTypeM PatternType+expTypeFully = normTypeFully . typeOf++-- Wrap a function name to give it a vacuous Eq instance for SizeSource.+newtype FName = FName (Maybe (QualName VName))+  deriving (Show)++instance Eq FName where+  _ == _ = True++instance Ord FName where+  compare _ _ = EQ++-- | What was the source of some existential size?  This is used for+-- using the same existential variable if the same source is+-- encountered in multiple locations.+data SizeSource+  = SourceArg FName (ExpBase NoInfo VName)+  | SourceBound (ExpBase NoInfo VName)+  | SourceSlice+      (Maybe (DimDecl VName))+      (Maybe (ExpBase NoInfo VName))+      (Maybe (ExpBase NoInfo VName))+      (Maybe (ExpBase NoInfo VName))+  deriving (Eq, Ord, Show)++-- | The state is a set of constraints and a counter for generating+-- type names.  This is distinct from the usual counter we use for+-- generating unique names, as these will be user-visible.+data TermTypeState = TermTypeState+  { stateConstraints :: Constraints,+    stateCounter :: !Int,+    -- | Mapping function arguments encountered to+    -- the sizes they ended up generating (when+    -- they could not be substituted directly).+    -- This happens for function arguments that are+    -- not constants or names.+    stateDimTable :: M.Map SizeSource VName+  }++newtype TermTypeM a+  = TermTypeM+      ( RWST+          TermEnv+          Occurences+          TermTypeState+          TypeM+          a+      )+  deriving+    ( Monad,+      Functor,+      Applicative,+      MonadReader TermEnv,+      MonadWriter Occurences,+      MonadState TermTypeState,+      MonadError TypeError+    )++instance MonadUnify TermTypeM where+  getConstraints = gets stateConstraints+  putConstraints x = modify $ \s -> s {stateConstraints = x}++  newTypeVar loc desc = do+    i <- incCounter+    v <- newID $ mkTypeVarName desc i+    constrain v $ NoConstraint Lifted $ mkUsage' loc+    return $ Scalar $ TypeVar mempty Nonunique (typeName v) []++  curLevel = asks termLevel++  newDimVar loc rigidity name = do+    i <- incCounter+    dim <- newID $ mkTypeVarName name i+    case rigidity of+      Rigid rsrc -> constrain dim $ UnknowableSize loc rsrc+      Nonrigid -> constrain dim $ Size Nothing $ mkUsage' loc+    return dim++  unifyError loc notes bcs doc = do+    checking <- asks termChecking+    case checking of+      Just checking' ->+        throwError $+          TypeError (srclocOf loc) notes $+            ppr checking' <> line </> doc <> ppr bcs+      Nothing ->+        throwError $ TypeError (srclocOf loc) notes $ doc <> ppr bcs++  matchError loc notes bcs t1 t2 = do+    checking <- asks termChecking+    case checking of+      Just checking'+        | hasNoBreadCrumbs bcs ->+          throwError $+            TypeError (srclocOf loc) notes $+              ppr checking'+        | otherwise ->+          throwError $+            TypeError (srclocOf loc) notes $+              ppr checking' <> line </> doc <> ppr bcs+      Nothing ->+        throwError $ TypeError (srclocOf loc) notes $ doc <> ppr bcs+    where+      doc =+        "Types"+          </> indent 2 (ppr t1)+          </> "and"+          </> indent 2 (ppr t2)+          </> "do not match."++onFailure :: Checking -> TermTypeM a -> TermTypeM a+onFailure c = local $ \env -> env {termChecking = Just c}++runTermTypeM :: TermTypeM a -> TypeM (a, Occurences)+runTermTypeM (TermTypeM m) = do+  initial_scope <- (initialTermScope <>) . envToTermScope <$> askEnv+  let initial_tenv =+        TermEnv+          { termScope = initial_scope,+            termChecking = Nothing,+            termLevel = 0+          }+  evalRWST m initial_tenv $ TermTypeState mempty 0 mempty++liftTypeM :: TypeM a -> TermTypeM a+liftTypeM = TermTypeM . lift++localScope :: (TermScope -> TermScope) -> TermTypeM a -> TermTypeM a+localScope f = local $ \tenv -> tenv {termScope = f $ termScope tenv}++incCounter :: TermTypeM Int+incCounter = do+  s <- get+  put s {stateCounter = stateCounter s + 1}+  return $ stateCounter s++extSize :: SrcLoc -> SizeSource -> TermTypeM (DimDecl VName, Maybe VName)+extSize loc e = do+  prev <- gets $ M.lookup e . stateDimTable+  case prev of+    Nothing -> do+      let rsrc = case e of+            SourceArg (FName fname) e' ->+              RigidArg fname $ prettyOneLine e'+            SourceBound e' ->+              RigidBound $ prettyOneLine e'+            SourceSlice d i j s ->+              RigidSlice d $ prettyOneLine $ DimSlice i j s+      d <- newDimVar loc (Rigid rsrc) "argdim"+      modify $ \s -> s {stateDimTable = M.insert e d $ stateDimTable s}+      return+        ( NamedDim $ qualName d,+          Just d+        )+    Just d ->+      return+        ( NamedDim $ qualName d,+          Nothing+        )++-- Any argument sizes created with 'extSize' inside the given action+-- will be removed once the action finishes.  This is to ensure that+-- just because e.g. @n+1@ appears as a size in one branch of a+-- conditional, that doesn't mean it's also available in the other branch.+noSizeEscape :: TermTypeM a -> TermTypeM a+noSizeEscape m = do+  dimtable <- gets stateDimTable+  x <- m+  modify $ \s -> s {stateDimTable = dimtable}+  return x++constrain :: VName -> Constraint -> TermTypeM ()+constrain v c = do+  lvl <- curLevel+  modifyConstraints $ M.insert v (lvl, c)++incLevel :: TermTypeM a -> TermTypeM a+incLevel = local $ \env -> env {termLevel = termLevel env + 1}++initialTermScope :: TermScope+initialTermScope =+  TermScope+    { scopeVtable = initialVtable,+      scopeTypeTable = mempty,+      scopeNameMap = topLevelNameMap,+      scopeModTable = mempty+    }+  where+    initialVtable = M.fromList $ mapMaybe addIntrinsicF $ M.toList intrinsics++    prim = Scalar . Prim+    arrow x y = Scalar $ Arrow mempty Unnamed x y++    addIntrinsicF (name, IntrinsicMonoFun pts t) =+      Just (name, BoundV Global [] $ arrow pts' $ prim t)+      where+        pts' = case pts of+          [pt] -> prim pt+          _ -> tupleRecord $ map prim pts+    addIntrinsicF (name, IntrinsicOverloadedFun ts pts rts) =+      Just (name, OverloadedF ts pts rts)+    addIntrinsicF (name, IntrinsicPolyFun tvs pts rt) =+      Just+        ( name,+          BoundV Global tvs $+            fromStruct $ Scalar $ Arrow mempty Unnamed pts' rt+        )+      where+        pts' = case pts of+          [pt] -> pt+          _ -> tupleRecord pts+    addIntrinsicF (name, IntrinsicEquality) =+      Just (name, EqualityF)+    addIntrinsicF _ = Nothing++instance MonadTypeChecker TermTypeM where+  warn loc problem = liftTypeM $ warn loc problem+  newName = liftTypeM . newName+  newID = liftTypeM . newID++  checkQualName space name loc = snd <$> checkQualNameWithEnv space name loc++  bindNameMap m = localScope $ \scope ->+    scope {scopeNameMap = m <> scopeNameMap scope}++  bindVal v (TypeM.BoundV tps t) = localScope $ \scope ->+    scope {scopeVtable = M.insert v vb $ scopeVtable scope}+    where+      vb = BoundV Local tps $ fromStruct t++  lookupType loc qn = do+    outer_env <- liftTypeM askEnv+    (scope, qn'@(QualName qs name)) <- checkQualNameWithEnv Type qn loc+    case M.lookup name $ scopeTypeTable scope of+      Nothing -> unknownType loc qn+      Just (TypeAbbr l ps def) ->+        return (qn', ps, qualifyTypeVars outer_env (map typeParamName ps) qs def, l)++  lookupMod loc qn = do+    (scope, qn'@(QualName _ name)) <- checkQualNameWithEnv Term qn loc+    case M.lookup name $ scopeModTable scope of+      Nothing -> unknownVariable Term qn loc+      Just m -> return (qn', m)++  lookupVar loc qn = do+    outer_env <- liftTypeM askEnv+    (scope, qn'@(QualName qs name)) <- checkQualNameWithEnv Term qn loc+    let usage = mkUsage loc $ "use of " ++ quote (pretty qn)++    t <- case M.lookup name $ scopeVtable scope of+      Nothing ->+        typeError loc mempty $+          "Unknown variable" <+> pquote (ppr qn) <> "."+      Just (WasConsumed wloc) -> useAfterConsume (baseName name) loc wloc+      Just (BoundV _ tparams t)+        | "_" `isPrefixOf` baseString name -> underscoreUse loc qn+        | otherwise -> do+          (tnames, t') <- instantiateTypeScheme loc tparams t+          return $ qualifyTypeVars outer_env tnames qs t'+      Just EqualityF -> do+        argtype <- newTypeVar loc "t"+        equalityType usage argtype+        return $+          Scalar $+            Arrow mempty Unnamed argtype $+              Scalar $ Arrow mempty Unnamed argtype $ Scalar $ Prim Bool+      Just (OverloadedF ts pts rt) -> do+        argtype <- newTypeVar loc "t"+        mustBeOneOf ts usage argtype+        let (pts', rt') = instOverloaded argtype pts rt+            arrow xt yt = Scalar $ Arrow mempty Unnamed xt yt+        return $ fromStruct $ foldr arrow rt' pts'++    observe $ Ident name (Info t) loc+    return (qn', t)+    where+      instOverloaded argtype pts rt =+        ( map (maybe (toStruct argtype) (Scalar . Prim)) pts,+          maybe (toStruct argtype) (Scalar . Prim) rt+        )++  checkNamedDim loc v = do+    (v', t) <- lookupVar loc v+    onFailure (CheckingRequired [Scalar $ Prim $ Signed Int64] (toStruct t)) $+      unify (mkUsage loc "use as array size") (toStruct t) $+        Scalar $ Prim $ Signed Int64+    return v'++  typeError loc notes s = do+    checking <- asks termChecking+    case checking of+      Just checking' ->+        throwError $ TypeError (srclocOf loc) notes (ppr checking' <> line </> s)+      Nothing ->+        throwError $ TypeError (srclocOf loc) notes s++checkQualNameWithEnv :: Namespace -> QualName Name -> SrcLoc -> TermTypeM (TermScope, QualName VName)+checkQualNameWithEnv space qn@(QualName quals name) loc = do+  scope <- asks termScope+  descend scope quals+  where+    descend scope []+      | Just name' <- M.lookup (space, name) $ scopeNameMap scope =+        return (scope, name')+      | otherwise =+        unknownVariable space qn loc+    descend scope (q : qs)+      | Just (QualName _ q') <- M.lookup (Term, q) $ scopeNameMap scope,+        Just res <- M.lookup q' $ scopeModTable scope =+        case res of+          -- Check if we are referring to the magical intrinsics+          -- module.+          _+            | baseTag q' <= maxIntrinsicTag ->+              checkIntrinsic space qn loc+          ModEnv q_scope -> do+            (scope', QualName qs' name') <- descend (envToTermScope q_scope) qs+            return (scope', QualName (q' : qs') name')+          ModFun {} -> unappliedFunctor loc+      | otherwise =+        unknownVariable space qn loc++checkIntrinsic :: Namespace -> QualName Name -> SrcLoc -> TermTypeM (TermScope, QualName VName)+checkIntrinsic space qn@(QualName _ name) loc+  | Just v <- M.lookup (space, name) intrinsicsNameMap = do+    me <- liftTypeM askImportName+    unless ("/prelude" `isPrefixOf` includeToString me) $+      warn loc "Using intrinsic functions directly can easily crash the compiler or result in wrong code generation."+    scope <- asks termScope+    return (scope, v)+  | otherwise =+    unknownVariable space qn loc++-- | Wrap 'Types.checkTypeDecl' to also perform an observation of+-- every size in the type.+checkTypeDecl :: TypeDeclBase NoInfo Name -> TermTypeM (TypeDeclBase Info VName)+checkTypeDecl tdecl = do+  (tdecl', _) <- Types.checkTypeDecl tdecl+  mapM_ observeDim $ nestedDims $ unInfo $ expandedType tdecl'+  return tdecl'+  where+    observeDim (NamedDim v) =+      observe $ Ident (qualLeaf v) (Info $ Scalar $ Prim $ Signed Int64) mempty+    observeDim _ = return ()++-- | Instantiate a type scheme with fresh type variables for its type+-- parameters. Returns the names of the fresh type variables, the+-- instance list, and the instantiated type.+instantiateTypeScheme ::+  SrcLoc ->+  [TypeParam] ->+  PatternType ->+  TermTypeM ([VName], PatternType)+instantiateTypeScheme loc tparams t = do+  let tnames = map typeParamName tparams+  (tparam_names, tparam_substs) <- unzip <$> mapM (instantiateTypeParam loc) tparams+  let substs = M.fromList $ zip tnames tparam_substs+      t' = applySubst (`M.lookup` substs) t+  return (tparam_names, t')++-- | Create a new type name and insert it (unconstrained) in the+-- substitution map.+instantiateTypeParam :: Monoid as => SrcLoc -> TypeParam -> TermTypeM (VName, Subst (TypeBase dim as))+instantiateTypeParam loc tparam = do+  i <- incCounter+  v <- newID $ mkTypeVarName (takeWhile isAscii (baseString (typeParamName tparam))) i+  case tparam of+    TypeParamType x _ _ -> do+      constrain v $ NoConstraint x $ mkUsage' loc+      return (v, Subst $ Scalar $ TypeVar mempty Nonunique (typeName v) [])+    TypeParamDim {} -> do+      constrain v $ Size Nothing $ mkUsage' loc+      return (v, SizeSubst $ NamedDim $ qualName v)++newArrayType :: SrcLoc -> String -> Int -> TermTypeM (StructType, StructType)+newArrayType loc desc r = do+  v <- newID $ nameFromString desc+  constrain v $ NoConstraint Unlifted $ mkUsage' loc+  dims <- replicateM r $ newDimVar loc Nonrigid "dim"+  let rowt = TypeVar () Nonunique (typeName v) []+  return+    ( Array () Nonunique rowt (ShapeDecl $ map (NamedDim . qualName) dims),+      Scalar rowt+    )++--- Errors++useAfterConsume :: Name -> SrcLoc -> SrcLoc -> TermTypeM a+useAfterConsume name rloc wloc =+  typeError rloc mempty $+    "Variable" <+> pquote (pprName name) <+> "previously consumed at"+      <+> text (locStrRel rloc wloc) <> ".  (Possibly through aliasing.)"++consumeAfterConsume :: Name -> SrcLoc -> SrcLoc -> TermTypeM a+consumeAfterConsume name loc1 loc2 =+  typeError loc2 mempty $+    "Variable" <+> pprName name <+> "previously consumed at"+      <+> text (locStrRel loc2 loc1) <> "."++badLetWithValue :: SrcLoc -> TermTypeM a+badLetWithValue loc =+  typeError+    loc+    mempty+    "New value for elements in let-with shares data with source array.  This is illegal, as it prevents in-place modification."++returnAliased :: Name -> Name -> SrcLoc -> TermTypeM ()+returnAliased fname name loc =+  typeError loc mempty $+    "Unique return value of" <+> pquote (pprName fname)+      <+> "is aliased to"+      <+> pquote (pprName name) <> ", which is not consumed."++uniqueReturnAliased :: Name -> SrcLoc -> TermTypeM a+uniqueReturnAliased fname loc =+  typeError loc mempty $+    "A unique tuple element of return value of"+      <+> pquote (pprName fname)+      <+> "is aliased to some other tuple component."++unexpectedType :: MonadTypeChecker m => SrcLoc -> StructType -> [StructType] -> m a+unexpectedType loc _ [] =+  typeError loc mempty $+    "Type of expression at" <+> text (locStr loc)+      <+> "cannot have any type - possibly a bug in the type checker."+unexpectedType loc t ts =+  typeError loc mempty $+    "Type of expression at" <+> text (locStr loc) <+> "must be one of"+      <+> commasep (map ppr ts) <> ", but is"+      <+> ppr t <> "."++--- Basic checking++-- | Determine if the two types of identical, ignoring uniqueness.+-- Mismatched dimensions are turned into fresh rigid type variables.+-- Causes a 'TypeError' if they fail to match, and otherwise returns+-- one of them.+unifyBranchTypes :: SrcLoc -> PatternType -> PatternType -> TermTypeM (PatternType, [VName])+unifyBranchTypes loc t1 t2 =+  onFailure (CheckingBranches (toStruct t1) (toStruct t2)) $+    unifyMostCommon (mkUsage loc "unification of branch results") t1 t2++unifyBranches :: SrcLoc -> Exp -> Exp -> TermTypeM (PatternType, [VName])+unifyBranches loc e1 e2 = do+  e1_t <- expTypeFully e1+  e2_t <- expTypeFully e2+  unifyBranchTypes loc e1_t e2_t++--- General binding.++doNotShadow :: [String]+doNotShadow = ["&&", "||"]++data InferredType+  = NoneInferred+  | Ascribed PatternType++checkPattern' ::+  UncheckedPattern ->+  InferredType ->+  TermTypeM Pattern+checkPattern' (PatternParens p loc) t =+  PatternParens <$> checkPattern' p t <*> pure loc+checkPattern' (Id name _ loc) _+  | name' `elem` doNotShadow =+    typeError loc mempty $ "The" <+> text name' <+> "operator may not be redefined."+  where+    name' = nameToString name+checkPattern' (Id name NoInfo loc) (Ascribed t) = do+  name' <- newID name+  return $ Id name' (Info t) loc+checkPattern' (Id name NoInfo loc) NoneInferred = do+  name' <- newID name+  t <- newTypeVar loc "t"+  return $ Id name' (Info t) loc+checkPattern' (Wildcard _ loc) (Ascribed t) =+  return $ Wildcard (Info $ t `setUniqueness` Nonunique) loc+checkPattern' (Wildcard NoInfo loc) NoneInferred = do+  t <- newTypeVar loc "t"+  return $ Wildcard (Info t) loc+checkPattern' (TuplePattern ps loc) (Ascribed t)+  | Just ts <- isTupleRecord t,+    length ts == length ps =+    TuplePattern <$> zipWithM checkPattern' ps (map Ascribed ts) <*> pure loc+checkPattern' p@(TuplePattern ps loc) (Ascribed t) = do+  ps_t <- replicateM (length ps) (newTypeVar loc "t")+  unify (mkUsage loc "matching a tuple pattern") (tupleRecord ps_t) $ toStruct t+  t' <- normTypeFully t+  checkPattern' p $ Ascribed t'+checkPattern' (TuplePattern ps loc) NoneInferred =+  TuplePattern <$> mapM (`checkPattern'` NoneInferred) ps <*> pure loc+checkPattern' (RecordPattern p_fs _) _+  | Just (f, fp) <- find (("_" `isPrefixOf`) . nameToString . fst) p_fs =+    typeError fp mempty $+      "Underscore-prefixed fields are not allowed."+        </> "Did you mean" <> dquotes (text (drop 1 (nameToString f)) <> "=_") <> "?"+checkPattern' (RecordPattern p_fs loc) (Ascribed (Scalar (Record t_fs)))+  | sort (map fst p_fs) == sort (M.keys t_fs) =+    RecordPattern . M.toList <$> check <*> pure loc+  where+    check =+      traverse (uncurry checkPattern') $+        M.intersectionWith+          (,)+          (M.fromList p_fs)+          (fmap Ascribed t_fs)+checkPattern' p@(RecordPattern fields loc) (Ascribed t) = do+  fields' <- traverse (const $ newTypeVar loc "t") $ M.fromList fields++  when (sort (M.keys fields') /= sort (map fst fields)) $+    typeError loc mempty $ "Duplicate fields in record pattern" <+> ppr p <> "."++  unify (mkUsage loc "matching a record pattern") (Scalar (Record fields')) $ toStruct t+  t' <- normTypeFully t+  checkPattern' p $ Ascribed t'+checkPattern' (RecordPattern fs loc) NoneInferred =+  RecordPattern . M.toList <$> traverse (`checkPattern'` NoneInferred) (M.fromList fs) <*> pure loc+checkPattern' (PatternAscription p (TypeDecl t NoInfo) loc) maybe_outer_t = do+  (t', st_nodims, _) <- checkTypeExp t+  (st, _) <- instantiateEmptyArrayDims loc "impl" Nonrigid st_nodims++  let st' = fromStruct st+  case maybe_outer_t of+    Ascribed outer_t -> do+      unify (mkUsage loc "explicit type ascription") (toStruct st) (toStruct outer_t)++      -- We also have to make sure that uniqueness matches.  This is+      -- done explicitly, because it is ignored by unification.+      st'' <- normTypeFully st'+      outer_t' <- normTypeFully outer_t+      case unifyTypesU unifyUniqueness st'' outer_t' of+        Just outer_t'' ->+          PatternAscription <$> checkPattern' p (Ascribed outer_t'')+            <*> pure (TypeDecl t' (Info st))+            <*> pure loc+        Nothing ->+          typeError loc mempty $+            "Cannot match type" <+> pquote (ppr outer_t') <+> "with expected type"+              <+> pquote (ppr st'') <> "."+    NoneInferred ->+      PatternAscription <$> checkPattern' p (Ascribed st')+        <*> pure (TypeDecl t' (Info st))+        <*> pure loc+  where+    unifyUniqueness u1 u2 = if u2 `subuniqueOf` u1 then Just u1 else Nothing+checkPattern' (PatternLit e NoInfo loc) (Ascribed t) = do+  e' <- checkExp e+  t' <- expTypeFully e'+  unify (mkUsage loc "matching against literal") (toStruct t') (toStruct t)+  return $ PatternLit e' (Info t') loc+checkPattern' (PatternLit e NoInfo loc) NoneInferred = do+  e' <- checkExp e+  t' <- expTypeFully e'+  return $ PatternLit e' (Info t') loc+checkPattern' (PatternConstr n NoInfo ps loc) (Ascribed (Scalar (Sum cs)))+  | Just ts <- M.lookup n cs = do+    ps' <- zipWithM checkPattern' ps $ map Ascribed ts+    return $ PatternConstr n (Info (Scalar (Sum cs))) ps' loc+checkPattern' (PatternConstr n NoInfo ps loc) (Ascribed t) = do+  t' <- newTypeVar loc "t"+  ps' <- mapM (`checkPattern'` NoneInferred) ps+  mustHaveConstr usage n t' (patternStructType <$> ps')+  unify usage t' (toStruct t)+  t'' <- normTypeFully t+  return $ PatternConstr n (Info t'') ps' loc+  where+    usage = mkUsage loc "matching against constructor"+checkPattern' (PatternConstr n NoInfo ps loc) NoneInferred = do+  ps' <- mapM (`checkPattern'` NoneInferred) ps+  t <- newTypeVar loc "t"+  mustHaveConstr usage n t (patternStructType <$> ps')+  return $ PatternConstr n (Info $ fromStruct t) ps' loc+  where+    usage = mkUsage loc "matching against constructor"++patternNameMap :: Pattern -> NameMap+patternNameMap = M.fromList . map asTerm . S.toList . patternNames+  where+    asTerm v = ((Term, baseName v), qualName v)++checkPattern ::+  UncheckedPattern ->+  InferredType ->+  (Pattern -> TermTypeM a) ->+  TermTypeM a+checkPattern p t m = do+  checkForDuplicateNames [p]+  p' <- onFailure (CheckingPattern p t) $ checkPattern' p t+  bindNameMap (patternNameMap p') $ m p'++binding :: [Ident] -> TermTypeM a -> TermTypeM a+binding bnds = check . handleVars+  where+    handleVars m =+      localScope (`bindVars` bnds) $ do+        -- Those identifiers that can potentially also be sizes are+        -- added as type constraints.  This is necessary so that we+        -- can properly detect scope violations during unification.+        -- We do this for *all* identifiers, not just those that are+        -- integers, because they may become integers later due to+        -- inference...+        forM_ bnds $ \ident ->+          constrain (identName ident) $ ParamSize $ srclocOf ident+        m++    bindVars :: TermScope -> [Ident] -> TermScope+    bindVars = foldl bindVar++    bindVar :: TermScope -> Ident -> TermScope+    bindVar scope (Ident name (Info tp) _) =+      let inedges = boundAliases $ aliases tp+          update (BoundV l tparams in_t)+            -- If 'name' is record or sum-typed, don't alias the+            -- components to 'name', because these no identity+            -- beyond their components.+            | Array {} <- tp = BoundV l tparams (in_t `addAliases` S.insert (AliasBound name))+            | otherwise = BoundV l tparams in_t+          update b = b++          tp' = tp `addAliases` S.insert (AliasBound name)+       in scope+            { scopeVtable =+                M.insert name (BoundV Local [] tp') $+                  adjustSeveral update inedges $+                    scopeVtable scope+            }++    adjustSeveral f = flip $ foldl $ flip $ M.adjust f++    -- Check whether the bound variables have been used correctly+    -- within their scope.+    check m = do+      (a, usages) <- collectBindingsOccurences m+      checkOccurences usages++      mapM_ (checkIfUsed usages) bnds++      return a++    -- Collect and remove all occurences in @bnds@.  This relies+    -- on the fact that no variables shadow any other.+    collectBindingsOccurences m = pass $ do+      (x, usage) <- listen m+      let (relevant, rest) = split usage+      return ((x, relevant), const rest)+      where+        split =+          unzip+            . map+              ( \occ ->+                  let (obs1, obs2) = divide $ observed occ+                      occ_cons = divide <$> consumed occ+                      con1 = fst <$> occ_cons+                      con2 = snd <$> occ_cons+                   in ( occ {observed = obs1, consumed = con1},+                        occ {observed = obs2, consumed = con2}+                      )+              )+        names = S.fromList $ map identName bnds+        divide s = (s `S.intersection` names, s `S.difference` names)++bindingTypes ::+  [Either (VName, TypeBinding) (VName, Constraint)] ->+  TermTypeM a ->+  TermTypeM a+bindingTypes types m = do+  lvl <- curLevel+  modifyConstraints (<> M.map (lvl,) (M.fromList constraints))+  localScope extend m+  where+    (tbinds, constraints) = partitionEithers types+    extend scope =+      scope+        { scopeTypeTable = M.fromList tbinds <> scopeTypeTable scope+        }++bindingTypeParams :: [TypeParam] -> TermTypeM a -> TermTypeM a+bindingTypeParams tparams =+  binding (mapMaybe typeParamIdent tparams)+    . bindingTypes (concatMap typeParamType tparams)+  where+    typeParamType (TypeParamType l v loc) =+      [ Left (v, TypeAbbr l [] (Scalar (TypeVar () Nonunique (typeName v) []))),+        Right (v, ParamType l loc)+      ]+    typeParamType (TypeParamDim v loc) =+      [Right (v, ParamSize loc)]++typeParamIdent :: TypeParam -> Maybe Ident+typeParamIdent (TypeParamDim v loc) =+  Just $ Ident v (Info $ Scalar $ Prim $ Signed Int64) loc+typeParamIdent _ = Nothing++bindingIdent ::+  IdentBase NoInfo Name ->+  PatternType ->+  (Ident -> TermTypeM a) ->+  TermTypeM a+bindingIdent (Ident v NoInfo vloc) t m =+  bindSpaced [(Term, v)] $ do+    v' <- checkName Term v vloc+    let ident = Ident v' (Info t) vloc+    binding [ident] $ m ident++bindingParams ::+  [UncheckedTypeParam] ->+  [UncheckedPattern] ->+  ([TypeParam] -> [Pattern] -> TermTypeM a) ->+  TermTypeM a+bindingParams tps orig_ps m = do+  checkForDuplicateNames orig_ps+  checkTypeParams tps $ \tps' -> bindingTypeParams tps' $ do+    let descend ps' (p : ps) =+          checkPattern p NoneInferred $ \p' ->+            binding (S.toList $ patternIdents p') $ descend (p' : ps') ps+        descend ps' [] = do+          -- Perform an observation of every type parameter.  This+          -- prevents unused-name warnings for otherwise unused+          -- dimensions.+          mapM_ observe $ mapMaybe typeParamIdent tps'+          m tps' $ reverse ps'++    descend [] orig_ps++bindingPattern ::+  PatternBase NoInfo Name ->+  InferredType ->+  (Pattern -> TermTypeM a) ->+  TermTypeM a+bindingPattern p t m = do+  checkForDuplicateNames [p]+  checkPattern p t $ \p' -> binding (S.toList $ patternIdents p') $ do+    -- Perform an observation of every declared dimension.  This+    -- prevents unused-name warnings for otherwise unused dimensions.+    mapM_ observe $ patternDims p'++    m p'++patternDims :: Pattern -> [Ident]+patternDims (PatternParens p _) = patternDims p+patternDims (TuplePattern pats _) = concatMap patternDims pats+patternDims (PatternAscription p (TypeDecl _ (Info t)) _) =+  patternDims p <> mapMaybe (dimIdent (srclocOf p)) (nestedDims t)+  where+    dimIdent _ AnyDim = Nothing+    dimIdent _ (ConstDim _) = Nothing+    dimIdent _ NamedDim {} = Nothing+patternDims _ = []++sliceShape ::+  Maybe (SrcLoc, Rigidity) ->+  [DimIndex] ->+  TypeBase (DimDecl VName) as ->+  TermTypeM (TypeBase (DimDecl VName) as, [VName])+sliceShape r slice t@(Array als u et (ShapeDecl orig_dims)) =+  runWriterT $ setDims <$> adjustDims slice orig_dims+  where+    setDims [] = stripArray (length orig_dims) t+    setDims dims' = Array als u et $ ShapeDecl dims'++    -- If the result is supposed to be AnyDim or a nonrigid size+    -- variable, then don't bother trying to create+    -- non-existential sizes.  This is necessary to make programs+    -- type-check without too much ceremony; see+    -- e.g. tests/inplace5.fut.+    isRigid Rigid {} = True+    isRigid _ = False+    refine_sizes = maybe False (isRigid . snd) r++    sliceSize orig_d i j stride =+      case r of+        Just (loc, Rigid _) -> do+          (d, ext) <-+            lift $+              extSize loc $+                SourceSlice orig_d' (bareExp <$> i) (bareExp <$> j) (bareExp <$> stride)+          tell $ maybeToList ext+          return d+        Just (loc, Nonrigid) ->+          lift $ NamedDim . qualName <$> newDimVar loc Nonrigid "slice_dim"+        Nothing ->+          pure AnyDim+      where+        -- The original size does not matter if the slice is fully specified.+        orig_d'+          | isJust i, isJust j = Nothing+          | otherwise = Just orig_d++    adjustDims (DimFix {} : idxes') (_ : dims) =+      adjustDims idxes' dims+    -- Pattern match some known slices to be non-existential.+    adjustDims (DimSlice i j stride : idxes') (_ : dims)+      | refine_sizes,+        maybe True ((== Just 0) . isInt64) i,+        Just j' <- maybeDimFromExp =<< j,+        maybe True ((== Just 1) . isInt64) stride =+        (j' :) <$> adjustDims idxes' dims+    adjustDims (DimSlice Nothing Nothing stride : idxes') (d : dims)+      | refine_sizes,+        maybe True (maybe False ((== 1) . abs) . isInt64) stride =+        (d :) <$> adjustDims idxes' dims+    adjustDims (DimSlice i j stride : idxes') (d : dims) =+      (:) <$> sliceSize d i j stride <*> adjustDims idxes' dims+    adjustDims _ dims =+      pure dims+sliceShape _ _ t = pure (t, [])++--- Main checkers++-- | @require ts e@ causes a 'TypeError' if @expType e@ is not one of+-- the types in @ts@.  Otherwise, simply returns @e@.+require :: String -> [PrimType] -> Exp -> TermTypeM Exp+require why ts e = do+  mustBeOneOf ts (mkUsage (srclocOf e) why) . toStruct =<< expType e+  return e++unifies :: String -> StructType -> Exp -> TermTypeM Exp+unifies why t e = do+  unify (mkUsage (srclocOf e) why) t . toStruct =<< expType e+  return e++-- The closure of a lambda or local function are those variables that+-- it references, and which local to the current top-level function.+lexicalClosure :: [Pattern] -> Occurences -> TermTypeM Aliasing+lexicalClosure params closure = do+  vtable <- asks $ scopeVtable . termScope+  let isLocal v = case v `M.lookup` vtable of+        Just (BoundV Local _ _) -> True+        _ -> False+  return $+    S.map AliasBound $+      S.filter isLocal $+        allOccuring closure S.\\ mconcat (map patternNames params)++noAliasesIfOverloaded :: PatternType -> TermTypeM PatternType+noAliasesIfOverloaded t@(Scalar (TypeVar _ u tn [])) = do+  subst <- fmap snd . M.lookup (typeLeaf tn) <$> getConstraints+  case subst of+    Just Overloaded {} -> return $ Scalar $ TypeVar mempty u tn []+    _ -> return t+noAliasesIfOverloaded t =+  return t++-- Check the common parts of ascription and coercion.+checkAscript ::+  SrcLoc ->+  UncheckedTypeDecl ->+  UncheckedExp ->+  (StructType -> StructType) ->+  TermTypeM (TypeDecl, Exp)+checkAscript loc decl e shapef = do+  decl' <- checkTypeDecl decl+  e' <- checkExp e+  t <- expTypeFully e'++  (decl_t_nonrigid, _) <-+    instantiateEmptyArrayDims loc "impl" Nonrigid $+      shapef $+        unInfo $ expandedType decl'++  onFailure (CheckingAscription (unInfo $ expandedType decl') (toStruct t)) $+    unify (mkUsage loc "type ascription") decl_t_nonrigid (toStruct t)++  -- We also have to make sure that uniqueness matches.  This is done+  -- explicitly, because uniqueness is ignored by unification.+  t' <- normTypeFully t+  decl_t' <- normTypeFully $ unInfo $ expandedType decl'+  unless (t' `subtypeOf` anySizes decl_t') $+    typeError loc mempty $+      "Type" <+> pquote (ppr t') <+> "is not a subtype of"+        <+> pquote (ppr decl_t') <> "."++  return (decl', e')++unscopeType ::+  SrcLoc ->+  M.Map VName Ident ->+  PatternType ->+  TermTypeM (PatternType, [VName])+unscopeType tloc unscoped t = do+  (t', m) <- runStateT (traverseDims onDim t) mempty+  return (t' `addAliases` S.map unAlias, M.elems m)+  where+    onDim _ p (NamedDim d)+      | Just loc <- srclocOf <$> M.lookup (qualLeaf d) unscoped =+        if p == PosImmediate || p == PosParam+          then inst loc $ qualLeaf d+          else return AnyDim+    onDim _ _ d = return d++    inst loc d = do+      prev <- gets $ M.lookup d+      case prev of+        Just d' -> return $ NamedDim $ qualName d'+        Nothing -> do+          d' <- lift $ newDimVar tloc (Rigid $ RigidOutOfScope loc d) "d"+          modify $ M.insert d d'+          return $ NamedDim $ qualName d'++    unAlias (AliasBound v) | v `M.member` unscoped = AliasFree v+    unAlias a = a++-- 'checkApplyExp' is like 'checkExp', but tries to find the "root+-- function", for better error messages.+checkApplyExp :: UncheckedExp -> TermTypeM (Exp, ApplyOp)+checkApplyExp (Apply e1 e2 _ _ loc) = do+  (e1', (fname, i)) <- checkApplyExp e1+  arg <- checkArg e2+  t <- expType e1'+  (t1, rt, argext, exts) <- checkApply loc (fname, i) t arg+  return+    ( Apply e1' (argExp arg) (Info (diet t1, argext)) (Info rt, Info exts) loc,+      (fname, i + 1)+    )+checkApplyExp e = do+  e' <- checkExp e+  return+    ( e',+      ( case e' of+          Var qn _ _ -> Just qn+          _ -> Nothing,+        0+      )+    )++checkExp :: UncheckedExp -> TermTypeM Exp+checkExp (Literal val loc) =+  return $ Literal val loc+checkExp (StringLit vs loc) =+  return $ StringLit vs loc+checkExp (IntLit val NoInfo loc) = do+  t <- newTypeVar loc "t"+  mustBeOneOf anyNumberType (mkUsage loc "integer literal") t+  return $ IntLit val (Info $ fromStruct t) loc+checkExp (FloatLit val NoInfo loc) = do+  t <- newTypeVar loc "t"+  mustBeOneOf anyFloatType (mkUsage loc "float literal") t+  return $ FloatLit val (Info $ fromStruct t) loc+checkExp (TupLit es loc) =+  TupLit <$> mapM checkExp es <*> pure loc+checkExp (RecordLit fs loc) = do+  fs' <- evalStateT (mapM checkField fs) mempty++  return $ RecordLit fs' loc+  where+    checkField (RecordFieldExplicit f e rloc) = do+      errIfAlreadySet f rloc+      modify $ M.insert f rloc+      RecordFieldExplicit f <$> lift (checkExp e) <*> pure rloc+    checkField (RecordFieldImplicit name NoInfo rloc) = do+      errIfAlreadySet name rloc+      (QualName _ name', t) <- lift $ lookupVar rloc $ qualName name+      modify $ M.insert name rloc+      return $ RecordFieldImplicit name' (Info t) rloc++    errIfAlreadySet f rloc = do+      maybe_sloc <- gets $ M.lookup f+      case maybe_sloc of+        Just sloc ->+          lift $+            typeError rloc mempty $+              "Field" <+> pquote (ppr f)+                <+> "previously defined at"+                <+> text (locStrRel rloc sloc) <> "."+        Nothing -> return ()+checkExp (ArrayLit all_es _ loc) =+  -- Construct the result type and unify all elements with it.  We+  -- only create a type variable for empty arrays; otherwise we use+  -- the type of the first element.  This significantly cuts down on+  -- the number of type variables generated for pathologically large+  -- multidimensional array literals.+  case all_es of+    [] -> do+      et <- newTypeVar loc "t"+      t <- arrayOfM loc et (ShapeDecl [ConstDim 0]) Unique+      return $ ArrayLit [] (Info t) loc+    e : es -> do+      e' <- checkExp e+      et <- expType e'+      es' <- mapM (unifies "type of first array element" (toStruct et) <=< checkExp) es+      et' <- normTypeFully et+      t <- arrayOfM loc et' (ShapeDecl [ConstDim $ length all_es]) Unique+      return $ ArrayLit (e' : es') (Info t) loc+checkExp (Range start maybe_step end _ loc) = do+  start' <- require "use in range expression" anySignedType =<< checkExp start+  start_t <- toStruct <$> expTypeFully start'+  maybe_step' <- case maybe_step of+    Nothing -> return Nothing+    Just step -> do+      let warning = warn loc "First and second element of range are identical, this will produce an empty array."+      case (start, step) of+        (Literal x _, Literal y _) -> when (x == y) warning+        (Var x_name _ _, Var y_name _ _) -> when (x_name == y_name) warning+        _ -> return ()+      Just <$> (unifies "use in range expression" start_t =<< checkExp step)++  let unifyRange e = unifies "use in range expression" start_t =<< checkExp e+  end' <- traverse unifyRange end++  end_t <- case end' of+    DownToExclusive e -> expType e+    ToInclusive e -> expType e+    UpToExclusive e -> expType e++  -- Special case some ranges to give them a known size.+  let dimFromBound = dimFromExp (SourceBound . bareExp)+  (dim, retext) <-+    case (isInt64 start', isInt64 <$> maybe_step', end') of+      (Just 0, Just (Just 1), UpToExclusive end'')+        | Scalar (Prim (Signed Int64)) <- end_t ->+          dimFromBound end''+      (Just 0, Nothing, UpToExclusive end'')+        | Scalar (Prim (Signed Int64)) <- end_t ->+          dimFromBound end''+      (Just 1, Just (Just 2), ToInclusive end'')+        | Scalar (Prim (Signed Int64)) <- end_t ->+          dimFromBound end''+      _ -> do+        d <- newDimVar loc (Rigid RigidRange) "range_dim"+        return (NamedDim $ qualName d, Just d)++  t <- arrayOfM loc start_t (ShapeDecl [dim]) Unique+  let ret = (Info (t `setAliases` mempty), Info $ maybeToList retext)++  return $ Range start' maybe_step' end' ret loc+checkExp (Ascript e decl loc) = do+  (decl', e') <- checkAscript loc decl e id+  return $ Ascript e' decl' loc+checkExp (Coerce e decl _ loc) = do+  -- We instantiate the declared types with all dimensions as nonrigid+  -- fresh type variables, which we then use to unify with the type of+  -- 'e'.  This lets 'e' have whatever sizes it wants, but the overall+  -- type must still match.  Eventually we will throw away those sizes+  -- (they will end up being unified with various sizes in 'e', which+  -- is fine).+  (decl', e') <- checkAscript loc decl e anySizes++  -- Now we instantiate the declared type again, but this time we keep+  -- around the sizes as existentials.  This is the result of the+  -- ascription as a whole.  We use matchDims to obtain the aliasing+  -- of 'e'.+  (decl_t_rigid, ext) <-+    instantiateDimsInReturnType loc Nothing $ unInfo $ expandedType decl'++  t <- expTypeFully e'++  t' <- matchDims (const pure) t $ fromStruct decl_t_rigid++  return $ Coerce e' decl' (Info t', Info ext) loc+checkExp (BinOp (op, oploc) NoInfo (e1, _) (e2, _) NoInfo NoInfo loc) = do+  (op', ftype) <- lookupVar oploc op+  e1_arg <- checkArg e1+  e2_arg <- checkArg e2++  -- Note that the application to the first operand cannot fix any+  -- existential sizes, because it must by necessity be a function.+  (p1_t, rt, p1_ext, _) <- checkApply loc (Just op', 0) ftype e1_arg+  (p2_t, rt', p2_ext, retext) <- checkApply loc (Just op', 1) rt e2_arg++  return $+    BinOp+      (op', oploc)+      (Info ftype)+      (argExp e1_arg, Info (toStruct p1_t, p1_ext))+      (argExp e2_arg, Info (toStruct p2_t, p2_ext))+      (Info rt')+      (Info retext)+      loc+checkExp (Project k e NoInfo loc) = do+  e' <- checkExp e+  t <- expType e'+  kt <- mustHaveField (mkUsage loc $ "projection of field " ++ quote (pretty k)) k t+  return $ Project k e' (Info kt) loc+checkExp (If e1 e2 e3 _ loc) =+  sequentially checkCond $ \e1' _ -> do+    ((e2', e3'), dflow) <- tapOccurences $ checkExp e2 `alternative` checkExp e3++    (brancht, retext) <- unifyBranches loc e2' e3'+    let t' = addAliases brancht (`S.difference` S.map AliasBound (allConsumed dflow))++    zeroOrderType+      (mkUsage loc "returning value of this type from 'if' expression")+      "type returned from branch"+      t'++    return $ If e1' e2' e3' (Info t', Info retext) loc+  where+    checkCond = do+      e1' <- checkExp e1+      let bool = Scalar $ Prim Bool+      e1_t <- toStruct <$> expType e1'+      onFailure (CheckingRequired [bool] e1_t) $+        unify (mkUsage (srclocOf e1') "use as 'if' condition") bool e1_t+      return e1'+checkExp (Parens e loc) =+  Parens <$> checkExp e <*> pure loc+checkExp (QualParens (modname, modnameloc) e loc) = do+  (modname', mod) <- lookupMod loc modname+  case mod of+    ModEnv env -> local (`withEnv` qualifyEnv modname' env) $ do+      e' <- checkExp e+      return $ QualParens (modname', modnameloc) e' loc+    ModFun {} ->+      typeError loc mempty $ "Module" <+> ppr modname <+> " is a parametric module."+  where+    qualifyEnv modname' env =+      env {envNameMap = M.map (qualify' modname') $ envNameMap env}+    qualify' modname' (QualName qs name) =+      QualName (qualQuals modname' ++ [qualLeaf modname'] ++ qs) name+checkExp (Var qn NoInfo loc) = do+  -- The qualifiers of a variable is divided into two parts: first a+  -- possibly-empty sequence of module qualifiers, followed by a+  -- possible-empty sequence of record field accesses.  We use scope+  -- information to perform the split, by taking qualifiers off the+  -- end until we find a module.++  (qn', t, fields) <- findRootVar (qualQuals qn) (qualLeaf qn)++  foldM checkField (Var qn' (Info t) loc) fields+  where+    findRootVar qs name =+      (whenFound <$> lookupVar loc (QualName qs name)) `catchError` notFound qs name++    whenFound (qn', t) = (qn', t, [])++    notFound qs name err+      | null qs = throwError err+      | otherwise = do+        (qn', t, fields) <-+          findRootVar (init qs) (last qs)+            `catchError` const (throwError err)+        return (qn', t, fields ++ [name])++    checkField e k = do+      t <- expType e+      let usage = mkUsage loc $ "projection of field " ++ quote (pretty k)+      kt <- mustHaveField usage k t+      return $ Project k e (Info kt) loc+checkExp (Negate arg loc) = do+  arg' <- require "numeric negation" anyNumberType =<< checkExp arg+  return $ Negate arg' loc+checkExp e@Apply {} = fst <$> checkApplyExp e+checkExp (LetPat pat e body _ loc) =+  sequentially (checkExp e) $ \e' e_occs -> do+    -- Not technically an ascription, but we want the pattern to have+    -- exactly the type of 'e'.+    t <- expType e'+    case anyConsumption e_occs of+      Just c ->+        let msg = "type computed with consumption at " ++ locStr (location c)+         in zeroOrderType (mkUsage loc "consumption in right-hand side of 'let'-binding") msg t+      _ -> return ()++    incLevel $+      bindingPattern pat (Ascribed t) $ \pat' -> do+        body' <- checkExp body+        (body_t, retext) <-+          unscopeType loc (patternMap pat') =<< expTypeFully body'++        return $ LetPat pat' e' body' (Info body_t, Info retext) loc+checkExp (LetFun name (tparams, params, maybe_retdecl, NoInfo, e) body NoInfo loc) =+  sequentially (checkBinding (name, maybe_retdecl, tparams, params, e, loc)) $+    \(tparams', params', maybe_retdecl', rettype, _, e') closure -> do+      closure' <- lexicalClosure params' closure++      bindSpaced [(Term, name)] $ do+        name' <- checkName Term name loc++        let arrow (xp, xt) yt = Scalar $ Arrow () xp xt yt+            ftype = foldr (arrow . patternParam) rettype params'+            entry = BoundV Local tparams' $ ftype `setAliases` closure'+            bindF scope =+              scope+                { scopeVtable =+                    M.insert name' entry $ scopeVtable scope,+                  scopeNameMap =+                    M.insert (Term, name) (qualName name') $+                      scopeNameMap scope+                }+        body' <- localScope bindF $ checkExp body++        -- We fake an ident here, but it's OK as it can't be a size+        -- anyway.+        let fake_ident = Ident name' (Info $ fromStruct ftype) mempty+        (body_t, _) <-+          unscopeType loc (M.singleton name' fake_ident)+            =<< expTypeFully body'++        return $+          LetFun+            name'+            (tparams', params', maybe_retdecl', Info rettype, e')+            body'+            (Info body_t)+            loc+checkExp (LetWith dest src idxes ve body NoInfo loc) =+  sequentially (checkIdent src) $ \src' _ -> do+    (t, _) <- newArrayType (srclocOf src) "src" $ length idxes+    unify (mkUsage loc "type of target array") t $ toStruct $ unInfo $ identType src'++    -- Need the fully normalised type here to get the proper aliasing information.+    src_t <- normTypeFully $ unInfo $ identType src'++    idxes' <- mapM checkDimIndex idxes+    (elemt, _) <- sliceShape (Just (loc, Nonrigid)) idxes' =<< normTypeFully t++    unless (unique src_t) $+      typeError loc mempty $+        "Source" <+> pquote (pprName (identName src))+          <+> "has type"+          <+> ppr src_t <> ", which is not unique."+    vtable <- asks $ scopeVtable . termScope+    forM_ (aliases src_t) $ \v ->+      case aliasVar v `M.lookup` vtable of+        Just (BoundV Local _ v_t)+          | not $ unique v_t ->+            typeError loc mempty $+              "Source" <+> pquote (pprName (identName src))+                <+> "aliases"+                <+> pquote (pprName (aliasVar v)) <> ", which is not consumable."+        _ -> return ()++    sequentially (unifies "type of target array" (toStruct elemt) =<< checkExp ve) $ \ve' _ -> do+      ve_t <- expTypeFully ve'+      when (AliasBound (identName src') `S.member` aliases ve_t) $+        badLetWithValue loc++      bindingIdent dest (src_t `setAliases` S.empty) $ \dest' -> do+        body' <- consuming src' $ checkExp body+        (body_t, _) <-+          unscopeType loc (M.singleton (identName dest') dest')+            =<< expTypeFully body'+        return $ LetWith dest' src' idxes' ve' body' (Info body_t) loc+checkExp (Update src idxes ve loc) = do+  (t, _) <- newArrayType (srclocOf src) "src" $ length idxes+  idxes' <- mapM checkDimIndex idxes+  (elemt, _) <- sliceShape (Just (loc, Nonrigid)) idxes' =<< normTypeFully t++  sequentially (checkExp ve >>= unifies "type of target array" elemt) $ \ve' _ ->+    sequentially (checkExp src >>= unifies "type of target array" t) $ \src' _ -> do+      src_t <- expTypeFully src'+      unless (unique src_t) $+        typeError loc mempty $+          "Source" <+> pquote (ppr src)+            <+> "has type"+            <+> ppr src_t <> ", which is not unique."++      let src_als = aliases src_t+      ve_t <- expTypeFully ve'+      unless (S.null $ src_als `S.intersection` aliases ve_t) $ badLetWithValue loc++      consume loc src_als+      return $ Update src' idxes' ve' loc++-- Record updates are a bit hacky, because we do not have row typing+-- (yet?).  For now, we only permit record updates where we know the+-- full type up to the field we are updating.+checkExp (RecordUpdate src fields ve NoInfo loc) = do+  src' <- checkExp src+  ve' <- checkExp ve+  a <- expTypeFully src'+  let usage = mkUsage loc "record update"+  r <- foldM (flip $ mustHaveField usage) a fields+  ve_t <- expType ve'+  let r' = anySizes $ toStruct r+      ve_t' = anySizes $ toStruct ve_t+  onFailure (CheckingRecordUpdate fields r' ve_t') $+    unify usage r' ve_t'+  maybe_a' <- onRecordField (const ve_t) fields <$> expTypeFully src'+  case maybe_a' of+    Just a' -> return $ RecordUpdate src' fields ve' (Info a') loc+    Nothing ->+      typeError loc mempty $+        "Full type of"+          </> indent 2 (ppr src)+          </> textwrap " is not known at this point.  Add a size annotation to the original record to disambiguate."+checkExp (Index e idxes _ loc) = do+  (t, _) <- newArrayType loc "e" $ length idxes+  e' <- unifies "being indexed at" t =<< checkExp e+  idxes' <- mapM checkDimIndex idxes+  -- XXX, the RigidSlice here will be overridden in sliceShape with a proper value.+  (t', retext) <-+    sliceShape (Just (loc, Rigid (RigidSlice Nothing ""))) idxes'+      =<< expTypeFully e'++  -- Remove aliases if the result is an overloaded type, because that+  -- will certainly not be aliased.+  t'' <- noAliasesIfOverloaded t'++  return $ Index e' idxes' (Info t'', Info retext) loc+checkExp (Assert e1 e2 NoInfo loc) = do+  e1' <- require "being asserted" [Bool] =<< checkExp e1+  e2' <- checkExp e2+  return $ Assert e1' e2' (Info (pretty e1)) loc+checkExp (Lambda params body rettype_te NoInfo loc) =+  removeSeminullOccurences $+    noUnique $+      incLevel $+        bindingParams [] params $ \_ params' -> do+          rettype_checked <- traverse checkTypeExp rettype_te+          let declared_rettype =+                case rettype_checked of+                  Just (_, st, _) -> Just st+                  Nothing -> Nothing+          (body', closure) <-+            tapOccurences $ checkFunBody params' body declared_rettype loc+          body_t <- expTypeFully body'++          params'' <- mapM updateTypes params'++          (rettype', rettype_st) <-+            case rettype_checked of+              Just (te, st, _) ->+                return (Just te, st)+              Nothing -> do+                ret <-+                  inferReturnSizes params'' $+                    toStruct $+                      inferReturnUniqueness params'' body_t+                return (Nothing, ret)++          checkGlobalAliases params' body_t loc+          verifyFunctionParams Nothing params'++          closure' <- lexicalClosure params'' closure++          return $ Lambda params'' body' rettype' (Info (closure', rettype_st)) loc+  where+    -- Inferring the sizes of the return type of a lambda is a lot+    -- like let-generalisation.  We wish to remove any rigid sizes+    -- that were created when checking the body, except for those that+    -- are visible in types that existed before we entered the body,+    -- are parameters, or are used in parameters.+    inferReturnSizes params' ret = do+      cur_lvl <- curLevel+      let named (Named x, _) = Just x+          named (Unnamed, _) = Nothing+          param_names = mapMaybe (named . patternParam) params'+          pos_sizes =+            typeDimNamesPos (foldFunType (map patternStructType params') ret)+          hide k (lvl, _) =+            lvl >= cur_lvl && k `notElem` param_names && k `S.notMember` pos_sizes++      hidden_sizes <-+        S.fromList . M.keys . M.filterWithKey hide <$> getConstraints++      let onDim (NamedDim name)+            | not (qualLeaf name `S.member` hidden_sizes) = NamedDim name+            | otherwise = AnyDim+          onDim d = d++      return $ first onDim ret+checkExp (OpSection op _ loc) = do+  (op', ftype) <- lookupVar loc op+  return $ OpSection op' (Info ftype) loc+checkExp (OpSectionLeft op _ e _ _ loc) = do+  (op', ftype) <- lookupVar loc op+  e_arg <- checkArg e+  (t1, rt, argext, retext) <- checkApply loc (Just op', 0) ftype e_arg+  case rt of+    Scalar (Arrow _ _ t2 rettype) ->+      return $+        OpSectionLeft+          op'+          (Info ftype)+          (argExp e_arg)+          (Info (toStruct t1, argext), Info $ toStruct t2)+          (Info rettype, Info retext)+          loc+    _ ->+      typeError loc mempty $+        "Operator section with invalid operator of type" <+> ppr ftype+checkExp (OpSectionRight op _ e _ NoInfo loc) = do+  (op', ftype) <- lookupVar loc op+  e_arg <- checkArg e+  case ftype of+    Scalar (Arrow as1 m1 t1 (Scalar (Arrow as2 m2 t2 ret))) -> do+      (t2', ret', argext, _) <-+        checkApply+          loc+          (Just op', 1)+          (Scalar $ Arrow as2 m2 t2 $ Scalar $ Arrow as1 m1 t1 ret)+          e_arg+      return $+        OpSectionRight+          op'+          (Info ftype)+          (argExp e_arg)+          (Info $ toStruct t1, Info (toStruct t2', argext))+          (Info $ addAliases ret (<> aliases ret'))+          loc+    _ ->+      typeError loc mempty $+        "Operator section with invalid operator of type" <+> ppr ftype+checkExp (ProjectSection fields NoInfo loc) = do+  a <- newTypeVar loc "a"+  let usage = mkUsage loc "projection at"+  b <- foldM (flip $ mustHaveField usage) a fields+  return $ ProjectSection fields (Info $ Scalar $ Arrow mempty Unnamed a b) loc+checkExp (IndexSection idxes NoInfo loc) = do+  (t, _) <- newArrayType loc "e" $ length idxes+  idxes' <- mapM checkDimIndex idxes+  (t', _) <- sliceShape Nothing idxes' t+  return $ IndexSection idxes' (Info $ fromStruct $ Scalar $ Arrow mempty Unnamed t t') loc+checkExp (DoLoop _ mergepat mergeexp form loopbody NoInfo loc) =+  sequentially (checkExp mergeexp) $ \mergeexp' _ -> do+    zeroOrderType+      (mkUsage (srclocOf mergeexp) "use as loop variable")+      "type used as loop variable"+      =<< expTypeFully mergeexp'++    -- The handling of dimension sizes is a bit intricate, but very+    -- similar to checking a function, followed by checking a call to+    -- it.  The overall procedure is as follows:+    --+    -- (1) All empty dimensions in the merge pattern are instantiated+    -- with nonrigid size variables.  All explicitly specified+    -- dimensions are preserved.+    --+    -- (2) The body of the loop is type-checked.  The result type is+    -- combined with the merge pattern type to determine which sizes are+    -- variant, and these are turned into size parameters for the merge+    -- pattern.+    --+    -- (3) We now conceptually have a function parameter type and return+    -- type.  We check that it can be called with the initial merge+    -- values as argument.  The result of this is the type of the loop+    -- as a whole.+    --+    -- (There is also a convergence loop for inferring uniqueness, but+    -- that's orthogonal to the size handling.)++    (merge_t, new_dims) <-+      instantiateEmptyArrayDims loc "loop" Nonrigid+        . anySizes -- dim handling (1)+        =<< expTypeFully mergeexp'++    -- dim handling (2)+    let checkLoopReturnSize mergepat' loopbody' = do+          loopbody_t <- expTypeFully loopbody'+          pat_t <- normTypeFully $ patternType mergepat'+          -- We are ignoring the dimensions here, because any mismatches+          -- should be turned into fresh size variables.+          onFailure (CheckingLoopBody (toStruct (anySizes pat_t)) (toStruct loopbody_t)) $+            expect+              (mkUsage (srclocOf loopbody) "matching loop body to loop pattern")+              (toStruct (anySizes pat_t))+              (toStruct loopbody_t)+          pat_t' <- normTypeFully pat_t+          loopbody_t' <- normTypeFully loopbody_t++          -- For each new_dims, figure out what they are instantiated+          -- with in the initial value.  This is used to determine+          -- whether a size is invariant because it always matches the+          -- initial instantiation of that size.+          let initSubst (NamedDim v, d) = Just (v, d)+              initSubst _ = Nothing+          init_substs <-+            M.fromList . mapMaybe initSubst . snd+              . anyDimOnMismatch pat_t'+              <$> expTypeFully mergeexp'++          -- Figure out which of the 'new_dims' dimensions are variant.+          -- This works because we know that each dimension from+          -- new_dims in the pattern is unique and distinct.+          --+          -- Our logic here is a bit reversed: the *mismatches* (from+          -- new_dims) are what we want to extract and turn into size+          -- parameters.+          let mismatchSubst (NamedDim v, d)+                | qualLeaf v `elem` new_dims =+                  case M.lookup v init_substs of+                    Just d'+                      | d' == d ->+                        return $ Just (qualLeaf v, SizeSubst d)+                    _ -> do+                      tell [qualLeaf v]+                      return Nothing+              mismatchSubst _ = return Nothing++              (init_substs', sparams) =+                runWriter $+                  M.fromList . catMaybes+                    <$> mapM+                      mismatchSubst+                      (snd $ anyDimOnMismatch pat_t' loopbody_t')++          -- Make sure that any of new_dims that are invariant will be+          -- replaced with the invariant size in the loop body.  Failure+          -- to do this can cause type annotations to still refer to+          -- new_dims.+          let dimToInit (v, SizeSubst d) =+                constrain v $ Size (Just d) (mkUsage loc "size of loop parameter")+              dimToInit _ =+                return ()+          mapM_ dimToInit $ M.toList init_substs'++          mergepat'' <- applySubst (`M.lookup` init_substs') <$> updateTypes mergepat'+          return (nub sparams, mergepat'')++    -- First we do a basic check of the loop body to figure out which of+    -- the merge parameters are being consumed.  For this, we first need+    -- to check the merge pattern, which requires the (initial) merge+    -- expression.+    --+    -- Play a little with occurences to ensure it does not look like+    -- none of the merge variables are being used.+    ((sparams, mergepat', form', loopbody'), bodyflow) <-+      case form of+        For i uboundexp -> do+          uboundexp' <- require "being the bound in a 'for' loop" anySignedType =<< checkExp uboundexp+          bound_t <- expTypeFully uboundexp'+          bindingIdent i bound_t $ \i' ->+            noUnique $+              bindingPattern mergepat (Ascribed merge_t) $+                \mergepat' -> onlySelfAliasing $+                  tapOccurences $ do+                    loopbody' <- noSizeEscape $ checkExp loopbody+                    (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'+                    return+                      ( sparams,+                        mergepat'',+                        For i' uboundexp',+                        loopbody'+                      )+        ForIn xpat e -> do+          (arr_t, _) <- newArrayType (srclocOf e) "e" 1+          e' <- unifies "being iterated in a 'for-in' loop" arr_t =<< checkExp e+          t <- expTypeFully e'+          case t of+            _+              | Just t' <- peelArray 1 t ->+                bindingPattern xpat (Ascribed t') $ \xpat' ->+                  noUnique $+                    bindingPattern mergepat (Ascribed merge_t) $+                      \mergepat' -> onlySelfAliasing $+                        tapOccurences $ do+                          loopbody' <- noSizeEscape $ checkExp loopbody+                          (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'+                          return+                            ( sparams,+                              mergepat'',+                              ForIn xpat' e',+                              loopbody'+                            )+              | otherwise ->+                typeError (srclocOf e) mempty $+                  "Iteratee of a for-in loop must be an array, but expression has type"+                    <+> ppr t+        While cond ->+          noUnique $+            bindingPattern mergepat (Ascribed merge_t) $ \mergepat' ->+              onlySelfAliasing $+                tapOccurences $+                  sequentially+                    ( checkExp cond+                        >>= unifies "being the condition of a 'while' loop" (Scalar $ Prim Bool)+                    )+                    $ \cond' _ -> do+                      loopbody' <- noSizeEscape $ checkExp loopbody+                      (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'+                      return+                        ( sparams,+                          mergepat'',+                          While cond',+                          loopbody'+                        )++    mergepat'' <- do+      loopbody_t <- expTypeFully loopbody'+      convergePattern mergepat' (allConsumed bodyflow) loopbody_t $+        mkUsage (srclocOf loopbody') "being (part of) the result of the loop body"++    let consumeMerge (Id _ (Info pt) ploc) mt+          | unique pt = consume ploc $ aliases mt+        consumeMerge (TuplePattern pats _) t+          | Just ts <- isTupleRecord t =+            zipWithM_ consumeMerge pats ts+        consumeMerge (PatternParens pat _) t =+          consumeMerge pat t+        consumeMerge (PatternAscription pat _ _) t =+          consumeMerge pat t+        consumeMerge _ _ =+          return ()+    consumeMerge mergepat'' =<< expTypeFully mergeexp'++    -- dim handling (3)+    let sparams_anydim = M.fromList $ zip sparams $ repeat $ SizeSubst AnyDim+        loopt_anydims =+          applySubst (`M.lookup` sparams_anydim) $+            patternType mergepat''+    (merge_t', _) <-+      instantiateEmptyArrayDims loc "loopres" Nonrigid $ toStruct loopt_anydims+    mergeexp_t <- toStruct <$> expTypeFully mergeexp'+    onFailure (CheckingLoopInitial (toStruct loopt_anydims) mergeexp_t) $+      unify+        (mkUsage (srclocOf mergeexp') "matching initial loop values to pattern")+        merge_t'+        mergeexp_t++    (loopt, retext) <- instantiateDimsInType loc RigidLoop loopt_anydims+    -- We set all of the uniqueness to be unique.  This is intentional,+    -- and matches what happens for function calls.  Those arrays that+    -- really *cannot* be consumed will alias something unconsumable,+    -- and will be caught that way.+    let bound_here = patternNames mergepat'' <> S.fromList sparams <> form_bound+        form_bound =+          case form' of+            For v _ -> S.singleton $ identName v+            ForIn forpat _ -> patternNames forpat+            While {} -> mempty+        loopt' =+          second (`S.difference` S.map AliasBound bound_here) $+            loopt `setUniqueness` Unique++    -- Eliminate those new_dims that turned into sparams so it won't+    -- look like we have ambiguous sizes lying around.+    modifyConstraints $ M.filterWithKey $ \k _ -> k `notElem` sparams++    return $ DoLoop sparams mergepat'' mergeexp' form' loopbody' (Info (loopt', retext)) loc+  where+    convergePattern pat body_cons body_t body_loc = do+      let consumed_merge = patternNames pat `S.intersection` body_cons++          uniquePat (Wildcard (Info t) wloc) =+            Wildcard (Info $ t `setUniqueness` Nonunique) wloc+          uniquePat (PatternParens p ploc) =+            PatternParens (uniquePat p) ploc+          uniquePat (Id name (Info t) iloc)+            | name `S.member` consumed_merge =+              let t' = t `setUniqueness` Unique `setAliases` mempty+               in Id name (Info t') iloc+            | otherwise =+              let t' = t `setUniqueness` Nonunique+               in Id name (Info t') iloc+          uniquePat (TuplePattern pats ploc) =+            TuplePattern (map uniquePat pats) ploc+          uniquePat (RecordPattern fs ploc) =+            RecordPattern (map (fmap uniquePat) fs) ploc+          uniquePat (PatternAscription p t ploc) =+            PatternAscription p t ploc+          uniquePat p@PatternLit {} = p+          uniquePat (PatternConstr n t ps ploc) =+            PatternConstr n t (map uniquePat ps) ploc++          -- Make the pattern unique where needed.+          pat' = uniquePat pat++      pat_t <- normTypeFully $ patternType pat'+      unless (toStructural body_t `subtypeOf` toStructural pat_t) $+        unexpectedType (srclocOf body_loc) (toStruct body_t) [toStruct pat_t]++      -- Check that the new values of consumed merge parameters do not+      -- alias something bound outside the loop, AND that anything+      -- returned for a unique merge parameter does not alias anything+      -- else returned.  We also update the aliases for the pattern.+      bound_outside <- asks $ S.fromList . M.keys . scopeVtable . termScope+      let combAliases t1 t2 =+            case t1 of+              Scalar Record {} -> t1+              _ -> t1 `addAliases` (<> aliases t2)++          checkMergeReturn (Id pat_v (Info pat_v_t) patloc) t+            | unique pat_v_t,+              v : _ <-+                S.toList $+                  S.map aliasVar (aliases t) `S.intersection` bound_outside =+              lift $+                typeError loc mempty $+                  "Return value for loop parameter"+                    <+> pquote (pprName pat_v)+                    <+> "aliases"+                    <+> pprName v <> "."+            | otherwise = do+              (cons, obs) <- get+              unless (S.null $ aliases t `S.intersection` cons) $+                lift $+                  typeError loc mempty $+                    "Return value for loop parameter"+                      <+> pquote (pprName pat_v)+                      <+> "aliases other consumed loop parameter."+              when+                ( unique pat_v_t+                    && not (S.null (aliases t `S.intersection` (cons <> obs)))+                )+                $ lift $+                  typeError loc mempty $+                    "Return value for consuming loop parameter"+                      <+> pquote (pprName pat_v)+                      <+> "aliases previously returned value."+              if unique pat_v_t+                then put (cons <> aliases t, obs)+                else put (cons, obs <> aliases t)++              return $ Id pat_v (Info (combAliases pat_v_t t)) patloc+          checkMergeReturn (Wildcard (Info pat_v_t) patloc) t =+            return $ Wildcard (Info (combAliases pat_v_t t)) patloc+          checkMergeReturn (PatternParens p _) t =+            checkMergeReturn p t+          checkMergeReturn (PatternAscription p _ _) t =+            checkMergeReturn p t+          checkMergeReturn (RecordPattern pfs patloc) (Scalar (Record tfs)) =+            RecordPattern . M.toList <$> sequence pfs' <*> pure patloc+            where+              pfs' =+                M.intersectionWith+                  checkMergeReturn+                  (M.fromList pfs)+                  tfs+          checkMergeReturn (TuplePattern pats patloc) t+            | Just ts <- isTupleRecord t =+              TuplePattern+                <$> zipWithM checkMergeReturn pats ts+                <*> pure patloc+          checkMergeReturn p _ =+            return p++      (pat'', (pat_cons, _)) <-+        runStateT (checkMergeReturn pat' body_t) (mempty, mempty)++      let body_cons' = body_cons <> S.map aliasVar pat_cons+      if body_cons' == body_cons && patternType pat'' == patternType pat+        then return pat'+        else convergePattern pat'' body_cons' body_t body_loc+checkExp (Constr name es NoInfo loc) = do+  t <- newTypeVar loc "t"+  es' <- mapM checkExp es+  ets <- mapM expTypeFully es'+  mustHaveConstr (mkUsage loc "use of constructor") name t (toStruct <$> ets)+  -- A sum value aliases *anything* that went into its construction.+  let als = foldMap aliases ets+  return $ Constr name es' (Info $ fromStruct t `addAliases` (<> als)) loc+checkExp (Match e cs _ loc) =+  sequentially (checkExp e) $ \e' _ -> do+    mt <- expTypeFully e'+    (cs', t, retext) <- checkCases mt cs+    zeroOrderType+      (mkUsage loc "being returned 'match'")+      "type returned from pattern match"+      t+    return $ Match e' cs' (Info t, Info retext) loc+checkExp (Attr info e loc) =+  Attr info <$> checkExp e <*> pure loc++checkCases ::+  PatternType ->+  NE.NonEmpty (CaseBase NoInfo Name) ->+  TermTypeM (NE.NonEmpty (CaseBase Info VName), PatternType, [VName])+checkCases mt rest_cs =+  case NE.uncons rest_cs of+    (c, Nothing) -> do+      (c', t, retext) <- checkCase mt c+      return (c' NE.:| [], t, retext)+    (c, Just cs) -> do+      (((c', c_t, _), (cs', cs_t, _)), dflow) <-+        tapOccurences $ checkCase mt c `alternative` checkCases mt cs+      (brancht, retext) <- unifyBranchTypes (srclocOf c) c_t cs_t+      let t =+            addAliases+              brancht+              (`S.difference` S.map AliasBound (allConsumed dflow))+      return (NE.cons c' cs', t, retext)++checkCase ::+  PatternType ->+  CaseBase NoInfo Name ->+  TermTypeM (CaseBase Info VName, PatternType, [VName])+checkCase mt (CasePat p e loc) =+  bindingPattern p (Ascribed mt) $ \p' -> do+    e' <- checkExp e+    (t, retext) <- unscopeType loc (patternMap p') =<< expTypeFully e'+    return (CasePat p' e' loc, t, retext)++-- | An unmatched pattern. Used in in the generation of+-- unmatched pattern warnings by the type checker.+data Unmatched p+  = UnmatchedNum p [ExpBase Info VName]+  | UnmatchedBool p+  | UnmatchedConstr p+  | Unmatched p+  deriving (Functor, Show)++instance Pretty (Unmatched (PatternBase Info VName)) where+  ppr um = case um of+    (UnmatchedNum p nums) -> ppr' p <+> "where p is not one of" <+> ppr nums+    (UnmatchedBool p) -> ppr' p+    (UnmatchedConstr p) -> ppr' p+    (Unmatched p) -> ppr' p+    where+      ppr' (PatternAscription p t _) = ppr p <> ":" <+> ppr t+      ppr' (PatternParens p _) = parens $ ppr' p+      ppr' (Id v _ _) = pprName v+      ppr' (TuplePattern pats _) = parens $ commasep $ map ppr' pats+      ppr' (RecordPattern fs _) = braces $ commasep $ map ppField fs+        where+          ppField (name, t) = text (nameToString name) <> equals <> ppr' t+      ppr' Wildcard {} = "_"+      ppr' (PatternLit e _ _) = ppr e+      ppr' (PatternConstr n _ ps _) = "#" <> ppr n <+> sep (map ppr' ps)++unpackPat :: Pattern -> [Maybe Pattern]+unpackPat Wildcard {} = [Nothing]+unpackPat (PatternParens p _) = unpackPat p+unpackPat Id {} = [Nothing]+unpackPat (TuplePattern ps _) = Just <$> ps+unpackPat (RecordPattern fs _) = Just . snd <$> sortFields (M.fromList fs)+unpackPat (PatternAscription p _ _) = unpackPat p+unpackPat p@PatternLit {} = [Just p]+unpackPat p@PatternConstr {} = [Just p]++wildPattern :: Pattern -> Int -> Unmatched Pattern -> Unmatched Pattern+wildPattern (TuplePattern ps loc) pos um = wildTuple <$> um+  where+    wildTuple p = TuplePattern (take (pos - 1) ps' ++ [p] ++ drop pos ps') loc+    ps' = map wildOut ps+    wildOut p = Wildcard (Info (patternType p)) (srclocOf p)+wildPattern (RecordPattern fs loc) pos um = wildRecord <$> um+  where+    wildRecord p =+      RecordPattern (take (pos - 1) fs' ++ [(fst (fs !! (pos - 1)), p)] ++ drop pos fs') loc+    fs' = map wildOut fs+    wildOut (f, p) = (f, Wildcard (Info (patternType p)) (srclocOf p))+wildPattern (PatternAscription p _ _) pos um = wildPattern p pos um+wildPattern (PatternParens p _) pos um = wildPattern p pos um+wildPattern (PatternConstr n t ps loc) pos um = wildConstr <$> um+  where+    wildConstr p = PatternConstr n t (take (pos - 1) ps' ++ [p] ++ drop pos ps') loc+    ps' = map wildOut ps+    wildOut p = Wildcard (Info (patternType p)) (srclocOf p)+wildPattern _ _ um = um++checkUnmatched :: Exp -> TermTypeM ()+checkUnmatched e = void $ checkUnmatched' e >> astMap tv e+  where+    checkUnmatched' (Match _ cs _ loc) =+      let ps = fmap (\(CasePat p _ _) -> p) cs+       in case unmatched id $ NE.toList ps of+            [] -> return ()+            ps' ->+              typeError loc mempty $+                "Unmatched cases in match expression:"+                  </> indent 2 (stack (map ppr ps'))+    checkUnmatched' _ = return ()+    tv =+      ASTMapper+        { mapOnExp =+            \e' -> checkUnmatched' e' >> return e',+          mapOnName = pure,+          mapOnQualName = pure,+          mapOnStructType = pure,+          mapOnPatternType = pure+        }++-- | A data type for constructor patterns.  This is used to make the+-- code for detecting unmatched constructors cleaner, by separating+-- the constructor-pattern cases from other cases.+data ConstrPat = ConstrPat+  { constrName :: Name,+    constrType :: PatternType,+    constrPayload :: [Pattern],+    constrSrcLoc :: SrcLoc+  }++-- Be aware of these fishy equality instances!++instance Eq ConstrPat where+  ConstrPat c1 _ _ _ == ConstrPat c2 _ _ _ = c1 == c2++instance Ord ConstrPat where+  ConstrPat c1 _ _ _ `compare` ConstrPat c2 _ _ _ = c1 `compare` c2++unmatched :: (Unmatched Pattern -> Unmatched Pattern) -> [Pattern] -> [Unmatched Pattern]+unmatched hole orig_ps+  | p : _ <- orig_ps,+    sameStructure labeledCols = do+    (i, cols) <- labeledCols+    let hole' = if isConstr p then hole else hole . wildPattern p i+    case sequence cols of+      Nothing -> []+      Just cs+        | all isPatternLit cs -> map hole' $ localUnmatched cs+        | otherwise -> unmatched hole' cs+  | otherwise = []+  where+    labeledCols = zip [1 ..] $ transpose $ map unpackPat orig_ps++    localUnmatched :: [Pattern] -> [Unmatched Pattern]+    localUnmatched [] = []+    localUnmatched ps'@(p' : _) =+      case patternType p' of+        Scalar (Sum cs'') ->+          -- We now know that we are matching a sum type, and thus+          -- that all patterns ps' are constructors (checked by+          -- 'all isPatternLit' before this function is called).+          let constrs = M.keys cs''+              matched = mapMaybe constr ps'+              unmatched' =+                map (UnmatchedConstr . buildConstr cs'') $+                  constrs \\ map constrName matched+           in case unmatched' of+                [] ->+                  let constrGroups = group (sort matched)+                      removedConstrs = mapMaybe stripConstrs constrGroups+                      transposed = (fmap . fmap) transpose removedConstrs+                      findUnmatched (pc, trans) = do+                        col <- trans+                        case col of+                          [] -> []+                          ((i, _) : _) -> unmatched (wilder i pc) (map snd col)+                      wilder i pc s = (`PatternParens` mempty) <$> wildPattern pc i s+                   in concatMap findUnmatched transposed+                _ -> unmatched'+        Scalar (Prim t) | not (any idOrWild ps') ->+          -- We now know that we are matching a sum type, and thus+          -- that all patterns ps' are literals (checked by 'all+          -- isPatternLit' before this function is called).+          case t of+            Bool ->+              let matched = nub $ mapMaybe (pExp >=> bool) $ filter isPatternLit ps'+               in map (UnmatchedBool . buildBool (Scalar (Prim t))) $ [True, False] \\ matched+            _ ->+              let matched = mapMaybe pExp $ filter isPatternLit ps'+               in [UnmatchedNum (buildId (Info $ Scalar $ Prim t) "p") matched]+        _ -> []++    isConstr PatternConstr {} = True+    isConstr (PatternParens p _) = isConstr p+    isConstr _ = False++    stripConstrs :: [ConstrPat] -> Maybe (Pattern, [[(Int, Pattern)]])+    stripConstrs (pc@ConstrPat {} : cs') = Just (unConstr pc, stripConstr pc : map stripConstr cs')+    stripConstrs [] = Nothing++    stripConstr :: ConstrPat -> [(Int, Pattern)]+    stripConstr (ConstrPat _ _ ps' _) = zip [1 ..] ps'++    sameStructure [] = True+    sameStructure (x : xs) = all (\y -> length y == length x') xs'+      where+        (x' : xs') = map snd (x : xs)++    pExp (PatternLit e' _ _) = Just e'+    pExp _ = Nothing++    constr (PatternConstr c (Info t) ps loc) = Just $ ConstrPat c t ps loc+    constr (PatternParens p _) = constr p+    constr (PatternAscription p' _ _) = constr p'+    constr _ = Nothing++    unConstr p =+      PatternConstr (constrName p) (Info $ constrType p) (constrPayload p) (constrSrcLoc p)++    isPatternLit PatternLit {} = True+    isPatternLit (PatternAscription p' _ _) = isPatternLit p'+    isPatternLit (PatternParens p' _) = isPatternLit p'+    isPatternLit PatternConstr {} = True+    isPatternLit _ = False++    idOrWild Id {} = True+    idOrWild Wildcard {} = True+    idOrWild (PatternAscription p' _ _) = idOrWild p'+    idOrWild (PatternParens p' _) = idOrWild p'+    idOrWild _ = False++    bool (Literal (BoolValue b) _) = Just b+    bool _ = Nothing++    buildConstr m c =+      let t = Scalar $ Sum m+          cs = m M.! c+          wildCS = map (\ct -> Wildcard (Info ct) mempty) cs+       in if null wildCS+            then PatternConstr c (Info t) [] mempty+            else PatternParens (PatternConstr c (Info t) wildCS mempty) mempty+    buildBool t b =+      PatternLit (Literal (BoolValue b) mempty) (Info (addSizes t)) mempty+    buildId t n =+      -- The VName tag here will never be used since the value+      -- exists exclusively for printing warnings.+      Id (VName (nameFromString n) (-1)) t mempty++checkIdent :: IdentBase NoInfo Name -> TermTypeM Ident+checkIdent (Ident name _ loc) = do+  (QualName _ name', vt) <- lookupVar loc (qualName name)+  return $ Ident name' (Info vt) loc++checkDimIndex :: DimIndexBase NoInfo Name -> TermTypeM DimIndex+checkDimIndex (DimFix i) =+  DimFix <$> (require "use as index" anySignedType =<< checkExp i)+checkDimIndex (DimSlice i j s) =+  DimSlice <$> check i <*> check j <*> check s+  where+    check =+      maybe (return Nothing) $+        fmap Just . unifies "use as index" (Scalar $ Prim $ Signed Int64) <=< checkExp++sequentially :: TermTypeM a -> (a -> Occurences -> TermTypeM b) -> TermTypeM b+sequentially m1 m2 = do+  (a, m1flow) <- collectOccurences m1+  (b, m2flow) <- collectOccurences $ m2 a m1flow+  occur $ m1flow `seqOccurences` m2flow+  return b++type Arg = (Exp, PatternType, Occurences, SrcLoc)++argExp :: Arg -> Exp+argExp (e, _, _, _) = e++argType :: Arg -> PatternType+argType (_, t, _, _) = t++checkArg :: UncheckedExp -> TermTypeM Arg+checkArg arg = do+  (arg', dflow) <- collectOccurences $ checkExp arg+  arg_t <- expType arg'+  return (arg', arg_t, dflow, srclocOf arg')++instantiateDimsInType ::+  SrcLoc ->+  RigidSource ->+  TypeBase (DimDecl VName) als ->+  TermTypeM (TypeBase (DimDecl VName) als, [VName])+instantiateDimsInType tloc rsrc =+  instantiateEmptyArrayDims tloc "d" $ Rigid rsrc++instantiateDimsInReturnType ::+  SrcLoc ->+  Maybe (QualName VName) ->+  TypeBase (DimDecl VName) als ->+  TermTypeM (TypeBase (DimDecl VName) als, [VName])+instantiateDimsInReturnType tloc fname =+  instantiateEmptyArrayDims tloc "ret" $ Rigid $ RigidRet fname++-- Some information about the function/operator we are trying to+-- apply, and how many arguments it has previously accepted.  Used for+-- generating nicer type errors.+type ApplyOp = (Maybe (QualName VName), Int)++checkApply ::+  SrcLoc ->+  ApplyOp ->+  PatternType ->+  Arg ->+  TermTypeM (PatternType, PatternType, Maybe VName, [VName])+checkApply+  loc+  (fname, _)+  (Scalar (Arrow as pname tp1 tp2))+  (argexp, argtype, dflow, argloc) =+    onFailure (CheckingApply fname argexp (toStruct tp1) (toStruct argtype)) $ do+      expect (mkUsage argloc "use as function argument") (toStruct tp1) (toStruct argtype)++      -- Perform substitutions of instantiated variables in the types.+      tp1' <- normTypeFully tp1+      (tp2', ext) <- instantiateDimsInReturnType loc fname =<< normTypeFully tp2+      argtype' <- normTypeFully argtype++      -- Check whether this would produce an impossible return type.+      let (_, tp2_paramdims, _) = dimUses $ toStruct tp2'+      case filter (`S.member` tp2_paramdims) ext of+        [] -> return ()+        ext_paramdims -> do+          let onDim (NamedDim qn)+                | qualLeaf qn `elem` ext_paramdims = AnyDim+              onDim d = d+          typeError loc mempty $+            "Anonymous size would appear in function parameter of return type:"+              </> indent 2 (ppr (first onDim tp2'))+              </> textwrap "This is usually because a higher-order function is used with functional arguments that return anonymous sizes, which are then used as parameters of other function arguments."++      occur [observation as loc]++      checkOccurences dflow++      case anyConsumption dflow of+        Just c ->+          let msg = "type of expression with consumption at " ++ locStr (location c)+           in zeroOrderType (mkUsage argloc "potential consumption in expression") msg tp1+        _ -> return ()++      occurs <- (dflow `seqOccurences`) <$> consumeArg argloc argtype' (diet tp1')++      checkIfConsumable loc $ S.map AliasBound $ allConsumed occurs+      occur occurs++      (argext, parsubst) <-+        case pname of+          Named pname' -> do+            (d, argext) <- sizeSubst tp1' argexp+            return+              ( argext,+                (`M.lookup` M.singleton pname' (SizeSubst d))+              )+          _ -> return (Nothing, const Nothing)+      let tp2'' = applySubst parsubst $ returnType tp2' (diet tp1') argtype'++      return (tp1', tp2'', argext, ext)+    where+      sizeSubst (Scalar (Prim (Signed Int64))) e = dimFromArg fname e+      sizeSubst _ _ = return (AnyDim, Nothing)+checkApply loc fname tfun@(Scalar TypeVar {}) arg = do+  tv <- newTypeVar loc "b"+  unify (mkUsage loc "use as function") (toStruct tfun) $+    Scalar $ Arrow mempty Unnamed (toStruct (argType arg)) tv+  tfun' <- normPatternType tfun+  checkApply loc fname tfun' arg+checkApply loc (fname, prev_applied) ftype (argexp, _, _, _) = do+  let fname' = maybe "expression" (pquote . ppr) fname++  typeError loc mempty $+    if prev_applied == 0+      then+        "Cannot apply" <+> fname' <+> "as function, as it has type:"+          </> indent 2 (ppr ftype)+      else+        "Cannot apply" <+> fname' <+> "to argument #" <> ppr (prev_applied + 1)+          <+> pquote (shorten $ pretty $ flatten $ ppr argexp) <> ","+          <+/> "as"+          <+> fname'+          <+> "only takes"+          <+> ppr prev_applied+          <+> arguments <> "."+  where+    arguments+      | prev_applied == 1 = "argument"+      | otherwise = "arguments"++isInt64 :: Exp -> Maybe Int64+isInt64 (Literal (SignedValue (Int64Value k')) _) = Just $ fromIntegral k'+isInt64 (IntLit k' _ _) = Just $ fromInteger k'+isInt64 (Negate x _) = negate <$> isInt64 x+isInt64 _ = Nothing++maybeDimFromExp :: Exp -> Maybe (DimDecl VName)+maybeDimFromExp (Var v _ _) = Just $ NamedDim v+maybeDimFromExp (Parens e _) = maybeDimFromExp e+maybeDimFromExp (QualParens _ e _) = maybeDimFromExp e+maybeDimFromExp e = ConstDim . fromIntegral <$> isInt64 e++dimFromExp :: (Exp -> SizeSource) -> Exp -> TermTypeM (DimDecl VName, Maybe VName)+dimFromExp rf (Parens e _) = dimFromExp rf e+dimFromExp rf (QualParens _ e _) = dimFromExp rf e+dimFromExp rf e+  | Just d <- maybeDimFromExp e =+    return (d, Nothing)+  | otherwise =+    extSize (srclocOf e) $ rf e++dimFromArg :: Maybe (QualName VName) -> Exp -> TermTypeM (DimDecl VName, Maybe VName)+dimFromArg fname = dimFromExp $ SourceArg (FName fname) . bareExp++-- | @returnType ret_type arg_diet arg_type@ gives result of applying+-- an argument the given types to a function with the given return+-- type, consuming the argument with the given diet.+returnType ::+  PatternType ->+  Diet ->+  PatternType ->+  PatternType+returnType (Array _ Unique et shape) _ _ =+  Array mempty Unique et shape+returnType (Array als Nonunique et shape) d arg =+  Array (als <> arg_als) Unique et shape -- Intentional!+  where+    arg_als = aliases $ maskAliases arg d+returnType (Scalar (Record fs)) d arg =+  Scalar $ Record $ fmap (\et -> returnType et d arg) fs+returnType (Scalar (Prim t)) _ _ =+  Scalar $ Prim t+returnType (Scalar (TypeVar _ Unique t targs)) _ _ =+  Scalar $ TypeVar mempty Unique t targs+returnType (Scalar (TypeVar als Nonunique t targs)) d arg =+  Scalar $ TypeVar (als <> arg_als) Unique t targs -- Intentional!+  where+    arg_als = aliases $ maskAliases arg d+returnType (Scalar (Arrow old_als v t1 t2)) d arg =+  Scalar $ Arrow als v (t1 `setAliases` mempty) (t2 `setAliases` als)+  where+    -- Make sure to propagate the aliases of an existing closure.+    als = old_als <> aliases (maskAliases arg d)+returnType (Scalar (Sum cs)) d arg =+  Scalar $ Sum $ (fmap . fmap) (\et -> returnType et d arg) cs++-- | @t `maskAliases` d@ removes aliases (sets them to 'mempty') from+-- the parts of @t@ that are denoted as consumed by the 'Diet' @d@.+maskAliases ::+  Monoid as =>+  TypeBase shape as ->+  Diet ->+  TypeBase shape as+maskAliases t Consume = t `setAliases` mempty+maskAliases t Observe = t+maskAliases (Scalar (Record ets)) (RecordDiet ds) =+  Scalar $ Record $ M.intersectionWith maskAliases ets ds+maskAliases t FuncDiet {} = t+maskAliases _ _ = error "Invalid arguments passed to maskAliases."++consumeArg :: SrcLoc -> PatternType -> Diet -> TermTypeM [Occurence]+consumeArg loc (Scalar (Record ets)) (RecordDiet ds) =+  concat . M.elems <$> traverse (uncurry $ consumeArg loc) (M.intersectionWith (,) ets ds)+consumeArg loc (Array _ Nonunique _ _) Consume =+  typeError loc mempty "Consuming parameter passed non-unique argument."+consumeArg loc (Scalar (TypeVar _ Nonunique _ _)) Consume =+  typeError loc mempty "Consuming parameter passed non-unique argument."+consumeArg loc (Scalar (Arrow _ _ t1 _)) (FuncDiet d _)+  | not $ contravariantArg t1 d =+    typeError loc mempty "Non-consuming higher-order parameter passed consuming argument."+  where+    contravariantArg (Array _ Unique _ _) Observe =+      False+    contravariantArg (Scalar (TypeVar _ Unique _ _)) Observe =+      False+    contravariantArg (Scalar (Record ets)) (RecordDiet ds) =+      and (M.intersectionWith contravariantArg ets ds)+    contravariantArg (Scalar (Arrow _ _ tp tr)) (FuncDiet dp dr) =+      contravariantArg tp dp && contravariantArg tr dr+    contravariantArg _ _ =+      True+consumeArg loc (Scalar (Arrow _ _ _ t2)) (FuncDiet _ pd) =+  consumeArg loc t2 pd+consumeArg loc at Consume = return [consumption (aliases at) loc]+consumeArg loc at _ = return [observation (aliases at) loc]++-- | Type-check a single expression in isolation.  This expression may+-- turn out to be polymorphic, in which case the list of type+-- parameters will be non-empty.+checkOneExp :: UncheckedExp -> TypeM ([TypeParam], Exp)+checkOneExp e = fmap fst . runTermTypeM $ do+  e' <- checkExp e+  let t = toStruct $ typeOf e'+  (tparams, _, _, _) <-+    letGeneralise (nameFromString "<exp>") (srclocOf e) [] [] t+  fixOverloadedTypes $ typeVars t+  e'' <- updateTypes e'+  checkUnmatched e''+  causalityCheck e''+  literalOverflowCheck e''+  return (tparams, e'')++-- Verify that all sum type constructors and empty array literals have+-- a size that is known (rigid or a type parameter).  This is to+-- ensure that we can actually determine their shape at run-time.+causalityCheck :: Exp -> TermTypeM ()+causalityCheck binding_body = do+  constraints <- getConstraints++  let checkCausality what known t loc+        | (d, dloc) : _ <-+            mapMaybe (unknown constraints known) $+              S.toList $ typeDimNames $ toStruct t =+          Just $ lift $ causality what loc d dloc t+        | otherwise = Nothing++      checkParamCausality known p =+        checkCausality (ppr p) known (patternType p) (srclocOf p)++      onExp ::+        S.Set VName ->+        Exp ->+        StateT (S.Set VName) (Either TypeError) Exp++      onExp known (Var v (Info t) loc)+        | Just bad <- checkCausality (pquote (ppr v)) known t loc =+          bad+      onExp known (ArrayLit [] (Info t) loc)+        | Just bad <- checkCausality "empty array" known t loc =+          bad+      onExp known (Lambda params _ _ _ _)+        | bad : _ <- mapMaybe (checkParamCausality known) params =+          bad+      onExp known e@(Coerce what _ (_, Info ext) _) = do+        modify (S.fromList ext <>)+        void $ onExp known what+        return e+      onExp known e@(LetPat _ bindee_e body_e (_, Info ext) _) = do+        sequencePoint known bindee_e body_e ext+        return e+      onExp known e@(Apply f arg (Info (_, p)) (_, Info ext) _) = do+        sequencePoint known arg f $ maybeToList p ++ ext+        return e+      onExp+        known+        e@( BinOp+              (f, floc)+              ft+              (x, Info (_, xp))+              (y, Info (_, yp))+              _+              (Info ext)+              _+            ) = do+          args_known <-+            lift $+              execStateT (sequencePoint known x y $ catMaybes [xp, yp]) mempty+          void $ onExp (args_known <> known) (Var f ft floc)+          modify ((args_known <> S.fromList ext) <>)+          return e+      onExp known e = do+        recurse known e++        case e of+          DoLoop _ _ _ _ _ (Info (_, ext)) _ ->+            modify (<> S.fromList ext)+          If _ _ _ (_, Info ext) _ ->+            modify (<> S.fromList ext)+          Index _ _ (_, Info ext) _ ->+            modify (<> S.fromList ext)+          Match _ _ (_, Info ext) _ ->+            modify (<> S.fromList ext)+          Range _ _ _ (_, Info ext) _ ->+            modify (<> S.fromList ext)+          _ ->+            return ()++        return e++      recurse known = void . astMap mapper+        where+          mapper = identityMapper {mapOnExp = onExp known}++      sequencePoint known x y ext = do+        new_known <- lift $ execStateT (onExp known x) mempty+        void $ onExp (new_known <> known) y+        modify ((new_known <> S.fromList ext) <>)++  either throwError (const $ return ()) $+    evalStateT (onExp mempty binding_body) mempty+  where+    unknown constraints known v = do+      guard $ v `S.notMember` known+      loc <- unknowable constraints v+      return (v, loc)++    unknowable constraints v =+      case snd <$> M.lookup v constraints of+        Just (UnknowableSize loc _) -> Just loc+        _ -> Nothing++    causality what loc d dloc t =+      Left $+        TypeError loc mempty $+          "Causality check: size" <+/> pquote (pprName d)+            <+/> "needed for type of"+            <+> what <> colon+            </> indent 2 (ppr t)+            </> "But"+            <+> pquote (pprName d)+            <+> "is computed at"+            <+/> text (locStrRel loc dloc) <> "."+            </> ""+            </> "Hint:"+            <+> align+              ( textwrap "Bind the expression producing" <+> pquote (pprName d)+                  <+> "with 'let' beforehand."+              )++-- | Traverse the expression, emitting warnings if any of the literals overflow+-- their inferred types+--+-- Note: currently unable to detect float underflow (such as 1e-400 -> 0)+literalOverflowCheck :: Exp -> TermTypeM ()+literalOverflowCheck = void . check+  where+    check e@(IntLit x ty loc) =+      e <$ case ty of+        Info (Scalar (Prim t)) -> warnBounds (inBoundsI x t) x t loc+        _ -> error "Inferred type of int literal is not a number"+    check e@(FloatLit x ty loc) =+      e <$ case ty of+        Info (Scalar (Prim (FloatType t))) -> warnBounds (inBoundsF x t) x t loc+        _ -> error "Inferred type of float literal is not a float"+    check e@(Negate (IntLit x ty loc1) loc2) =+      e <$ case ty of+        Info (Scalar (Prim t)) -> warnBounds (inBoundsI (- x) t) (- x) t (loc1 <> loc2)+        _ -> error "Inferred type of int literal is not a number"+    check e = astMap identityMapper {mapOnExp = check} e+    bitWidth ty = 8 * intByteSize ty :: Int+    inBoundsI x (Signed t) = x >= -2 ^ (bitWidth t - 1) && x < 2 ^ (bitWidth t - 1)+    inBoundsI x (Unsigned t) = x >= 0 && x < 2 ^ bitWidth t+    inBoundsI x (FloatType Float32) = not $ isInfinite (fromIntegral x :: Float)+    inBoundsI x (FloatType Float64) = not $ isInfinite (fromIntegral x :: Double)+    inBoundsI _ Bool = error "Inferred type of int literal is not a number"+    inBoundsF x Float32 = not $ isInfinite (realToFrac x :: Float)+    inBoundsF x Float64 = not $ isInfinite x+    warnBounds inBounds x ty loc =+      unless inBounds $+        typeError loc mempty $+          "Literal " <> ppr x+            <> " out of bounds for inferred type "+            <> ppr ty+            <> "."++-- | Type-check a top-level (or module-level) function definition.+-- Despite the name, this is also used for checking constant+-- definitions, by treating them as 0-ary functions.+checkFunDef ::+  ( Name,+    Maybe UncheckedTypeExp,+    [UncheckedTypeParam],+    [UncheckedPattern],+    UncheckedExp,+    SrcLoc+  ) ->+  TypeM+    ( VName,+      [TypeParam],+      [Pattern],+      Maybe (TypeExp VName),+      StructType,+      [VName],+      Exp+    )+checkFunDef (fname, maybe_retdecl, tparams, params, body, loc) =+  fmap fst $+    runTermTypeM $ do+      (tparams', params', maybe_retdecl', rettype', retext, body') <-+        checkBinding (fname, maybe_retdecl, tparams, params, body, loc)++      -- Since this is a top-level function, we also resolve overloaded+      -- types, using either defaults or complaining about ambiguities.+      fixOverloadedTypes $+        typeVars rettype' <> foldMap (typeVars . patternType) params'++      -- Then replace all inferred types in the body and parameters.+      body'' <- updateTypes body'+      params'' <- updateTypes params'+      maybe_retdecl'' <- traverse updateTypes maybe_retdecl'+      rettype'' <- normTypeFully rettype'++      -- Check if pattern matches are exhaustive and yield+      -- errors if not.+      checkUnmatched body''++      -- Check if the function body can actually be evaluated.+      causalityCheck body''++      literalOverflowCheck body''++      bindSpaced [(Term, fname)] $ do+        fname' <- checkName Term fname loc+        when (nameToString fname `elem` doNotShadow) $+          typeError loc mempty $+            "The" <+> pprName fname <+> "operator may not be redefined."++        return (fname', tparams', params'', maybe_retdecl'', rettype'', retext, body'')++-- | This is "fixing" as in "setting them", not "correcting them".  We+-- only make very conservative fixing.+fixOverloadedTypes :: Names -> TermTypeM ()+fixOverloadedTypes tyvars_at_toplevel =+  getConstraints >>= mapM_ fixOverloaded . M.toList . M.map snd+  where+    fixOverloaded (v, Overloaded ots usage)+      | Signed Int32 `elem` ots = do+        unify usage (Scalar (TypeVar () Nonunique (typeName v) [])) $+          Scalar $ Prim $ Signed Int32+        when (v `S.member` tyvars_at_toplevel) $+          warn usage "Defaulting ambiguous type to i32."+      | FloatType Float64 `elem` ots = do+        unify usage (Scalar (TypeVar () Nonunique (typeName v) [])) $+          Scalar $ Prim $ FloatType Float64+        when (v `S.member` tyvars_at_toplevel) $+          warn usage "Defaulting ambiguous type to f64."+      | otherwise =+        typeError usage mempty $+          "Type is ambiguous (could be one of" <+> commasep (map ppr ots) <> ")."+            </> "Add a type annotation to disambiguate the type."+    fixOverloaded (_, NoConstraint _ usage) =+      typeError usage mempty $+        "Type of expression is ambiguous."+          </> "Add a type annotation to disambiguate the type."+    fixOverloaded (_, Equality usage) =+      typeError usage mempty $+        "Type is ambiguous (must be equality type)."+          </> "Add a type annotation to disambiguate the type."+    fixOverloaded (_, HasFields fs usage) =+      typeError usage mempty $+        "Type is ambiguous.  Must be record with fields:"+          </> indent 2 (stack $ map field $ M.toList fs)+          </> "Add a type annotation to disambiguate the type."+      where+        field (l, t) = ppr l <> colon <+> align (ppr t)+    fixOverloaded (_, HasConstrs cs usage) =+      typeError usage mempty $+        "Type is ambiguous (must be a sum type with constructors:"+          <+> ppr (Sum cs) <> ")."+          </> "Add a type annotation to disambiguate the type."+    fixOverloaded (_, Size Nothing usage) =+      typeError usage mempty "Size is ambiguous."+    fixOverloaded _ = return ()++hiddenParamNames :: [Pattern] -> Names+hiddenParamNames params = hidden+  where+    param_all_names = mconcat $ map patternNames params+    named (Named x, _) = Just x+    named (Unnamed, _) = Nothing+    param_names =+      S.fromList $ mapMaybe (named . patternParam) params+    hidden = param_all_names `S.difference` param_names++inferredReturnType :: SrcLoc -> [Pattern] -> PatternType -> TermTypeM StructType+inferredReturnType loc params t =+  -- The inferred type may refer to names that are bound by the+  -- parameter patterns, but which will not be visible in the type.+  -- These we must turn into fresh type variables, which will be+  -- existential in the return type.+  fmap (toStruct . fst) $+    unscopeType+      loc+      (M.filterWithKey (const . (`S.member` hidden)) $ foldMap patternMap params)+      $ inferReturnUniqueness params t+  where+    hidden = hiddenParamNames params++checkBinding ::+  ( Name,+    Maybe UncheckedTypeExp,+    [UncheckedTypeParam],+    [UncheckedPattern],+    UncheckedExp,+    SrcLoc+  ) ->+  TermTypeM+    ( [TypeParam],+      [Pattern],+      Maybe (TypeExp VName),+      StructType,+      [VName],+      Exp+    )+checkBinding (fname, maybe_retdecl, tparams, params, body, loc) =+  noUnique $+    incLevel $+      bindingParams tparams params $ \tparams' params' -> do+        when (null params && any isSizeParam tparams) $+          typeError+            loc+            mempty+            "Size parameters are only allowed on bindings that also have value parameters."++        maybe_retdecl' <- forM maybe_retdecl $ \retdecl -> do+          (retdecl', ret_nodims, _) <- checkTypeExp retdecl+          (ret, _) <- instantiateEmptyArrayDims loc "funret" Nonrigid ret_nodims+          return (retdecl', ret)++        body' <-+          checkFunBody+            params'+            body+            (snd <$> maybe_retdecl')+            (maybe loc srclocOf maybe_retdecl)++        params'' <- mapM updateTypes params'+        body_t <- expTypeFully body'++        (maybe_retdecl'', rettype) <- case maybe_retdecl' of+          Just (retdecl', ret) -> do+            let rettype_structural = toStructural ret+            checkReturnAlias rettype_structural params'' body_t++            when (null params) $ nothingMustBeUnique loc rettype_structural++            ret' <- normTypeFully ret++            return (Just retdecl', ret')+          Nothing+            | null params ->+              return (Nothing, toStruct $ body_t `setUniqueness` Nonunique)+            | otherwise -> do+              body_t' <- inferredReturnType loc params'' body_t+              return (Nothing, body_t')++        verifyFunctionParams (Just fname) params''++        (tparams'', params''', rettype'', retext) <-+          letGeneralise fname loc tparams' params'' rettype++        checkGlobalAliases params'' body_t loc++        return (tparams'', params''', maybe_retdecl'', rettype'', retext, body')+  where+    checkReturnAlias rettp params' =+      foldM_ (checkReturnAlias' params') S.empty . returnAliasing rettp+    checkReturnAlias' params' seen (Unique, names)+      | any (`S.member` S.map snd seen) $ S.toList names =+        uniqueReturnAliased fname loc+      | otherwise = do+        notAliasingParam params' names+        return $ seen `S.union` tag Unique names+    checkReturnAlias' _ seen (Nonunique, names)+      | any (`S.member` seen) $ S.toList $ tag Unique names =+        uniqueReturnAliased fname loc+      | otherwise = return $ seen `S.union` tag Nonunique names++    notAliasingParam params' names =+      forM_ params' $ \p ->+        let consumedNonunique p' =+              not (unique $ unInfo $ identType p') && (identName p' `S.member` names)+         in case find consumedNonunique $ S.toList $ patternIdents p of+              Just p' ->+                returnAliased fname (baseName $ identName p') loc+              Nothing ->+                return ()++    tag u = S.map (u,)++    returnAliasing (Scalar (Record ets1)) (Scalar (Record ets2)) =+      concat $ M.elems $ M.intersectionWith returnAliasing ets1 ets2+    returnAliasing expected got =+      [(uniqueness expected, S.map aliasVar $ aliases got)]++-- | Extract all the shape names that occur in positive position+-- (roughly, left side of an arrow) in a given type.+typeDimNamesPos :: TypeBase (DimDecl VName) als -> S.Set VName+typeDimNamesPos (Scalar (Arrow _ _ t1 t2)) = onParam t1 <> typeDimNamesPos t2+  where+    onParam :: TypeBase (DimDecl VName) als -> S.Set VName+    onParam (Scalar Arrow {}) = mempty+    onParam (Scalar (Record fs)) = mconcat $ map onParam $ M.elems fs+    onParam (Scalar (TypeVar _ _ _ targs)) = mconcat $ map onTypeArg targs+    onParam t = typeDimNames t+    onTypeArg (TypeArgDim (NamedDim d) _) = S.singleton $ qualLeaf d+    onTypeArg (TypeArgDim _ _) = mempty+    onTypeArg (TypeArgType t _) = onParam t+typeDimNamesPos _ = mempty++checkGlobalAliases :: [Pattern] -> PatternType -> SrcLoc -> TermTypeM ()+checkGlobalAliases params body_t loc = do+  vtable <- asks $ scopeVtable . termScope+  let isLocal v = case v `M.lookup` vtable of+        Just (BoundV Local _ _) -> True+        _ -> False+  let als =+        filter (not . isLocal) $+          S.toList $+            boundArrayAliases body_t+              `S.difference` foldMap patternNames params+  case als of+    v : _+      | not $ null params ->+        typeError loc mempty $+          "Function result aliases the free variable "+            <> pquote (pprName v)+            <> "."+            </> "Use" <+> pquote "copy" <+> "to break the aliasing."+    _ ->+      return ()++inferReturnUniqueness :: [Pattern] -> PatternType -> PatternType+inferReturnUniqueness params t =+  let forbidden = aliasesMultipleTimes t+      uniques = uniqueParamNames params+      delve (Scalar (Record fs)) =+        Scalar $ Record $ M.map delve fs+      delve t'+        | all (`S.member` uniques) (boundArrayAliases t'),+          not $ any ((`S.member` forbidden) . aliasVar) (aliases t') =+          t'+        | otherwise =+          t' `setUniqueness` Nonunique+   in delve t++-- An alias inhibits uniqueness if it is used in disjoint values.+aliasesMultipleTimes :: PatternType -> Names+aliasesMultipleTimes = S.fromList . map fst . filter ((> 1) . snd) . M.toList . delve+  where+    delve (Scalar (Record fs)) =+      foldl' (M.unionWith (+)) mempty $ map delve $ M.elems fs+    delve t =+      M.fromList $ zip (map aliasVar $ S.toList (aliases t)) $ repeat (1 :: Int)++uniqueParamNames :: [Pattern] -> Names+uniqueParamNames =+  S.map identName+    . S.filter (unique . unInfo . identType)+    . foldMap patternIdents++boundArrayAliases :: PatternType -> S.Set VName+boundArrayAliases (Array als _ _ _) = boundAliases als+boundArrayAliases (Scalar Prim {}) = mempty+boundArrayAliases (Scalar (Record fs)) = foldMap boundArrayAliases fs+boundArrayAliases (Scalar (TypeVar als _ _ _)) = boundAliases als+boundArrayAliases (Scalar Arrow {}) = mempty+boundArrayAliases (Scalar (Sum fs)) =+  mconcat $ concatMap (map boundArrayAliases) $ M.elems fs++-- | The set of in-scope variables that are being aliased.+boundAliases :: Aliasing -> S.Set VName+boundAliases = S.map aliasVar . S.filter bound+  where+    bound AliasBound {} = True+    bound AliasFree {} = False++nothingMustBeUnique :: SrcLoc -> TypeBase () () -> TermTypeM ()+nothingMustBeUnique loc = check+  where+    check (Array _ Unique _ _) = bad+    check (Scalar (TypeVar _ Unique _ _)) = bad+    check (Scalar (Record fs)) = mapM_ check fs+    check (Scalar (Sum fs)) = mapM_ (mapM_ check) fs+    check _ = return ()+    bad = typeError loc mempty "A top-level constant cannot have a unique type."++-- | Verify certain restrictions on function parameters, and bail out+-- on dubious constructions.+--+-- These restrictions apply to all functions (anonymous or otherwise).+-- Top-level functions have further restrictions that are checked+-- during let-generalisation.+verifyFunctionParams :: Maybe Name -> [Pattern] -> TermTypeM ()+verifyFunctionParams fname params =+  onFailure (CheckingParams fname) $+    verifyParams (foldMap patternNames params) =<< mapM updateTypes params+  where+    verifyParams forbidden (p : ps)+      | d : _ <- S.toList $ patternDimNames p `S.intersection` forbidden =+        typeError p mempty $+          "Parameter" <+> pquote (ppr p)+            <+/> "refers to size" <+> pquote (pprName d)+            <> comma+            <+/> textwrap "which will not be accessible to the caller"+            <> comma+            <+/> textwrap "possibly because it is nested in a tuple or record."+            <+/> textwrap "Consider ascribing an explicit type that does not reference "+            <> pquote (pprName d)+            <> "."+      | otherwise = verifyParams forbidden' ps+      where+        forbidden' =+          case patternParam p of+            (Named v, _) -> forbidden `S.difference` S.singleton v+            _ -> forbidden+    verifyParams _ [] = return ()++-- Returns the sizes of the immediate type produced,+-- the sizes of parameter types, and the sizes of return types.+dimUses :: StructType -> (Names, Names, Names)+dimUses = execWriter . traverseDims f+  where+    f _ PosImmediate (NamedDim v) = tell (S.singleton (qualLeaf v), mempty, mempty)+    f _ PosParam (NamedDim v) = tell (mempty, S.singleton (qualLeaf v), mempty)+    f _ PosReturn (NamedDim v) = tell (mempty, mempty, S.singleton (qualLeaf v))+    f _ _ _ = return ()++-- | Find at all type variables in the given type that are covered by+-- the constraints, and produce type parameters that close over them.+--+-- The passed-in list of type parameters is always prepended to the+-- produced list of type parameters.+closeOverTypes ::+  Name ->+  SrcLoc ->+  [TypeParam] ->+  [StructType] ->+  StructType ->+  Constraints ->+  TermTypeM ([TypeParam], StructType, [VName])+closeOverTypes defname defloc tparams paramts ret substs = do+  (more_tparams, retext) <-+    partitionEithers . catMaybes+      <$> mapM closeOver (M.toList $ M.map snd to_close_over)+  let retToAnyDim v = do+        guard $ v `S.member` ret_sizes+        UnknowableSize {} <- snd <$> M.lookup v substs+        Just $ SizeSubst AnyDim+  return+    ( tparams ++ more_tparams,+      applySubst retToAnyDim ret,+      retext+    )+  where+    t = foldFunType paramts ret+    to_close_over = M.filterWithKey (\k _ -> k `S.member` visible) substs+    visible = typeVars t <> typeDimNames t++    (produced_sizes, param_sizes, ret_sizes) = dimUses t++    -- Avoid duplicate type parameters.+    closeOver (k, _)+      | k `elem` map typeParamName tparams =+        return Nothing+    closeOver (k, NoConstraint l usage) =+      return $ Just $ Left $ TypeParamType l k $ srclocOf usage+    closeOver (k, ParamType l loc) =+      return $ Just $ Left $ TypeParamType l k loc+    closeOver (k, Size Nothing usage) =+      return $ Just $ Left $ TypeParamDim k $ srclocOf usage+    closeOver (k, UnknowableSize _ _)+      | k `S.member` param_sizes = do+        notes <- dimNotes defloc $ NamedDim $ qualName k+        typeError defloc notes $+          "Unknowable size" <+> pquote (pprName k)+            <+> "imposes constraint on type of"+            <+> pquote (pprName defname)+            <> ", which is inferred as:"+            </> indent 2 (ppr t)+      | k `S.member` produced_sizes =+        return $ Just $ Right k+    closeOver (_, _) =+      return Nothing++letGeneralise ::+  Name ->+  SrcLoc ->+  [TypeParam] ->+  [Pattern] ->+  StructType ->+  TermTypeM ([TypeParam], [Pattern], StructType, [VName])+letGeneralise defname defloc tparams params rettype =+  onFailure (CheckingLetGeneralise defname) $ do+    now_substs <- getConstraints++    -- Candidates for let-generalisation are those type variables that+    --+    -- (1) were not known before we checked this function, and+    --+    -- (2) are not used in the (new) definition of any type variables+    -- known before we checked this function.+    --+    -- (3) are not referenced from an overloaded type (for example,+    -- are the element types of an incompletely resolved record type).+    -- This is a bit more restrictive than I'd like, and SML for+    -- example does not have this restriction.+    --+    -- Criteria (1) and (2) is implemented by looking at the binding+    -- level of the type variables.+    let keep_type_vars = overloadedTypeVars now_substs++    cur_lvl <- curLevel+    let candidate k (lvl, _) = (k `S.notMember` keep_type_vars) && lvl >= cur_lvl+        new_substs = M.filterWithKey candidate now_substs++    (tparams', rettype', retext) <-+      closeOverTypes+        defname+        defloc+        tparams+        (map patternStructType params)+        rettype+        new_substs++    rettype'' <- updateTypes rettype'++    let used_sizes =+          foldMap typeDimNames $+            rettype'' : map patternStructType params+    case filter ((`S.notMember` used_sizes) . typeParamName) $+      filter isSizeParam tparams' of+      [] -> return ()+      tp : _ ->+        typeError defloc mempty $+          "Size parameter" <+> pquote (ppr tp) <+> "unused."++    -- We keep those type variables that were not closed over by+    -- let-generalisation.+    modifyConstraints $ M.filterWithKey $ \k _ -> k `notElem` map typeParamName tparams'++    return (tparams', params, rettype'', retext)++checkFunBody ::+  [Pattern] ->+  UncheckedExp ->+  Maybe StructType ->+  SrcLoc ->+  TermTypeM Exp+checkFunBody params body maybe_rettype loc = do+  body' <- noSizeEscape $ checkExp body++  -- Unify body return type with return annotation, if one exists.+  case maybe_rettype of+    Just rettype -> do+      (rettype_withdims, _) <- instantiateEmptyArrayDims loc "impl" Nonrigid rettype++      body_t <- expTypeFully body'+      -- We need to turn any sizes provided by "hidden" parameter+      -- names into existential sizes instead.+      let hidden = hiddenParamNames params+      (body_t', _) <-+        unscopeType+          loc+          ( M.filterWithKey (const . (`S.member` hidden)) $+              foldMap patternMap params+          )+          body_t++      let usage = mkUsage (srclocOf body) "return type annotation"+      onFailure (CheckingReturn rettype (toStruct body_t')) $+        expect usage rettype_withdims $ toStruct body_t'++      -- We also have to make sure that uniqueness matches.  This is done+      -- explicitly, because uniqueness is ignored by unification.+      rettype' <- normTypeFully rettype+      body_t'' <- normTypeFully rettype -- Substs may have changed.+      unless (body_t'' `subtypeOf` anySizes rettype') $+        typeError (srclocOf body) mempty $+          "Body type" </> indent 2 (ppr body_t'')+            </> "is not a subtype of annotated type"+            </> indent 2 (ppr rettype')+    Nothing -> return ()++  return body'++--- Consumption++occur :: Occurences -> TermTypeM ()+occur = tell++-- | Proclaim that we have made read-only use of the given variable.+observe :: Ident -> TermTypeM ()+observe (Ident nm (Info t) loc) =+  let als = AliasBound nm `S.insert` aliases t+   in occur [observation als loc]++checkIfConsumable :: SrcLoc -> Aliasing -> TermTypeM ()+checkIfConsumable loc als = do+  vtable <- asks $ scopeVtable . termScope+  let consumable v = case M.lookup v vtable of+        Just (BoundV Local _ t)+          | arrayRank t > 0 -> unique t+          | Scalar TypeVar {} <- t -> unique t+          | otherwise -> True+        _ -> False+  case filter (not . consumable) $ map aliasVar $ S.toList als of+    v : _ ->+      typeError loc mempty $+        "Would consume variable" <+> pquote (pprName v)+          <> ", which is not allowed."+    [] -> return ()++-- | Proclaim that we have written to the given variable.+consume :: SrcLoc -> Aliasing -> TermTypeM ()+consume loc als = do+  checkIfConsumable loc als+  occur [consumption als loc]++-- | Proclaim that we have written to the given variable, and mark+-- accesses to it and all of its aliases as invalid inside the given+-- computation.+consuming :: Ident -> TermTypeM a -> TermTypeM a+consuming (Ident name (Info t) loc) m = do+  consume loc $ AliasBound name `S.insert` aliases t+  localScope consume' m+  where+    consume' scope =+      scope {scopeVtable = M.insert name (WasConsumed loc) $ scopeVtable scope}++collectOccurences :: TermTypeM a -> TermTypeM (a, Occurences)+collectOccurences m = pass $ do+  (x, dataflow) <- listen m+  return ((x, dataflow), const mempty)++tapOccurences :: TermTypeM a -> TermTypeM (a, Occurences)+tapOccurences = listen++removeSeminullOccurences :: TermTypeM a -> TermTypeM a+removeSeminullOccurences = censor $ filter $ not . seminullOccurence++checkIfUsed :: Occurences -> Ident -> TermTypeM ()+checkIfUsed occs v+  | not $ identName v `S.member` allOccuring occs,+    not $ "_" `isPrefixOf` prettyName (identName v) =+    warn (srclocOf v) $ "Unused variable " ++ quote (pretty $ baseName $ identName v) ++ "."+  | otherwise =+    return ()++alternative :: TermTypeM a -> TermTypeM b -> TermTypeM (a, b)+alternative m1 m2 = pass $ do+  (x, occurs1) <- listen $ noSizeEscape m1+  (y, occurs2) <- listen $ noSizeEscape m2+  checkOccurences occurs1+  checkOccurences occurs2+  let usage = occurs1 `altOccurences` occurs2+  return ((x, y), const usage)++-- | Make all bindings nonunique.+noUnique :: TermTypeM a -> TermTypeM a+noUnique = localScope (\scope -> scope {scopeVtable = M.map set $ scopeVtable scope})+  where+    set (BoundV l tparams t) = BoundV l tparams $ t `setUniqueness` Nonunique+    set (OverloadedF ts pts rt) = OverloadedF ts pts rt+    set EqualityF = EqualityF+    set (WasConsumed loc) = WasConsumed loc++onlySelfAliasing :: TermTypeM a -> TermTypeM a+onlySelfAliasing = localScope (\scope -> scope {scopeVtable = M.mapWithKey set $ scopeVtable scope})+  where+    set k (BoundV l tparams t) =+      BoundV l tparams $+        t `addAliases` S.intersection (S.singleton (AliasBound k))+    set _ (OverloadedF ts pts rt) = OverloadedF ts pts rt+    set _ EqualityF = EqualityF+    set _ (WasConsumed loc) = WasConsumed loc++arrayOfM ::+  (Pretty (ShapeDecl dim), Monoid as) =>+  SrcLoc ->+  TypeBase dim as ->+  ShapeDecl dim ->+  Uniqueness ->+  TermTypeM (TypeBase dim as)+arrayOfM loc t shape u = do+  zeroOrderType (mkUsage loc "use as array element") "type used in array" t+  return $ arrayOf t shape u++updateTypes :: ASTMappable e => e -> TermTypeM e+updateTypes = astMap tv+  where+    tv =+      ASTMapper+        { mapOnExp = astMap tv,+          mapOnName = pure,+          mapOnQualName = pure,+          mapOnStructType = normTypeFully,+          mapOnPatternType = normTypeFully+        }
src/Language/Futhark/TypeChecker/Types.hs view
@@ -2,26 +2,23 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Safe #-}+ -- | Type checker building blocks that do not involve unification. module Language.Futhark.TypeChecker.Types-  ( checkTypeExp-  , checkTypeDecl--  , unifyTypesU-  , subtypeOf-  , subuniqueOf--  , checkForDuplicateNames-  , checkTypeParams-  , typeParamToArg--  , TypeSub(..)-  , TypeSubs-  , substituteTypes--  , Subst(..)-  , Substitutable(..)-  , substTypesAny+  ( checkTypeExp,+    checkTypeDecl,+    unifyTypesU,+    subtypeOf,+    subuniqueOf,+    checkForDuplicateNames,+    checkTypeParams,+    typeParamToArg,+    TypeSub (..),+    TypeSubs,+    substituteTypes,+    Subst (..),+    Substitutable (..),+    substTypesAny,   ) where @@ -29,57 +26,70 @@ import Control.Monad.Reader import Control.Monad.State import Data.Bifunctor-import Data.List (foldl', sort, nub)-import Data.Maybe+import Data.List (foldl', nub, sort) import qualified Data.Map.Strict as M-+import Data.Maybe+import Futhark.Util.Pretty import Language.Futhark-import Language.Futhark.TypeChecker.Monad import Language.Futhark.Traversals-import Futhark.Util.Pretty+import Language.Futhark.TypeChecker.Monad  -- | @unifyTypes uf t1 t2@ attempts to unify @t1@ and @t2@.  If -- unification cannot happen, 'Nothing' is returned, otherwise a type -- that combines the aliasing of @t1@ and @t2@ is returned. -- Uniqueness is unified with @uf@.-unifyTypesU :: (Monoid als, ArrayDim dim) =>-               (Uniqueness -> Uniqueness -> Maybe Uniqueness)-            -> TypeBase dim als -> TypeBase dim als -> Maybe (TypeBase dim als)+unifyTypesU ::+  (Monoid als, ArrayDim dim) =>+  (Uniqueness -> Uniqueness -> Maybe Uniqueness) ->+  TypeBase dim als ->+  TypeBase dim als ->+  Maybe (TypeBase dim als) unifyTypesU uf (Array als1 u1 et1 shape1) (Array als2 u2 et2 shape2) =   Array (als1 <> als2) <$> uf u1 u2-  <*> unifyScalarTypes uf et1 et2 <*> unifyShapes shape1 shape2+    <*> unifyScalarTypes uf et1 et2+    <*> unifyShapes shape1 shape2 unifyTypesU uf (Scalar t1) (Scalar t2) = Scalar <$> unifyScalarTypes uf t1 t2 unifyTypesU _ _ _ = Nothing -unifyScalarTypes :: (Monoid als, ArrayDim dim) =>-                    (Uniqueness -> Uniqueness -> Maybe Uniqueness)-                 -> ScalarTypeBase dim als -> ScalarTypeBase dim als -> Maybe (ScalarTypeBase dim als)+unifyScalarTypes ::+  (Monoid als, ArrayDim dim) =>+  (Uniqueness -> Uniqueness -> Maybe Uniqueness) ->+  ScalarTypeBase dim als ->+  ScalarTypeBase dim als ->+  Maybe (ScalarTypeBase dim als) unifyScalarTypes _ (Prim t1) (Prim t2)-  | t1 == t2  = Just $ Prim t1+  | t1 == t2 = Just $ Prim t1   | otherwise = Nothing unifyScalarTypes uf (TypeVar als1 u1 t1 targs1) (TypeVar als2 u2 t2 targs2)   | t1 == t2 = do-      u3 <- uf u1 u2-      targs3 <- zipWithM (unifyTypeArgs uf) targs1 targs2-      Just $ TypeVar (als1 <> als2) u3 t1 targs3+    u3 <- uf u1 u2+    targs3 <- zipWithM (unifyTypeArgs uf) targs1 targs2+    Just $ TypeVar (als1 <> als2) u3 t1 targs3   | otherwise = Nothing unifyScalarTypes uf (Record ts1) (Record ts2)   | length ts1 == length ts2,     sort (M.keys ts1) == sort (M.keys ts2) =-      Record <$> traverse (uncurry (unifyTypesU uf))-      (M.intersectionWith (,) ts1 ts2)+    Record+      <$> traverse+        (uncurry (unifyTypesU uf))+        (M.intersectionWith (,) ts1 ts2) unifyScalarTypes uf (Arrow as1 mn1 t1 t1') (Arrow as2 _ t2 t2') =   Arrow (as1 <> as2) mn1 <$> unifyTypesU (flip uf) t1 t2 <*> unifyTypesU uf t1' t2' unifyScalarTypes uf (Sum cs1) (Sum cs2)   | length cs1 == length cs2,     sort (M.keys cs1) == sort (M.keys cs2) =-      Sum <$> traverse (uncurry (zipWithM (unifyTypesU uf)))-      (M.intersectionWith (,) cs1 cs2)+    Sum+      <$> traverse+        (uncurry (zipWithM (unifyTypesU uf)))+        (M.intersectionWith (,) cs1 cs2) unifyScalarTypes _ _ _ = Nothing -unifyTypeArgs :: (ArrayDim dim) =>-                 (Uniqueness -> Uniqueness -> Maybe Uniqueness)-              -> TypeArg dim -> TypeArg dim -> Maybe (TypeArg dim)+unifyTypeArgs ::+  (ArrayDim dim) =>+  (Uniqueness -> Uniqueness -> Maybe Uniqueness) ->+  TypeArg dim ->+  TypeArg dim ->+  Maybe (TypeArg dim) unifyTypeArgs _ (TypeArgDim d1 loc) (TypeArgDim d2 _) =   TypeArgDim <$> unifyDims d1 d2 <*> pure loc unifyTypeArgs uf (TypeArgType t1 loc) (TypeArgType t2 _) =@@ -89,20 +99,25 @@  -- | @x \`subtypeOf\` y@ is true if @x@ is a subtype of @y@ (or equal to -- @y@), meaning @x@ is valid whenever @y@ is.-subtypeOf :: ArrayDim dim =>-             TypeBase dim as1 -> TypeBase dim as2 -> Bool+subtypeOf ::+  ArrayDim dim =>+  TypeBase dim as1 ->+  TypeBase dim as2 ->+  Bool subtypeOf t1 t2 = isJust $ unifyTypesU unifyUniqueness (toStruct t1) (toStruct t2)-  where unifyUniqueness u2 u1 = if u2 `subuniqueOf` u1 then Just u1 else Nothing+  where+    unifyUniqueness u2 u1 = if u2 `subuniqueOf` u1 then Just u1 else Nothing  -- | @x `subuniqueOf` y@ is true if @x@ is not less unique than @y@. subuniqueOf :: Uniqueness -> Uniqueness -> Bool subuniqueOf Nonunique Unique = False-subuniqueOf _ _              = True+subuniqueOf _ _ = True  -- | Use 'checkTypeExp' to check a type declaration.-checkTypeDecl :: MonadTypeChecker m =>-                 TypeDeclBase NoInfo Name-              -> m (TypeDeclBase Info VName, Liftedness)+checkTypeDecl ::+  MonadTypeChecker m =>+  TypeDeclBase NoInfo Name ->+  m (TypeDeclBase Info VName, Liftedness) checkTypeDecl (TypeDecl t NoInfo) = do   checkForDuplicateNamesInType t   (t', st, l) <- checkTypeExp t@@ -111,16 +126,18 @@ -- | Type-check a single 'TypeExp', returning the checked 'TypeExp', -- its fully expanded type (modulo yet-unelaborated type variables), -- and whether it is potentially higher-order.-checkTypeExp :: MonadTypeChecker m =>-                TypeExp Name-             -> m (TypeExp VName, StructType, Liftedness)+checkTypeExp ::+  MonadTypeChecker m =>+  TypeExp Name ->+  m (TypeExp VName, StructType, Liftedness) checkTypeExp (TEVar name loc) = do   (name', ps, t, l) <- lookupType loc name   case ps of     [] -> return (TEVar name' loc, t, l)-    _  -> typeError loc mempty $-          "Type constructor" <+> pquote (spread (ppr name : map ppr ps)) <+>-          "used without any arguments."+    _ ->+      typeError loc mempty $+        "Type constructor" <+> pquote (spread (ppr name : map ppr ps))+          <+> "used without any arguments." checkTypeExp (TETuple ts loc) = do   (ts', ts_s, ls) <- unzip3 <$> mapM checkTypeExp ts   return (TETuple ts' loc, tupleRecord ts_s, foldl' max Unlifted ls)@@ -131,12 +148,14 @@     typeError loc mempty $ "Duplicate record fields in" <+> ppr t <> "."    fs_ts_ls <- traverse checkTypeExp $ M.fromList fs-  let fs' = fmap (\(x,_,_) -> x) fs_ts_ls-      ts_s = fmap (\(_,y,_) -> y) fs_ts_ls-      ls = fmap (\(_,_,z) -> z) fs_ts_ls-  return (TERecord (M.toList fs') loc,-          Scalar $ Record ts_s,-          foldl' max Unlifted ls)+  let fs' = fmap (\(x, _, _) -> x) fs_ts_ls+      ts_s = fmap (\(_, y, _) -> y) fs_ts_ls+      ls = fmap (\(_, _, z) -> z) fs_ts_ls+  return+    ( TERecord (M.toList fs') loc,+      Scalar $ Record ts_s,+      foldl' max Unlifted ls+    ) checkTypeExp (TEArray t d loc) = do   (t', st, l) <- checkTypeExp t   (d', d'') <- checkDimExp d@@ -144,86 +163,106 @@     (Unlifted, st') -> return (TEArray t' d' loc, st', Unlifted)     (SizeLifted, _) ->       typeError loc mempty $-      "Cannot create array with elements of size-lifted type" <+> pquote (ppr t) <+/>-      "(might cause irregular array)."+        "Cannot create array with elements of size-lifted type" <+> pquote (ppr t)+          <+/> "(might cause irregular array)."     (Lifted, _) ->       typeError loc mempty $-      "Cannot create array with elements of lifted type" <+> pquote (ppr t) <+/>-      "(might contain function)."-  where checkDimExp DimExpAny =-          return (DimExpAny, AnyDim)-        checkDimExp (DimExpConst k dloc) =-          return (DimExpConst k dloc, ConstDim k)-        checkDimExp (DimExpNamed v dloc) = do-          v' <-  checkNamedDim loc v-          return (DimExpNamed v' dloc, NamedDim v')+        "Cannot create array with elements of lifted type" <+> pquote (ppr t)+          <+/> "(might contain function)."+  where+    checkDimExp DimExpAny =+      return (DimExpAny, AnyDim)+    checkDimExp (DimExpConst k dloc) =+      return (DimExpConst k dloc, ConstDim k)+    checkDimExp (DimExpNamed v dloc) = do+      v' <- checkNamedDim loc v+      return (DimExpNamed v' dloc, NamedDim v') checkTypeExp (TEUnique t loc) = do   (t', st, l) <- checkTypeExp t   unless (mayContainArray st) $     warn loc $ "Declaring " <> quote (pretty st) <> " as unique has no effect."   return (TEUnique t' loc, st `setUniqueness` Unique, l)-  where mayContainArray (Scalar Prim{}) = False-        mayContainArray Array{} = True-        mayContainArray (Scalar (Record fs)) = any mayContainArray fs-        mayContainArray (Scalar TypeVar{}) = True-        mayContainArray (Scalar Arrow{}) = False-        mayContainArray (Scalar (Sum cs)) = (any . any) mayContainArray cs+  where+    mayContainArray (Scalar Prim {}) = False+    mayContainArray Array {} = True+    mayContainArray (Scalar (Record fs)) = any mayContainArray fs+    mayContainArray (Scalar TypeVar {}) = True+    mayContainArray (Scalar Arrow {}) = False+    mayContainArray (Scalar (Sum cs)) = (any . any) mayContainArray cs checkTypeExp (TEArrow (Just v) t1 t2 loc) = do   (t1', st1, _) <- checkTypeExp t1   bindSpaced [(Term, v)] $ do     v' <- checkName Term v loc     bindVal v' (BoundV [] st1) $ do       (t2', st2, _) <- checkTypeExp t2-      return (TEArrow (Just v') t1' t2' loc,-              Scalar $ Arrow mempty (Named v') st1 st2,-              Lifted)+      return+        ( TEArrow (Just v') t1' t2' loc,+          Scalar $ Arrow mempty (Named v') st1 st2,+          Lifted+        ) checkTypeExp (TEArrow Nothing t1 t2 loc) = do   (t1', st1, _) <- checkTypeExp t1   (t2', st2, _) <- checkTypeExp t2-  return (TEArrow Nothing t1' t2' loc,-          Scalar $ Arrow mempty Unnamed st1 st2,-          Lifted)-checkTypeExp ote@TEApply{} = do+  return+    ( TEArrow Nothing t1' t2' loc,+      Scalar $ Arrow mempty Unnamed st1 st2,+      Lifted+    )+checkTypeExp ote@TEApply {} = do   (tname, tname_loc, targs) <- rootAndArgs ote   (tname', ps, t, l) <- lookupType tloc tname   if length ps /= length targs-  then typeError tloc mempty $-       "Type constructor" <+> pquote (ppr tname) <+> "requires" <+> ppr (length ps) <+>-       "arguments, but provided" <+> ppr (length targs) <> "."-  else do-    (targs', substs) <- unzip <$> zipWithM checkArgApply ps targs-    return (foldl (\x y -> TEApply x y tloc) (TEVar tname' tname_loc) targs',-            substituteTypes (mconcat substs) t,-            l)-  where tloc = srclocOf ote--        rootAndArgs :: MonadTypeChecker m => TypeExp Name -> m (QualName Name, SrcLoc, [TypeArgExp Name])-        rootAndArgs (TEVar qn loc) = return (qn, loc, [])-        rootAndArgs (TEApply op arg _) = do (op', loc, args) <- rootAndArgs op-                                            return (op', loc, args++[arg])-        rootAndArgs te' = typeError (srclocOf te') mempty $-                          "Type" <+> pquote (ppr te') <+> "is not a type constructor."--        checkArgApply (TypeParamDim pv _) (TypeArgExpDim (DimExpNamed v dloc) loc) = do-          v' <- checkNamedDim loc v-          return (TypeArgExpDim (DimExpNamed v' dloc) loc,-                  M.singleton pv $ DimSub $ NamedDim v')-        checkArgApply (TypeParamDim pv _) (TypeArgExpDim (DimExpConst x dloc) loc) =-          return (TypeArgExpDim (DimExpConst x dloc) loc,-                  M.singleton pv $ DimSub $ ConstDim x)-        checkArgApply (TypeParamDim pv _) (TypeArgExpDim DimExpAny loc) =-          return (TypeArgExpDim DimExpAny loc,-                  M.singleton pv $ DimSub AnyDim)--        checkArgApply (TypeParamType l pv _) (TypeArgExpType te) = do-          (te', st, _) <- checkTypeExp te-          return (TypeArgExpType te',-                  M.singleton pv $ TypeSub $ TypeAbbr l [] st)+    then+      typeError tloc mempty $+        "Type constructor" <+> pquote (ppr tname) <+> "requires" <+> ppr (length ps)+          <+> "arguments, but provided"+          <+> ppr (length targs) <> "."+    else do+      (targs', substs) <- unzip <$> zipWithM checkArgApply ps targs+      return+        ( foldl (\x y -> TEApply x y tloc) (TEVar tname' tname_loc) targs',+          substituteTypes (mconcat substs) t,+          l+        )+  where+    tloc = srclocOf ote -        checkArgApply p a =-          typeError tloc mempty $ "Type argument" <+> ppr a <+>-          "not valid for a type parameter" <+> ppr p <> "."+    rootAndArgs :: MonadTypeChecker m => TypeExp Name -> m (QualName Name, SrcLoc, [TypeArgExp Name])+    rootAndArgs (TEVar qn loc) = return (qn, loc, [])+    rootAndArgs (TEApply op arg _) = do+      (op', loc, args) <- rootAndArgs op+      return (op', loc, args ++ [arg])+    rootAndArgs te' =+      typeError (srclocOf te') mempty $+        "Type" <+> pquote (ppr te') <+> "is not a type constructor." +    checkArgApply (TypeParamDim pv _) (TypeArgExpDim (DimExpNamed v dloc) loc) = do+      v' <- checkNamedDim loc v+      return+        ( TypeArgExpDim (DimExpNamed v' dloc) loc,+          M.singleton pv $ DimSub $ NamedDim v'+        )+    checkArgApply (TypeParamDim pv _) (TypeArgExpDim (DimExpConst x dloc) loc) =+      return+        ( TypeArgExpDim (DimExpConst x dloc) loc,+          M.singleton pv $ DimSub $ ConstDim x+        )+    checkArgApply (TypeParamDim pv _) (TypeArgExpDim DimExpAny loc) =+      return+        ( TypeArgExpDim DimExpAny loc,+          M.singleton pv $ DimSub AnyDim+        )+    checkArgApply (TypeParamType l pv _) (TypeArgExpType te) = do+      (te', st, _) <- checkTypeExp te+      return+        ( TypeArgExpType te',+          M.singleton pv $ TypeSub $ TypeAbbr l [] st+        )+    checkArgApply p a =+      typeError tloc mempty $+        "Type argument" <+> ppr a+          <+> "not valid for a type parameter"+          <+> ppr p <> "." checkTypeExp t@(TESum cs loc) = do   let constructors = map fst cs   unless (sort constructors == sort (nub constructors)) $@@ -233,94 +272,109 @@     typeError loc mempty "Sum types must have less than 256 constructors."    cs_ts_ls <- (traverse . traverse) checkTypeExp $ M.fromList cs-  let cs'  = (fmap . fmap) (\(x,_,_) -> x) cs_ts_ls+  let cs' = (fmap . fmap) (\(x, _, _) -> x) cs_ts_ls       ts_s = (fmap . fmap) (\(_, y, _) -> y) cs_ts_ls-      ls   = (concatMap . fmap) (\(_, _, z) -> z) cs_ts_ls-  return (TESum (M.toList cs') loc,-          Scalar $ Sum ts_s,-          foldl' max Unlifted ls)+      ls = (concatMap . fmap) (\(_, _, z) -> z) cs_ts_ls+  return+    ( TESum (M.toList cs') loc,+      Scalar $ Sum ts_s,+      foldl' max Unlifted ls+    )  -- | Check for duplication of names inside a pattern group.  Produces -- a description of all names used in the pattern group.-checkForDuplicateNames :: MonadTypeChecker m =>-                          [UncheckedPattern] -> m ()+checkForDuplicateNames ::+  MonadTypeChecker m =>+  [UncheckedPattern] ->+  m () checkForDuplicateNames = (`evalStateT` mempty) . mapM_ check-  where check (Id v _ loc) = seen v loc-        check (PatternParens p _) = check p-        check Wildcard{} = return ()-        check (TuplePattern ps _) = mapM_ check ps-        check (RecordPattern fs _) = mapM_ (check . snd) fs-        check (PatternAscription p _ _) = check p-        check PatternLit{} = return ()-        check (PatternConstr _ _ ps _) = mapM_ check ps+  where+    check (Id v _ loc) = seen v loc+    check (PatternParens p _) = check p+    check Wildcard {} = return ()+    check (TuplePattern ps _) = mapM_ check ps+    check (RecordPattern fs _) = mapM_ (check . snd) fs+    check (PatternAscription p _ _) = check p+    check PatternLit {} = return ()+    check (PatternConstr _ _ ps _) = mapM_ check ps -        seen v loc = do-          already <- gets $ M.lookup v-          case already of-            Just prev_loc ->-              lift $ typeError loc mempty $-              "Name" <+> pquote (ppr v) <+> "also bound at" <+>-              text (locStr prev_loc) <> "."-            Nothing ->-              modify $ M.insert v loc+    seen v loc = do+      already <- gets $ M.lookup v+      case already of+        Just prev_loc ->+          lift $+            typeError loc mempty $+              "Name" <+> pquote (ppr v) <+> "also bound at"+                <+> text (locStr prev_loc) <> "."+        Nothing ->+          modify $ M.insert v loc  -- | Check whether the type contains arrow types that define the same -- parameter.  These might also exist further down, but that's not -- really a problem - we mostly do this checking to help the user, -- since it is likely an error, but it's easy to assign a semantics to -- it (normal name shadowing).-checkForDuplicateNamesInType :: MonadTypeChecker m =>-                                TypeExp Name -> m ()+checkForDuplicateNamesInType ::+  MonadTypeChecker m =>+  TypeExp Name ->+  m () checkForDuplicateNamesInType = check mempty-  where check seen (TEArrow (Just v) t1 t2 loc)-          | Just prev_loc <- M.lookup v seen =-              typeError loc mempty $-              text "Name" <+> pquote (ppr v) <+>-              "also bound at" <+> text (locStr prev_loc) <> "."-          | otherwise =-              check seen' t1 >> check seen' t2-              where seen' = M.insert v loc seen-        check seen (TEArrow Nothing t1 t2 _) =-          check seen t1 >> check seen t2-        check seen (TETuple ts _) = mapM_ (check seen) ts-        check seen (TERecord fs _) = mapM_ (check seen . snd) fs-        check seen (TEUnique t _) = check seen t-        check seen (TESum cs _) = mapM_ (mapM (check seen) . snd) cs-        check seen (TEApply t1 (TypeArgExpType t2) _) =-          check seen t1 >> check seen t2-        check seen (TEApply t1 TypeArgExpDim{} _) =-          check seen t1-        check _ TEArray{} = return ()-        check _ TEVar{} = return ()+  where+    check seen (TEArrow (Just v) t1 t2 loc)+      | Just prev_loc <- M.lookup v seen =+        typeError loc mempty $+          text "Name" <+> pquote (ppr v)+            <+> "also bound at"+            <+> text (locStr prev_loc) <> "."+      | otherwise =+        check seen' t1 >> check seen' t2+      where+        seen' = M.insert v loc seen+    check seen (TEArrow Nothing t1 t2 _) =+      check seen t1 >> check seen t2+    check seen (TETuple ts _) = mapM_ (check seen) ts+    check seen (TERecord fs _) = mapM_ (check seen . snd) fs+    check seen (TEUnique t _) = check seen t+    check seen (TESum cs _) = mapM_ (mapM (check seen) . snd) cs+    check seen (TEApply t1 (TypeArgExpType t2) _) =+      check seen t1 >> check seen t2+    check seen (TEApply t1 TypeArgExpDim {} _) =+      check seen t1+    check _ TEArray {} = return ()+    check _ TEVar {} = return ()  -- | @checkTypeParams ps m@ checks the type parameters @ps@, then -- invokes the continuation @m@ with the checked parameters, while -- extending the monadic name map with @ps@.-checkTypeParams :: MonadTypeChecker m =>-                   [TypeParamBase Name]-                -> ([TypeParamBase VName] -> m a)-                -> m a+checkTypeParams ::+  MonadTypeChecker m =>+  [TypeParamBase Name] ->+  ([TypeParamBase VName] -> m a) ->+  m a checkTypeParams ps m =   bindSpaced (map typeParamSpace ps) $-  m =<< evalStateT (mapM checkTypeParam ps) mempty-  where typeParamSpace (TypeParamDim pv _) = (Term, pv)-        typeParamSpace (TypeParamType _ pv _) = (Type, pv)+    m =<< evalStateT (mapM checkTypeParam ps) mempty+  where+    typeParamSpace (TypeParamDim pv _) = (Term, pv)+    typeParamSpace (TypeParamType _ pv _) = (Type, pv) -        checkParamName ns v loc = do-          seen <- gets $ M.lookup (ns,v)-          case seen of-            Just prev ->-              lift $ typeError loc mempty $-              text "Type parameter" <+> pquote (ppr v) <+>-              "previously defined at" <+> text (locStr prev) <> "."-            Nothing -> do-              modify $ M.insert (ns,v) loc-              lift $ checkName ns v loc+    checkParamName ns v loc = do+      seen <- gets $ M.lookup (ns, v)+      case seen of+        Just prev ->+          lift $+            typeError loc mempty $+              text "Type parameter" <+> pquote (ppr v)+                <+> "previously defined at"+                <+> text (locStr prev) <> "."+        Nothing -> do+          modify $ M.insert (ns, v) loc+          lift $ checkName ns v loc -        checkTypeParam (TypeParamDim pv loc) =-          TypeParamDim <$> checkParamName Term pv loc <*> pure loc-        checkTypeParam (TypeParamType l pv loc) =-          TypeParamType l <$> checkParamName Type pv loc <*> pure loc+    checkTypeParam (TypeParamDim pv loc) =+      TypeParamDim <$> checkParamName Term pv loc <*> pure loc+    checkTypeParam (TypeParamType l pv loc) =+      TypeParamType l <$> checkParamName Type pv loc <*> pure loc  -- | Construct a type argument corresponding to a type parameter. typeParamToArg :: TypeParam -> StructTypeArg@@ -330,9 +384,10 @@   TypeArgType (Scalar $ TypeVar () Nonunique (typeName v) []) ploc  -- | A substitution for when using 'substituteTypes'.-data TypeSub = TypeSub TypeBinding-             | DimSub (DimDecl VName)-             deriving (Show)+data TypeSub+  = TypeSub TypeBinding+  | DimSub (DimDecl VName)+  deriving (Show)  -- | A collection of type substitutions. type TypeSubs = M.Map VName TypeSub@@ -341,14 +396,17 @@ substituteTypes :: Monoid als => TypeSubs -> TypeBase (DimDecl VName) als -> TypeBase (DimDecl VName) als substituteTypes substs ot = case ot of   Array als u at shape ->-    arrayOf (substituteTypes substs (Scalar at) `setAliases` mempty)-    (substituteInShape shape) u `addAliases` (<>als)+    arrayOf+      (substituteTypes substs (Scalar at) `setAliases` mempty)+      (substituteInShape shape)+      u+      `addAliases` (<> als)   Scalar (Prim t) -> Scalar $ Prim t   Scalar (TypeVar als u v targs)     | Just (TypeSub (TypeAbbr _ ps t)) <-         M.lookup (qualLeaf (qualNameFromTypeName v)) substs ->-        applyType ps (t `setAliases` mempty) (map substituteInTypeArg targs)-        `setUniqueness` u `addAliases` (<>als)+      applyType ps (t `setAliases` mempty) (map substituteInTypeArg targs)+        `setUniqueness` u `addAliases` (<> als)     | otherwise -> Scalar $ TypeVar als u v $ map substituteInTypeArg targs   Scalar (Record ts) ->     Scalar $ Record $ fmap (substituteTypes substs) ts@@ -356,34 +414,40 @@     Scalar $ Arrow als v (substituteTypes substs t1) (substituteTypes substs t2)   Scalar (Sum cs) ->     Scalar $ Sum $ (fmap . fmap) (substituteTypes substs) cs-  where substituteInTypeArg (TypeArgDim d loc) =-          TypeArgDim (substituteInDim d) loc-        substituteInTypeArg (TypeArgType t loc) =-          TypeArgType (substituteTypes substs t) loc+  where+    substituteInTypeArg (TypeArgDim d loc) =+      TypeArgDim (substituteInDim d) loc+    substituteInTypeArg (TypeArgType t loc) =+      TypeArgType (substituteTypes substs t) loc -        substituteInShape (ShapeDecl ds) =-          ShapeDecl $ map substituteInDim ds+    substituteInShape (ShapeDecl ds) =+      ShapeDecl $ map substituteInDim ds -        substituteInDim (NamedDim v)-          | Just (DimSub d) <- M.lookup (qualLeaf v) substs = d-        substituteInDim d = d+    substituteInDim (NamedDim v)+      | Just (DimSub d) <- M.lookup (qualLeaf v) substs = d+    substituteInDim d = d -applyType :: Monoid als =>-             [TypeParam] -> TypeBase (DimDecl VName) als -> [StructTypeArg] -> TypeBase (DimDecl VName) als+applyType ::+  Monoid als =>+  [TypeParam] ->+  TypeBase (DimDecl VName) als ->+  [StructTypeArg] ->+  TypeBase (DimDecl VName) als applyType ps t args =   substituteTypes substs t-  where substs = M.fromList $ zipWith mkSubst ps args-        -- We are assuming everything has already been type-checked for correctness.-        mkSubst (TypeParamDim pv _) (TypeArgDim (NamedDim v) _) =-          (pv, DimSub $ NamedDim v)-        mkSubst (TypeParamDim pv _) (TypeArgDim (ConstDim x) _) =-          (pv, DimSub $ ConstDim x)-        mkSubst (TypeParamDim pv _) (TypeArgDim AnyDim  _) =-          (pv, DimSub AnyDim)-        mkSubst (TypeParamType l pv _) (TypeArgType at _) =-          (pv, TypeSub $ TypeAbbr l [] at)-        mkSubst p a =-          error $ "applyType mkSubst: cannot substitute " ++ pretty a ++ " for " ++ pretty p+  where+    substs = M.fromList $ zipWith mkSubst ps args+    -- We are assuming everything has already been type-checked for correctness.+    mkSubst (TypeParamDim pv _) (TypeArgDim (NamedDim v) _) =+      (pv, DimSub $ NamedDim v)+    mkSubst (TypeParamDim pv _) (TypeArgDim (ConstDim x) _) =+      (pv, DimSub $ ConstDim x)+    mkSubst (TypeParamDim pv _) (TypeArgDim AnyDim _) =+      (pv, DimSub AnyDim)+    mkSubst (TypeParamType l pv _) (TypeArgType at _) =+      (pv, TypeSub $ TypeAbbr l [] at)+    mkSubst p a =+      error $ "applyType mkSubst: cannot substitute " ++ pretty a ++ " for " ++ pretty p  -- | A type substituion may be a substitution or a yet-unknown -- substitution (but which is certainly an overloaded primitive@@ -406,7 +470,7 @@   applySubst = substTypesAny  instance Substitutable (TypeBase (DimDecl VName) Aliasing) where-  applySubst = substTypesAny . (fmap (fmap fromStruct).)+  applySubst = substTypesAny . (fmap (fmap fromStruct) .)  instance Substitutable (DimDecl VName) where   applySubst f (NamedDim (QualName _ v))@@ -418,28 +482,36 @@  instance Substitutable Pattern where   applySubst f = runIdentity . astMap mapper-    where mapper = ASTMapper { mapOnExp = return-                             , mapOnName = return-                             , mapOnQualName = return-                             , mapOnStructType = return . applySubst f-                             , mapOnPatternType = return . applySubst f-                             }+    where+      mapper =+        ASTMapper+          { mapOnExp = return,+            mapOnName = return,+            mapOnQualName = return,+            mapOnStructType = return . applySubst f,+            mapOnPatternType = return . applySubst f+          }  -- | Perform substitutions, from type names to types, on a type. Works -- regardless of what shape and uniqueness information is attached to the type.-substTypesAny :: Monoid as =>-                 (VName -> Maybe (Subst (TypeBase (DimDecl VName) as)))-              -> TypeBase (DimDecl VName) as -> TypeBase (DimDecl VName) as+substTypesAny ::+  Monoid as =>+  (VName -> Maybe (Subst (TypeBase (DimDecl VName) as))) ->+  TypeBase (DimDecl VName) as ->+  TypeBase (DimDecl VName) as substTypesAny lookupSubst ot = case ot of   Array als u et shape ->-    arrayOf (substTypesAny lookupSubst' (Scalar et))-    (applySubst lookupSubst' shape) u `setAliases` als+    arrayOf+      (substTypesAny lookupSubst' (Scalar et))+      (applySubst lookupSubst' shape)+      u+      `setAliases` als   Scalar (Prim t) -> Scalar $ Prim t   -- We only substitute for a type variable with no arguments, since   -- type parameters cannot have higher kind.   Scalar (TypeVar als u v targs) ->     case lookupSubst $ qualLeaf (qualNameFromTypeName v) of-      Just (Subst t) -> substTypesAny lookupSubst $ t `setUniqueness` u `addAliases` (<>als)+      Just (Subst t) -> substTypesAny lookupSubst $ t `setUniqueness` u `addAliases` (<> als)       Just PrimSubst -> Scalar $ TypeVar mempty u v $ map subsTypeArg targs       _ -> Scalar $ TypeVar als u v $ map subsTypeArg targs   Scalar (Record ts) -> Scalar $ Record $ fmap (substTypesAny lookupSubst) ts@@ -447,10 +519,10 @@     Scalar $ Arrow als v (substTypesAny lookupSubst t1) (substTypesAny lookupSubst t2)   Scalar (Sum ts) ->     Scalar $ Sum $ fmap (fmap $ substTypesAny lookupSubst) ts--  where subsTypeArg (TypeArgType t loc) =-          TypeArgType (substTypesAny lookupSubst' t) loc-        subsTypeArg (TypeArgDim v loc) =-          TypeArgDim (applySubst lookupSubst' v) loc+  where+    subsTypeArg (TypeArgType t loc) =+      TypeArgType (substTypesAny lookupSubst' t) loc+    subsTypeArg (TypeArgDim v loc) =+      TypeArgDim (applySubst lookupSubst' v) loc -        lookupSubst' = fmap (fmap $ second (const ())) . lookupSubst+    lookupSubst' = fmap (fmap $ second (const ())) . lookupSubst
src/Language/Futhark/TypeChecker/Unify.hs view
@@ -1,980 +1,1146 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE Trustworthy #-}--- | Implementation of unification and other core type system building--- blocks.-module Language.Futhark.TypeChecker.Unify-  ( Constraint(..)-  , Usage-  , mkUsage-  , mkUsage'-  , Level-  , Constraints-  , MonadUnify(..)-  , Rigidity(..)-  , RigidSource(..)-  , BreadCrumbs-  , noBreadCrumbs-  , hasNoBreadCrumbs-  , dimNotes-  , mkTypeVarName--  , zeroOrderType-  , mustHaveConstr-  , mustHaveField-  , mustBeOneOf-  , equalityType-  , normType-  , normPatternType-  , normTypeFully-  , instantiateEmptyArrayDims--  , unify-  , expect-  , unifyMostCommon-  , anyDimOnMismatch-  , doUnification-  )-where--import Control.Monad.Except-import Control.Monad.Writer hiding (Sum)-import Control.Monad.RWS.Strict hiding (Sum)-import Control.Monad.State-import Data.Bifoldable (biany)-import Data.List (intersect)-import Data.Maybe-import qualified Data.Map.Strict as M-import qualified Data.Set as S--import Language.Futhark hiding (unifyDims)-import Language.Futhark.TypeChecker.Monad hiding (BoundV)-import Language.Futhark.TypeChecker.Types-import Futhark.Util.Pretty hiding (empty)---- | A piece of information that describes what process the type--- checker currently performing.  This is used to give better error--- messages for unification errors.-data BreadCrumb = MatchingTypes StructType StructType-                | MatchingFields [Name]-                | MatchingConstructor Name-                | Matching Doc--instance Pretty BreadCrumb where-  ppr (MatchingTypes t1 t2) =-    "When matching type" </> indent 2 (ppr t1) </>-    "with" </> indent 2 (ppr t2)-  ppr (MatchingFields fields) =-    "When matching types of record field" <+>-    pquote (mconcat $ punctuate "." $ map ppr fields) <> dot-  ppr (MatchingConstructor c) =-    "When matching types of constructor" <+> pquote (ppr c) <> dot-  ppr (Matching s) =-    s---- | Unification failures can occur deep down inside complicated types--- (consider nested records).  We leave breadcrumbs behind us so we--- can report the path we took to find the mismatch.-newtype BreadCrumbs = BreadCrumbs [BreadCrumb]---- | An empty path.-noBreadCrumbs :: BreadCrumbs-noBreadCrumbs = BreadCrumbs []---- | Is the path empty?-hasNoBreadCrumbs :: BreadCrumbs -> Bool-hasNoBreadCrumbs (BreadCrumbs xs) = null xs---- | Drop a breadcrumb on the path behind you.-breadCrumb :: BreadCrumb -> BreadCrumbs -> BreadCrumbs-breadCrumb (MatchingFields xs) (BreadCrumbs (MatchingFields ys : bcs)) =-  BreadCrumbs $ MatchingFields (ys++xs) : bcs-breadCrumb bc (BreadCrumbs bcs) =-  BreadCrumbs $ bc : bcs--instance Pretty BreadCrumbs where-  ppr (BreadCrumbs []) = mempty-  ppr (BreadCrumbs bcs) = line <> stack (map ppr bcs)---- | A usage that caused a type constraint.-data Usage = Usage (Maybe String) SrcLoc-  deriving (Show)---- | Construct a 'Usage' from a location and a description.-mkUsage :: SrcLoc -> String -> Usage-mkUsage = flip (Usage . Just)---- | Construct a 'Usage' that has just a location, but no particular--- description.-mkUsage' :: SrcLoc -> Usage-mkUsage' = Usage Nothing--instance Pretty Usage where-  ppr (Usage Nothing loc) = "use at " <> textwrap (locStr loc)-  ppr (Usage (Just s) loc) = textwrap s <+/> "at" <+> textwrap (locStr loc)--instance Located Usage where-  locOf (Usage _ loc) = locOf loc---- | The level at which a type variable is bound.  Higher means--- deeper.  We can only unify a type variable at level @i@ with a type--- @t@ if all type names that occur in @t@ are at most at level @i@.-type Level = Int---- | A constraint on a yet-ambiguous type variable.-data Constraint = NoConstraint Liftedness Usage-                | ParamType Liftedness SrcLoc-                | Constraint StructType Usage-                | Overloaded [PrimType] Usage-                | HasFields (M.Map Name StructType) Usage-                | Equality Usage-                | HasConstrs (M.Map Name [StructType]) Usage-                | ParamSize SrcLoc-                | Size (Maybe (DimDecl VName)) Usage-                  -- ^ Is not actually a type, but a term-level size,-                  -- possibly already set to something specific.-                | UnknowableSize SrcLoc RigidSource-                  -- ^ A size that does not unify with anything --                  -- created from the result of applying a function-                  -- whose return size is existential, or otherwise-                  -- hiding a size.-                deriving Show--instance Located Constraint where-  locOf (NoConstraint _ usage) = locOf usage-  locOf (ParamType _ usage) = locOf usage-  locOf (Constraint _ usage) = locOf usage-  locOf (Overloaded _ usage) = locOf usage-  locOf (HasFields _ usage) = locOf usage-  locOf (Equality usage) = locOf usage-  locOf (HasConstrs _ usage) = locOf usage-  locOf (ParamSize loc) = locOf loc-  locOf (Size _ usage) = locOf usage-  locOf (UnknowableSize loc _) = locOf loc---- | Mapping from fresh type variables, instantiated from the type--- schemes of polymorphic functions, to (possibly) specific types as--- determined on application and the location of that application, or--- a partial constraint on their type.-type Constraints = M.Map VName (Level, Constraint)--lookupSubst :: VName -> Constraints -> Maybe (Subst StructType)-lookupSubst v constraints = case snd <$> M.lookup v constraints of-                              Just (Constraint t _) -> Just $ Subst t-                              Just Overloaded{} -> Just PrimSubst-                              Just (Size (Just d) _) ->-                                Just $ SizeSubst $ applySubst (`lookupSubst` constraints) d-                              _ -> Nothing---- | The source of a rigid size.-data RigidSource-  = RigidArg (Maybe (QualName VName)) String-    -- ^ A function argument that is not a constant or variable name.-  | RigidRet (Maybe (QualName VName))-    -- ^ An existential return size.-  | RigidLoop-  | RigidSlice (Maybe (DimDecl VName)) String-    -- ^ Produced by a complicated slice expression.-  | RigidRange-    -- ^ Produced by a complicated range expression.-  | RigidBound String-    -- ^ Produced by a range expression with this bound.-  | RigidCond StructType StructType-    -- ^ Mismatch in branches.-  | RigidUnify-    -- ^ Invented during unification.-  | RigidOutOfScope SrcLoc VName-  deriving (Eq, Ord, Show)---- | The ridigity of a size variable.  All rigid sizes are tagged with--- information about how they were generated.-data Rigidity = Rigid RigidSource | Nonrigid-              deriving (Eq, Ord, Show)--prettySource :: SrcLoc -> SrcLoc -> RigidSource -> Doc--prettySource ctx loc (RigidRet Nothing) =-  "is unknown size returned by function at" <+>-  text (locStrRel ctx loc) <> "."--prettySource ctx loc (RigidRet (Just fname)) =-  "is unknown size returned by" <+> pquote (ppr fname) <+>-  "at" <+> text (locStrRel ctx loc) <> "."--prettySource ctx loc (RigidArg fname arg) =-  "is value of argument" </>-  indent 2 (shorten arg) </>-  "passed to" <+> fname' <+> "at" <+> text (locStrRel ctx loc) <> "."-  where fname' = maybe "function" (pquote . ppr) fname--prettySource ctx loc (RigidSlice d slice) =-  "is size produced by slice" </>-  indent 2 (shorten slice) </>-  d_desc <> "at" <+> text (locStrRel ctx loc) <> "."-  where d_desc = case d of-                   Just d' -> "of dimension of size " <> pquote (ppr d') <> " "-                   Nothing -> mempty--prettySource ctx loc RigidLoop =-  "is unknown size of value returned at" <+> text (locStrRel ctx loc) <> "."--prettySource ctx loc RigidRange =-  "is unknown length of range at" <+> text (locStrRel ctx loc) <> "."--prettySource ctx loc (RigidBound bound) =-  "generated from expression" </>-  indent 2 (shorten bound) </>-  "used in range at " <> text (locStrRel ctx loc) <> "."--prettySource ctx loc (RigidOutOfScope boundloc v) =-  "is an unknown size arising from " <> pquote (pprName v) <>-  " going out of scope at " <> text (locStrRel ctx loc) <> "." </>-  "Originally bound at " <> text (locStrRel ctx boundloc) <> "."--prettySource _ _ RigidUnify =-  "is an artificial size invented during unification of functions with anonymous sizes"--prettySource ctx loc (RigidCond t1 t2) =-  "is unknown due to conditional expression at " <>-  text (locStrRel ctx loc) <> "." </>-  "One branch returns array of type: " <> align (ppr t1) </>-  "The other an array of type:       " <> align (ppr t2)---- | Retrieve notes describing the purpose or origin of the given--- 'DimDecl'.  The location is used as the *current* location, for the--- purpose of reporting relative locations.-dimNotes :: (Located a, MonadUnify m) => a -> DimDecl VName -> m Notes-dimNotes ctx (NamedDim d) = do-  c <- M.lookup (qualLeaf d) <$> getConstraints-  case c of-    Just (_, UnknowableSize loc rsrc) ->-      return $ aNote $ pretty $-      pquote (ppr d) <+> prettySource (srclocOf ctx) loc rsrc-    _ -> return mempty-dimNotes _ _ = return mempty--typeNotes :: (Located a, MonadUnify m) => a -> StructType -> m Notes-typeNotes ctx =-  fmap mconcat . mapM (dimNotes ctx . NamedDim . qualName) .-  S.toList . typeDimNames---- | Monads that which to perform unification must implement this type--- class.-class Monad m => MonadUnify m where-  getConstraints :: m Constraints-  putConstraints :: Constraints -> m ()-  modifyConstraints :: (Constraints -> Constraints) -> m ()-  modifyConstraints f = do-    x <- getConstraints-    putConstraints $ f x--  newTypeVar :: Monoid als => SrcLoc -> String -> m (TypeBase dim als)-  newDimVar :: SrcLoc -> Rigidity -> String -> m VName--  curLevel :: m Level--  matchError :: Located loc => loc -> Notes -> BreadCrumbs-             -> StructType -> StructType -> m a--  unifyError :: Located loc => loc -> Notes -> BreadCrumbs-             -> Doc -> m a---- | Replace all type variables with their substitution.-normTypeFully :: (Substitutable a, MonadUnify m) => a -> m a-normTypeFully t = do constraints <- getConstraints-                     return $ applySubst (`lookupSubst` constraints) t---- | Replace any top-level type variable with its substitution.-normType :: MonadUnify m => StructType -> m StructType-normType t@(Scalar (TypeVar _ _ (TypeName [] v) [])) = do-  constraints <- getConstraints-  case snd <$> M.lookup v constraints of-    Just (Constraint t' _) -> normType t'-    _ -> return t-normType t = return t---- | Replace any top-level type variable with its substitution.-normPatternType :: MonadUnify m => PatternType -> m PatternType-normPatternType t@(Scalar (TypeVar als u (TypeName [] v) [])) = do-  constraints <- getConstraints-  case snd <$> M.lookup v constraints of-    Just (Constraint t' _) ->-      normPatternType $ t' `setUniqueness` u `setAliases` als-    _ -> return t-normPatternType t = return t--rigidConstraint :: Constraint -> Bool-rigidConstraint ParamType{} = True-rigidConstraint ParamSize{} = True-rigidConstraint UnknowableSize{} = True-rigidConstraint _ = False---- | Replace 'AnyDim' dimensions that occur as 'PosImmediate' or--- 'PosParam' with a fresh 'NamedDim'.-instantiateEmptyArrayDims :: MonadUnify m =>-                             SrcLoc -> String -> Rigidity-                          -> TypeBase (DimDecl VName) als-                          -> m (TypeBase (DimDecl VName) als, [VName])-instantiateEmptyArrayDims tloc desc r = runWriterT . traverseDims onDim-  where onDim _ PosImmediate AnyDim = inst-        onDim _ PosParam AnyDim = inst-        onDim _ _ d = return d-        inst = do-          dim <- lift $ newDimVar tloc r desc-          tell [dim]-          return $ NamedDim $ qualName dim---- | Is the given type variable the name of an abstract type or type--- parameter, which we cannot substitute?-isRigid :: VName -> Constraints -> Bool-isRigid v constraints =-  maybe True (rigidConstraint . snd) $ M.lookup v constraints---- | If the given type variable is nonrigid, what is its level?-isNonRigid :: VName -> Constraints -> Maybe Level-isNonRigid v constraints = do-  (lvl, c) <- M.lookup v constraints-  guard $ not $ rigidConstraint c-  return lvl--type UnifyDims m =-  BreadCrumbs -> [VName] -> (VName -> Maybe Int) -> DimDecl VName -> DimDecl VName -> m ()--flipUnifyDims :: UnifyDims m -> UnifyDims m-flipUnifyDims onDims bcs bound nonrigid t1 t2 =-  onDims bcs bound nonrigid t2 t1--unifyWith :: MonadUnify m =>-             UnifyDims m -> Usage -> BreadCrumbs-          -> StructType -> StructType -> m ()-unifyWith onDims usage = subunify False mempty-  where-    swap True x y = (y, x)-    swap False x y = (x, y)--    subunify ord bound bcs t1 t2 = do-      constraints <- getConstraints--      t1' <- normType t1-      t2' <- normType t2--      let nonrigid v = isNonRigid v constraints--          failure = matchError (srclocOf usage) mempty bcs t1' t2'--          -- Remove any of the intermediate dimensions we added just-          -- for unification purposes.-          unbound = applySubst f-            where f d | d `elem` bound = Just $ SizeSubst AnyDim-                      | otherwise      = Nothing--          link ord' v lvl =-            linkVarToType linkDims usage bcs v lvl . unbound-            where -- We may have to flip the order of future calls to-                  -- onDims inside linkVarToType.-                  linkDims | ord' = flipUnifyDims onDims-                           | otherwise = onDims--          unifyTypeArg bcs' (TypeArgDim d1 _) (TypeArgDim d2 _) =-            onDims' bcs' (swap ord d1 d2)-          unifyTypeArg bcs' (TypeArgType t _) (TypeArgType arg_t _) =-            subunify ord bound bcs' t arg_t-          unifyTypeArg bcs' _ _ = unifyError usage mempty bcs'-            "Cannot unify a type argument with a dimension argument (or vice versa)."--          onDims' bcs' (d1, d2) =-            onDims bcs' bound nonrigid-            (applySubst (`lookupSubst` constraints) d1)-            (applySubst (`lookupSubst` constraints) d2)--      case (t1', t2') of-        (Scalar (Record fs),-         Scalar (Record arg_fs))-          | M.keys fs == M.keys arg_fs ->-              forM_ (M.toList $ M.intersectionWith (,) fs arg_fs) $ \(k, (k_t1, k_t2)) -> do-              let bcs' = breadCrumb (MatchingFields [k]) bcs-              subunify ord bound bcs' k_t1 k_t2-          | otherwise -> do-              let missing = filter (`notElem` M.keys arg_fs) (M.keys fs) ++-                            filter (`notElem` M.keys fs) (M.keys arg_fs)-              unifyError usage mempty bcs $-                "Unshared fields:" <+> commasep (map ppr missing) <> "."--        (Scalar (TypeVar _ _ (TypeName _ tn) targs),-         Scalar (TypeVar _ _ (TypeName _ arg_tn) arg_targs))-          | tn == arg_tn, length targs == length arg_targs -> do-            let bcs' = breadCrumb (Matching "When matching type arguments.") bcs-            zipWithM_ (unifyTypeArg bcs') targs arg_targs--        (Scalar (TypeVar _ _ (TypeName [] v1) []),-         Scalar (TypeVar _ _ (TypeName [] v2) [])) ->-          case (nonrigid v1, nonrigid v2) of-            (Nothing, Nothing) -> failure-            (Just lvl1, Nothing) -> link ord v1 lvl1 t2'-            (Nothing, Just lvl2) -> link (not ord) v2 lvl2 t1'-            (Just lvl1, Just lvl2)-              | lvl1 <= lvl2 -> link ord v1 lvl1 t2'-              | otherwise    -> link (not ord) v2 lvl2 t1'--        (Scalar (TypeVar _ _ (TypeName [] v1) []), _)-          | Just lvl <- nonrigid v1 ->-              link ord v1 lvl t2'-        (_, Scalar (TypeVar _ _ (TypeName [] v2) []))-          | Just lvl <- nonrigid v2 ->-              link (not ord) v2 lvl t1'--        (Scalar (Arrow _ p1 a1 b1),-         Scalar (Arrow _ p2 a2 b2)) -> do-          let (r1, r2) = swap ord (Rigid RigidUnify) Nonrigid-          (a1', a1_dims) <- instantiateEmptyArrayDims (srclocOf usage) "anonymous" r1 a1-          (a2', a2_dims) <- instantiateEmptyArrayDims (srclocOf usage) "anonymous" r2 a2-          let bound' = bound <> mapMaybe pname [p1, p2] <> a1_dims <> a2_dims-          subunify (not ord) bound-            (breadCrumb (Matching "When matching parameter types.") bcs)-            a1' a2'-          subunify ord bound'-            (breadCrumb (Matching "When matching return types.") bcs)-            b1' b2'-          where (b1', b2') =-                  -- Replace one parameter name with the other in the-                  -- return type, in case of dependent types.  I.e.,-                  -- we want type '(n: i32) -> [n]i32' to unify with-                  -- type '(x: i32) -> [x]i32'.-                  case (p1, p2) of-                    (Named p1', Named p2') ->-                      let f v | v == p2' = Just $ SizeSubst $ NamedDim $ qualName p1'-                              | otherwise = Nothing-                      in (b1, applySubst f b2)--                    (_, _) ->-                      (b1, b2)--                pname (Named x) = Just x-                pname Unnamed = Nothing--        (Array{}, Array{})-          | ShapeDecl (t1_d : _) <- arrayShape t1',-            ShapeDecl (t2_d : _) <- arrayShape t2',-            Just t1'' <- peelArray 1 t1',-            Just t2'' <- peelArray 1 t2' -> do-              onDims' bcs (swap ord t1_d t2_d)-              subunify ord bound bcs t1'' t2''--        (Scalar (Sum cs),-         Scalar (Sum arg_cs))-          | M.keys cs == M.keys arg_cs ->-              unifySharedConstructors onDims usage bcs-              (map unbound <$> cs) (map unbound <$> arg_cs)-          | otherwise -> do-              let missing = filter (`notElem` M.keys arg_cs) (M.keys cs) ++-                            filter (`notElem` M.keys cs) (M.keys arg_cs)-              unifyError usage mempty bcs $-                "Unshared constructors:" <+> commasep (map (("#"<>) . ppr) missing) <> "."--        _ | t1' == t2' -> return ()-          | otherwise -> failure--unifyDims :: MonadUnify m => Usage -> UnifyDims m-unifyDims _ _ _ _ d1 d2-  | d1 == d2 = return ()-unifyDims usage bcs _ nonrigid (NamedDim (QualName _ d1)) d2-  | Just lvl1 <- nonrigid d1 =-      linkVarToDim usage bcs d1 lvl1 d2-unifyDims usage bcs _ nonrigid d1 (NamedDim (QualName _ d2))-  | Just lvl2 <- nonrigid d2 =-      linkVarToDim usage bcs d2 lvl2 d1-unifyDims usage bcs _ _ d1 d2 = do-  notes <- (<>) <$> dimNotes usage d1 <*> dimNotes usage d2-  unifyError usage notes bcs $-    "Dimensions" <+> pquote (ppr d1) <+>-    "and" <+> pquote (ppr d2) <+> "do not match."---- | Unifies two types.-unify :: MonadUnify m => Usage -> StructType -> StructType -> m ()-unify usage = unifyWith (unifyDims usage) usage noBreadCrumbs---- | @expect super sub@ checks that @sub@ is a subtype of @super@.-expect :: MonadUnify m => Usage -> StructType -> StructType -> m ()-expect usage = unifyWith onDims usage noBreadCrumbs-  where onDims _ _ _ AnyDim _ = return ()-        onDims _ _ _ d1 d2-          | d1 == d2 = return ()-        onDims bcs bound nonrigid (NamedDim (QualName _ d1)) d2-          | Just lvl1 <- nonrigid d1, d2 /= AnyDim, not $ boundParam bound d2 =-              linkVarToDim usage bcs d1 lvl1 d2-        onDims bcs bound nonrigid d1 (NamedDim (QualName _ d2))-          | Just lvl2 <- nonrigid d2, not $ boundParam bound d1 =-              linkVarToDim usage bcs d2 lvl2 d1-        onDims bcs _ _ d1 d2 = do-          notes <- (<>) <$> dimNotes usage d1 <*> dimNotes usage d2-          unifyError usage notes bcs $ "Dimensions" <+> pquote (ppr d1) <+>-            "and" <+> pquote (ppr d2) <+> "do not match."--        boundParam bound (NamedDim (QualName _ d)) = d `elem` bound-        boundParam _ _ = False--hasEmptyDims :: StructType -> Bool-hasEmptyDims = biany empty (const False)-  where empty AnyDim = True-        empty _ = False--occursCheck :: MonadUnify m =>-               Usage -> BreadCrumbs-            -> VName -> StructType -> m ()-occursCheck usage bcs vn tp =-  when (vn `S.member` typeVars tp) $-  unifyError usage mempty bcs $ "Occurs check: cannot instantiate" <+>-  pprName vn <+> "with" <+> ppr tp <> "."--scopeCheck :: MonadUnify m =>-              Usage -> BreadCrumbs-           -> VName -> Level -> StructType -> m ()-scopeCheck usage bcs vn max_lvl tp = do-  constraints <- getConstraints-  checkType constraints tp-  where checkType constraints t =-          mapM_ (check constraints) $ typeVars t <> typeDimNames t--        check constraints v-          | Just (lvl, c) <- M.lookup v constraints,-            lvl > max_lvl =-              if rigidConstraint c-              then scopeViolation v-              else modifyConstraints $ M.insert v (max_lvl, c)--          | otherwise =-              return ()--        scopeViolation v = do-          notes <- typeNotes usage tp-          unifyError usage notes bcs $ "Cannot unify type" </>-            indent 2 (ppr tp) </>-            "with" <+> pquote (pprName vn) <+> "(scope violation)." </>-            "This is because" <+> pquote (pprName v) <+>-            "is rigidly bound in a deeper scope."--linkVarToType :: MonadUnify m =>-                 UnifyDims m -> Usage -> BreadCrumbs-              -> VName -> Level -> StructType -> m ()-linkVarToType onDims usage bcs vn lvl tp = do-  occursCheck usage bcs vn tp-  scopeCheck usage bcs vn lvl tp--  constraints <- getConstraints-  let tp' = removeUniqueness tp-  modifyConstraints $ M.insert vn (lvl, Constraint tp' usage)-  case snd <$> M.lookup vn constraints of--    Just (NoConstraint Unlifted unlift_usage) -> do-      let bcs' = breadCrumb-                 (Matching $ "When verifying that" <+> pquote (pprName vn) <+>-                  textwrap "is not instantiated with a function type, due to" <+>-                  ppr unlift_usage)-                 bcs-      zeroOrderTypeWith usage bcs' tp'--      when (hasEmptyDims tp') $-        unifyError usage mempty bcs $ "Type variable" <+> pprName vn <+>-        "cannot be instantiated with type containing anonymous sizes:" </>-        indent 2 (ppr tp) </>-        textwrap "This is usually because the size of an array returned by a higher-order function argument cannot be determined statically.  This can also be due to the return size being a value parameter.  Add type annotation to clarify."--    Just (Equality _) ->-      equalityType usage tp'--    Just (Overloaded ts old_usage)-      | tp `notElem` map (Scalar . Prim) ts ->-          case tp' of-            Scalar (TypeVar _ _ (TypeName [] v) [])-              | not $ isRigid v constraints ->-                  linkVarToTypes usage v ts-            _ ->-              unifyError usage mempty bcs $ "Cannot instantiate" <+> pquote (pprName vn) <+>-              "with type" </> indent 2 (ppr tp) </> "as" <+>-              pquote (pprName vn) <+> "must be one of" <+>-              commasep (map ppr ts) <+/>-              "due to" <+/> ppr old_usage <> "."--    Just (HasFields required_fields old_usage) ->-      case tp of-        Scalar (Record tp_fields)-          | all (`M.member` tp_fields) $ M.keys required_fields -> do-              required_fields' <- mapM normTypeFully required_fields-              let bcs' =-                    breadCrumb-                    (Matching $ pprName vn <+>-                     "must be a record with at least the fields:" </>-                     indent 2 (ppr (Record required_fields')) </>-                    "due to" <+> ppr old_usage <> ".")-                    bcs-              mapM_ (uncurry $ unifyWith onDims usage bcs') $ M.elems $-                M.intersectionWith (,) required_fields tp_fields-        Scalar (TypeVar _ _ (TypeName [] v) [])-          | not $ isRigid v constraints ->-              modifyConstraints $ M.insert v-              (lvl, HasFields required_fields old_usage)-        _ ->-          unifyError usage mempty bcs $-          "Cannot instantiate" <+> pquote (pprName vn) <+> "with type" </>-          indent 2 (ppr tp) </>-          "as" <+> pquote (pprName vn) <+> "must be a record with fields" </>-          indent 2 (ppr (Record required_fields)) </>-          "due to" <+> ppr old_usage <> "."--    Just (HasConstrs required_cs old_usage) ->-      case tp of-        Scalar (Sum ts)-          | all (`M.member` ts) $ M.keys required_cs ->-              unifySharedConstructors onDims usage bcs required_cs ts-        Scalar (TypeVar _ _ (TypeName [] v) [])-          | not $ isRigid v constraints -> do-              case M.lookup v constraints of-                Just (_, HasConstrs v_cs _) ->-                  unifySharedConstructors onDims usage bcs required_cs v_cs-                _ -> return ()-              modifyConstraints $ M.insertWith combineConstrs v-                (lvl, HasConstrs required_cs old_usage)-              where combineConstrs (_, HasConstrs cs1 usage1) (_, HasConstrs cs2 _) =-                      (lvl, HasConstrs (M.union cs1 cs2) usage1)-                    combineConstrs hasCs _ = hasCs-        _ -> noSumType--    _ -> return ()--  where noSumType = unifyError usage mempty bcs-                    "Cannot unify a sum type with a non-sum type"--linkVarToDim :: MonadUnify m =>-                Usage -> BreadCrumbs-             -> VName -> Level -> DimDecl VName -> m ()-linkVarToDim usage bcs vn lvl dim = do-  constraints <- getConstraints--  case dim of-    NamedDim dim'-      | Just (dim_lvl, c) <- qualLeaf dim' `M.lookup` constraints,-        dim_lvl > lvl ->-          case c of-            ParamSize{} -> do-              notes <- dimNotes usage dim-              unifyError usage notes bcs $-                "Cannot unify size variable" <+> pquote (ppr dim') <+>-                "with" <+> pquote (pprName vn) <+> "(scope violation)." </>-                "This is because" <+> pquote (ppr dim') <+>-                "is rigidly bound in a deeper scope."-            _ -> modifyConstraints $ M.insert (qualLeaf dim') (lvl, c)-    _ -> return ()--  modifyConstraints $ M.insert vn (lvl, Size (Just dim) usage)--removeUniqueness :: TypeBase dim as -> TypeBase dim as-removeUniqueness (Scalar (Record ets)) =-  Scalar $ Record $ fmap removeUniqueness ets-removeUniqueness (Scalar (Arrow als p t1 t2)) =-  Scalar $ Arrow als p (removeUniqueness t1) (removeUniqueness t2)-removeUniqueness (Scalar (Sum cs)) =-  Scalar $ Sum $ (fmap . fmap) removeUniqueness cs-removeUniqueness t = t `setUniqueness` Nonunique---- | Assert that this type must be one of the given primitive types.-mustBeOneOf :: MonadUnify m => [PrimType] -> Usage -> StructType -> m ()-mustBeOneOf [req_t] usage t = unify usage (Scalar (Prim req_t)) t-mustBeOneOf ts usage t = do-  t' <- normType t-  constraints <- getConstraints-  let isRigid' v = isRigid v constraints--  case t' of-    Scalar (TypeVar _ _ (TypeName [] v) [])-      | not $ isRigid' v -> linkVarToTypes usage v ts--    Scalar (Prim pt) | pt `elem` ts -> return ()--    _ -> failure--  where failure = unifyError usage mempty noBreadCrumbs $-                  text "Cannot unify type" <+> pquote (ppr t) <+>-                  "with any of " <> commasep (map ppr ts) <> "."--linkVarToTypes :: MonadUnify m => Usage -> VName -> [PrimType] -> m ()-linkVarToTypes usage vn ts = do-  vn_constraint <- M.lookup vn <$> getConstraints-  case vn_constraint of-    Just (lvl, Overloaded vn_ts vn_usage) ->-      case ts `intersect` vn_ts of-        [] -> unifyError usage mempty noBreadCrumbs $-              "Type constrained to one of" <+>-              commasep (map ppr ts) <+> "but also one of" <+>-              commasep (map ppr vn_ts) <+> "due to" <+> ppr vn_usage <> "."-        ts' -> modifyConstraints $ M.insert vn (lvl, Overloaded ts' usage)--    Just (_, HasConstrs _ vn_usage) ->-      unifyError usage mempty noBreadCrumbs $-      "Type constrained to one of" <+> commasep (map ppr ts) <>-      ", but also inferred to be sum type due to" <+> ppr vn_usage <> "."--    Just (_, HasFields _ vn_usage) ->-      unifyError usage mempty noBreadCrumbs $-      "Type constrained to one of" <+> commasep (map ppr ts) <>-      ", but also inferred to be record due to" <+> ppr vn_usage <> "."--    Just (lvl, _) -> modifyConstraints $ M.insert vn (lvl, Overloaded ts usage)--    Nothing ->-      unifyError usage mempty noBreadCrumbs $-      "Cannot constrain type to one of" <+> commasep (map ppr ts)---- | Assert that this type must support equality.-equalityType :: (MonadUnify m, Pretty (ShapeDecl dim), Monoid as) =>-                Usage -> TypeBase dim as -> m ()-equalityType usage t = do-  unless (orderZero t) $-    unifyError usage mempty noBreadCrumbs $-    "Type " <+> pquote (ppr t) <+> "does not support equality (is higher-order)."-  mapM_ mustBeEquality $ typeVars t-  where mustBeEquality vn = do-          constraints <- getConstraints-          case M.lookup vn constraints of-            Just (_, Constraint (Scalar (TypeVar _ _ (TypeName [] vn') [])) _) ->-              mustBeEquality vn'-            Just (_, Constraint vn_t cusage)-              | not $ orderZero vn_t ->-                  unifyError usage mempty noBreadCrumbs $-                  "Type" <+> pquote (ppr t) <+> "does not support equality." </>-                  "Constrained to be higher-order due to" <+> ppr cusage <+> "."-              | otherwise -> return ()-            Just (lvl, NoConstraint _ _) ->-              modifyConstraints $ M.insert vn (lvl, Equality usage)-            Just (_, Overloaded _ _) ->-              return () -- All primtypes support equality.-            Just (_, Equality{}) ->-              return ()-            Just (_, HasConstrs cs _) ->-              mapM_ (equalityType usage) $ concat $ M.elems cs-            _ ->-              unifyError usage mempty noBreadCrumbs $-              "Type" <+> pprName vn <+> "does not support equality."--zeroOrderTypeWith :: (MonadUnify m, Pretty (ShapeDecl dim), Monoid as) =>-                     Usage -> BreadCrumbs -> TypeBase dim as -> m ()-zeroOrderTypeWith usage bcs t = do-  unless (orderZero t) $-    unifyError usage mempty bcs $-    "Type" </> indent 2 (ppr t) </> "found to be functional."-  mapM_ mustBeZeroOrder . S.toList . typeVars $ t-  where mustBeZeroOrder vn = do-          constraints <- getConstraints-          case M.lookup vn constraints of-            Just (lvl, NoConstraint _ _) ->-              modifyConstraints $ M.insert vn (lvl, NoConstraint Unlifted usage)-            Just (_, ParamType Lifted ploc) ->-              unifyError usage mempty bcs $ "Type parameter" <+>-              pquote (pprName vn) <+> "at" <+>-              text (locStr ploc) <+> "may be a function."-            _ -> return ()---- | Assert that this type must be zero-order.-zeroOrderType :: (MonadUnify m, Pretty (ShapeDecl dim), Monoid as) =>-                 Usage -> String -> TypeBase dim as -> m ()-zeroOrderType usage desc =-  zeroOrderTypeWith usage $ breadCrumb bc noBreadCrumbs-  where bc = Matching $ "When checking" <+> textwrap desc--unifySharedConstructors :: MonadUnify m =>-                           UnifyDims m -> Usage -> BreadCrumbs-                        -> M.Map Name [StructType]-                        -> M.Map Name [StructType]-                        -> m ()-unifySharedConstructors onDims usage bcs cs1 cs2 =-  forM_ (M.toList $ M.intersectionWith (,) cs1 cs2) $ \(c, (f1, f2)) ->-  unifyConstructor c f1 f2-  where unifyConstructor c f1 f2-          | length f1 == length f2 = do-              let bcs' = breadCrumb (MatchingConstructor c) bcs-              zipWithM_ (unifyWith onDims usage bcs') f1 f2-          | otherwise =-              unifyError usage mempty bcs $-              "Cannot unify constructor" <+> pquote (pprName c) <> "."---- | In @mustHaveConstr usage c t fs@, the type @t@ must have a--- constructor named @c@ that takes arguments of types @ts@.-mustHaveConstr :: MonadUnify m =>-                  Usage -> Name -> StructType -> [StructType] -> m ()-mustHaveConstr usage c t fs = do-  constraints <- getConstraints-  case t of-    Scalar (TypeVar _ _ (TypeName _ tn) [])-      | Just (lvl, NoConstraint{}) <- M.lookup tn constraints -> do-          mapM_ (scopeCheck usage noBreadCrumbs tn lvl) fs-          modifyConstraints $ M.insert tn (lvl, HasConstrs (M.singleton c fs) usage)-      | Just (lvl, HasConstrs cs _) <- M.lookup tn constraints ->-        case M.lookup c cs of-          Nothing  -> modifyConstraints $ M.insert tn (lvl, HasConstrs (M.insert c fs cs) usage)-          Just fs'-            | length fs == length fs' -> zipWithM_ (unify usage) fs fs'-            | otherwise ->-                unifyError usage mempty noBreadCrumbs $-                "Different arity for constructor" <+> pquote (ppr c) <> "."--    Scalar (Sum cs) ->-      case M.lookup c cs of-        Nothing ->-          unifyError usage mempty noBreadCrumbs $-          "Constuctor" <+> pquote (ppr c) <+> "not present in type."-        Just fs'-            | length fs == length fs' -> zipWithM_ (unify usage) fs fs'-            | otherwise ->-                unifyError usage mempty noBreadCrumbs $-                "Different arity for constructor" <+> pquote (ppr c) <+> "."--    _ -> do unify usage t $ Scalar $ Sum $ M.singleton c fs-            return ()--mustHaveFieldWith :: MonadUnify m =>-                     UnifyDims m -> Usage -> BreadCrumbs-                  -> Name -> PatternType -> m PatternType-mustHaveFieldWith onDims usage bcs l t = do-  constraints <- getConstraints-  l_type <- newTypeVar (srclocOf usage) "t"-  let l_type' = toStruct l_type-  case t of-    Scalar (TypeVar _ _ (TypeName _ tn) [])-      | Just (lvl, NoConstraint{}) <- M.lookup tn constraints -> do-          scopeCheck usage bcs tn lvl l_type'-          modifyConstraints $ M.insert tn (lvl, HasFields (M.singleton l l_type') usage)-          return l_type-      | Just (lvl, HasFields fields _) <- M.lookup tn constraints -> do-          case M.lookup l fields of-            Just t' -> unifyWith onDims usage bcs l_type' t'-            Nothing -> modifyConstraints $ M.insert tn-                       (lvl, HasFields (M.insert l l_type' fields) usage)-          return l_type-    Scalar (Record fields)-      | Just t' <- M.lookup l fields -> do-          unify usage l_type' $ toStruct t'-          return t'-      | otherwise ->-          unifyError usage mempty bcs $-            "Attempt to access field" <+> pquote (ppr l) <+> " of value of type" <+>-            ppr (toStructural t) <> "."-    _ -> do unify usage (toStruct t) $ Scalar $ Record $ M.singleton l l_type'-            return l_type---- | Assert that some type must have a field with this name and type.-mustHaveField :: MonadUnify m =>-                 Usage -> Name -> PatternType -> m PatternType-mustHaveField usage = mustHaveFieldWith (unifyDims usage) usage noBreadCrumbs---- | Replace dimension mismatches with AnyDim.-anyDimOnMismatch :: Monoid as =>-                    TypeBase (DimDecl VName) as -> TypeBase (DimDecl VName) as-                 -> (TypeBase (DimDecl VName) as, [(DimDecl VName, DimDecl VName)])-anyDimOnMismatch t1 t2 = runWriter $ matchDims onDims t1 t2-  where onDims d1 d2-          | d1 == d2 = return d1-          | otherwise = do tell [(d1, d2)]-                           return AnyDim--newDimOnMismatch :: (Monoid as, MonadUnify m) =>-                    SrcLoc -> TypeBase (DimDecl VName) as -> TypeBase (DimDecl VName) as-                 -> m (TypeBase (DimDecl VName) as, [VName])-newDimOnMismatch loc t1 t2 = do-  (t, seen) <- runStateT (matchDims onDims t1 t2) mempty-  return (t, M.elems seen)-  where r = Rigid $ RigidCond (toStruct t1) (toStruct t2)-        onDims d1 d2-          | d1 == d2 = return d1-          | otherwise = do-              -- Remember mismatches we have seen before and reuse the-              -- same new size.-              maybe_d <- gets $ M.lookup (d1, d2)-              case maybe_d of-                Just d -> return $ NamedDim $ qualName d-                Nothing -> do-                  d <- lift $ newDimVar loc r "differ"-                  modify $ M.insert (d1, d2) d-                  return $ NamedDim $ qualName d---- | Like unification, but creates new size variables where mismatches--- occur.  Returns the new dimensions thus created.-unifyMostCommon :: MonadUnify m =>-                   Usage -> PatternType -> PatternType -> m (PatternType, [VName])-unifyMostCommon usage t1 t2 = do-  -- We are ignoring the dimensions here, because any mismatches-  -- should be turned into fresh size variables.-  let allOK _ _ _ _ _ = return ()-  unifyWith allOK usage noBreadCrumbs (toStruct t1) (toStruct t2)-  t1' <- normTypeFully t1-  t2' <- normTypeFully t2-  newDimOnMismatch (srclocOf usage) t1' t2'---- Simple MonadUnify implementation.--type UnifyMState = (Constraints, Int)--newtype UnifyM a = UnifyM (StateT UnifyMState (Except TypeError) a)-  deriving (Monad, Functor, Applicative,-            MonadState UnifyMState,-            MonadError TypeError)--newVar :: String -> UnifyM VName-newVar name = do-  (x, i) <- get-  put (x, i+1)-  return $ VName (mkTypeVarName name i) i--instance MonadUnify UnifyM where-  getConstraints = gets fst-  putConstraints x = modify $ \(_, i) -> (x, i)--  newTypeVar loc name = do-    v <- newVar name-    modifyConstraints $ M.insert v (0, NoConstraint Lifted $ Usage Nothing loc)-    return $ Scalar $ TypeVar mempty Nonunique (typeName v) []--  newDimVar loc rigidity name = do-    dim <- newVar name-    case rigidity of-      Rigid src -> modifyConstraints $ M.insert dim (0, UnknowableSize loc src)-      Nonrigid -> modifyConstraints $ M.insert dim (0, Size Nothing $ Usage Nothing loc)-    return dim--  curLevel = pure 0--  unifyError loc notes bcs doc =-    throwError $ TypeError (srclocOf loc) notes $ doc <> ppr bcs--  matchError loc notes bcs t1 t2 =-    throwError $ TypeError (srclocOf loc) notes $ doc <> ppr bcs-    where doc = "Types" </>-                indent 2 (ppr t1) </>-                "and" </>-                indent 2 (ppr t2) </>-                "do not match."---- | Construct a the name of a new type variable given a base--- description and a tag number (note that this is distinct from--- actually constructing a VName; the tag here is intended for human--- consumption but the machine does not care).-mkTypeVarName :: String -> Int -> Name-mkTypeVarName desc i =-  nameFromString $ desc ++ mapMaybe subscript (show i)-  where subscript = flip lookup $ zip "0123456789" "₀₁₂₃₄₅₆₇₈₉"--runUnifyM :: [TypeParam] -> UnifyM a -> Either TypeError a-runUnifyM tparams (UnifyM m) = runExcept $ evalStateT m (constraints, 0)-  where constraints = M.fromList $ map f tparams-        f (TypeParamDim p loc) = (p, (0, Size Nothing $ Usage Nothing loc))-        f (TypeParamType l p loc) = (p, (0, NoConstraint l $ Usage Nothing loc))---- | Perform a unification of two types outside a monadic context.--- The type parameters are allowed to be instantiated; all other types--- are considered rigid.-doUnification :: SrcLoc -> [TypeParam]-              -> StructType -> StructType-              -> Either TypeError StructType+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE Trustworthy #-}++-- | Implementation of unification and other core type system building+-- blocks.+module Language.Futhark.TypeChecker.Unify+  ( Constraint (..),+    Usage,+    mkUsage,+    mkUsage',+    Level,+    Constraints,+    MonadUnify (..),+    Rigidity (..),+    RigidSource (..),+    BreadCrumbs,+    noBreadCrumbs,+    hasNoBreadCrumbs,+    dimNotes,+    mkTypeVarName,+    zeroOrderType,+    mustHaveConstr,+    mustHaveField,+    mustBeOneOf,+    equalityType,+    normType,+    normPatternType,+    normTypeFully,+    instantiateEmptyArrayDims,+    unify,+    expect,+    unifyMostCommon,+    anyDimOnMismatch,+    doUnification,+  )+where++import Control.Monad.Except+import Control.Monad.RWS.Strict hiding (Sum)+import Control.Monad.State+import Control.Monad.Writer hiding (Sum)+import Data.Bifoldable (biany)+import Data.List (intersect)+import qualified Data.Map.Strict as M+import Data.Maybe+import qualified Data.Set as S+import Futhark.Util.Pretty hiding (empty)+import Language.Futhark hiding (unifyDims)+import Language.Futhark.TypeChecker.Monad hiding (BoundV)+import Language.Futhark.TypeChecker.Types++-- | A piece of information that describes what process the type+-- checker currently performing.  This is used to give better error+-- messages for unification errors.+data BreadCrumb+  = MatchingTypes StructType StructType+  | MatchingFields [Name]+  | MatchingConstructor Name+  | Matching Doc++instance Pretty BreadCrumb where+  ppr (MatchingTypes t1 t2) =+    "When matching type" </> indent 2 (ppr t1)+      </> "with"+      </> indent 2 (ppr t2)+  ppr (MatchingFields fields) =+    "When matching types of record field"+      <+> pquote (mconcat $ punctuate "." $ map ppr fields) <> dot+  ppr (MatchingConstructor c) =+    "When matching types of constructor" <+> pquote (ppr c) <> dot+  ppr (Matching s) =+    s++-- | Unification failures can occur deep down inside complicated types+-- (consider nested records).  We leave breadcrumbs behind us so we+-- can report the path we took to find the mismatch.+newtype BreadCrumbs = BreadCrumbs [BreadCrumb]++-- | An empty path.+noBreadCrumbs :: BreadCrumbs+noBreadCrumbs = BreadCrumbs []++-- | Is the path empty?+hasNoBreadCrumbs :: BreadCrumbs -> Bool+hasNoBreadCrumbs (BreadCrumbs xs) = null xs++-- | Drop a breadcrumb on the path behind you.+breadCrumb :: BreadCrumb -> BreadCrumbs -> BreadCrumbs+breadCrumb (MatchingFields xs) (BreadCrumbs (MatchingFields ys : bcs)) =+  BreadCrumbs $ MatchingFields (ys ++ xs) : bcs+breadCrumb bc (BreadCrumbs bcs) =+  BreadCrumbs $ bc : bcs++instance Pretty BreadCrumbs where+  ppr (BreadCrumbs []) = mempty+  ppr (BreadCrumbs bcs) = line <> stack (map ppr bcs)++-- | A usage that caused a type constraint.+data Usage = Usage (Maybe String) SrcLoc+  deriving (Show)++-- | Construct a 'Usage' from a location and a description.+mkUsage :: SrcLoc -> String -> Usage+mkUsage = flip (Usage . Just)++-- | Construct a 'Usage' that has just a location, but no particular+-- description.+mkUsage' :: SrcLoc -> Usage+mkUsage' = Usage Nothing++instance Pretty Usage where+  ppr (Usage Nothing loc) = "use at " <> textwrap (locStr loc)+  ppr (Usage (Just s) loc) = textwrap s <+/> "at" <+> textwrap (locStr loc)++instance Located Usage where+  locOf (Usage _ loc) = locOf loc++-- | The level at which a type variable is bound.  Higher means+-- deeper.  We can only unify a type variable at level @i@ with a type+-- @t@ if all type names that occur in @t@ are at most at level @i@.+type Level = Int++-- | A constraint on a yet-ambiguous type variable.+data Constraint+  = NoConstraint Liftedness Usage+  | ParamType Liftedness SrcLoc+  | Constraint StructType Usage+  | Overloaded [PrimType] Usage+  | HasFields (M.Map Name StructType) Usage+  | Equality Usage+  | HasConstrs (M.Map Name [StructType]) Usage+  | ParamSize SrcLoc+  | -- | Is not actually a type, but a term-level size,+    -- possibly already set to something specific.+    Size (Maybe (DimDecl VName)) Usage+  | -- | A size that does not unify with anything -+    -- created from the result of applying a function+    -- whose return size is existential, or otherwise+    -- hiding a size.+    UnknowableSize SrcLoc RigidSource+  deriving (Show)++instance Located Constraint where+  locOf (NoConstraint _ usage) = locOf usage+  locOf (ParamType _ usage) = locOf usage+  locOf (Constraint _ usage) = locOf usage+  locOf (Overloaded _ usage) = locOf usage+  locOf (HasFields _ usage) = locOf usage+  locOf (Equality usage) = locOf usage+  locOf (HasConstrs _ usage) = locOf usage+  locOf (ParamSize loc) = locOf loc+  locOf (Size _ usage) = locOf usage+  locOf (UnknowableSize loc _) = locOf loc++-- | Mapping from fresh type variables, instantiated from the type+-- schemes of polymorphic functions, to (possibly) specific types as+-- determined on application and the location of that application, or+-- a partial constraint on their type.+type Constraints = M.Map VName (Level, Constraint)++lookupSubst :: VName -> Constraints -> Maybe (Subst StructType)+lookupSubst v constraints = case snd <$> M.lookup v constraints of+  Just (Constraint t _) -> Just $ Subst t+  Just Overloaded {} -> Just PrimSubst+  Just (Size (Just d) _) ->+    Just $ SizeSubst $ applySubst (`lookupSubst` constraints) d+  _ -> Nothing++-- | The source of a rigid size.+data RigidSource+  = -- | A function argument that is not a constant or variable name.+    RigidArg (Maybe (QualName VName)) String+  | -- | An existential return size.+    RigidRet (Maybe (QualName VName))+  | RigidLoop+  | -- | Produced by a complicated slice expression.+    RigidSlice (Maybe (DimDecl VName)) String+  | -- | Produced by a complicated range expression.+    RigidRange+  | -- | Produced by a range expression with this bound.+    RigidBound String+  | -- | Mismatch in branches.+    RigidCond StructType StructType+  | -- | Invented during unification.+    RigidUnify+  | RigidOutOfScope SrcLoc VName+  deriving (Eq, Ord, Show)++-- | The ridigity of a size variable.  All rigid sizes are tagged with+-- information about how they were generated.+data Rigidity = Rigid RigidSource | Nonrigid+  deriving (Eq, Ord, Show)++prettySource :: SrcLoc -> SrcLoc -> RigidSource -> Doc+prettySource ctx loc (RigidRet Nothing) =+  "is unknown size returned by function at"+    <+> text (locStrRel ctx loc) <> "."+prettySource ctx loc (RigidRet (Just fname)) =+  "is unknown size returned by" <+> pquote (ppr fname)+    <+> "at"+    <+> text (locStrRel ctx loc) <> "."+prettySource ctx loc (RigidArg fname arg) =+  "is value of argument"+    </> indent 2 (shorten arg)+    </> "passed to" <+> fname' <+> "at" <+> text (locStrRel ctx loc) <> "."+  where+    fname' = maybe "function" (pquote . ppr) fname+prettySource ctx loc (RigidSlice d slice) =+  "is size produced by slice"+    </> indent 2 (shorten slice)+    </> d_desc <> "at" <+> text (locStrRel ctx loc) <> "."+  where+    d_desc = case d of+      Just d' -> "of dimension of size " <> pquote (ppr d') <> " "+      Nothing -> mempty+prettySource ctx loc RigidLoop =+  "is unknown size of value returned at" <+> text (locStrRel ctx loc) <> "."+prettySource ctx loc RigidRange =+  "is unknown length of range at" <+> text (locStrRel ctx loc) <> "."+prettySource ctx loc (RigidBound bound) =+  "generated from expression"+    </> indent 2 (shorten bound)+    </> "used in range at " <> text (locStrRel ctx loc) <> "."+prettySource ctx loc (RigidOutOfScope boundloc v) =+  "is an unknown size arising from " <> pquote (pprName v)+    <> " going out of scope at "+    <> text (locStrRel ctx loc)+    <> "."+    </> "Originally bound at "+    <> text (locStrRel ctx boundloc)+    <> "."+prettySource _ _ RigidUnify =+  "is an artificial size invented during unification of functions with anonymous sizes"+prettySource ctx loc (RigidCond t1 t2) =+  "is unknown due to conditional expression at "+    <> text (locStrRel ctx loc)+    <> "."+    </> "One branch returns array of type: "+    <> align (ppr t1)+    </> "The other an array of type:       "+    <> align (ppr t2)++-- | Retrieve notes describing the purpose or origin of the given+-- 'DimDecl'.  The location is used as the *current* location, for the+-- purpose of reporting relative locations.+dimNotes :: (Located a, MonadUnify m) => a -> DimDecl VName -> m Notes+dimNotes ctx (NamedDim d) = do+  c <- M.lookup (qualLeaf d) <$> getConstraints+  case c of+    Just (_, UnknowableSize loc rsrc) ->+      return $+        aNote $+          pretty $+            pquote (ppr d) <+> prettySource (srclocOf ctx) loc rsrc+    _ -> return mempty+dimNotes _ _ = return mempty++typeNotes :: (Located a, MonadUnify m) => a -> StructType -> m Notes+typeNotes ctx =+  fmap mconcat . mapM (dimNotes ctx . NamedDim . qualName)+    . S.toList+    . typeDimNames++-- | Monads that which to perform unification must implement this type+-- class.+class Monad m => MonadUnify m where+  getConstraints :: m Constraints+  putConstraints :: Constraints -> m ()+  modifyConstraints :: (Constraints -> Constraints) -> m ()+  modifyConstraints f = do+    x <- getConstraints+    putConstraints $ f x++  newTypeVar :: Monoid als => SrcLoc -> String -> m (TypeBase dim als)+  newDimVar :: SrcLoc -> Rigidity -> String -> m VName++  curLevel :: m Level++  matchError ::+    Located loc =>+    loc ->+    Notes ->+    BreadCrumbs ->+    StructType ->+    StructType ->+    m a++  unifyError ::+    Located loc =>+    loc ->+    Notes ->+    BreadCrumbs ->+    Doc ->+    m a++-- | Replace all type variables with their substitution.+normTypeFully :: (Substitutable a, MonadUnify m) => a -> m a+normTypeFully t = do+  constraints <- getConstraints+  return $ applySubst (`lookupSubst` constraints) t++-- | Replace any top-level type variable with its substitution.+normType :: MonadUnify m => StructType -> m StructType+normType t@(Scalar (TypeVar _ _ (TypeName [] v) [])) = do+  constraints <- getConstraints+  case snd <$> M.lookup v constraints of+    Just (Constraint t' _) -> normType t'+    _ -> return t+normType t = return t++-- | Replace any top-level type variable with its substitution.+normPatternType :: MonadUnify m => PatternType -> m PatternType+normPatternType t@(Scalar (TypeVar als u (TypeName [] v) [])) = do+  constraints <- getConstraints+  case snd <$> M.lookup v constraints of+    Just (Constraint t' _) ->+      normPatternType $ t' `setUniqueness` u `setAliases` als+    _ -> return t+normPatternType t = return t++rigidConstraint :: Constraint -> Bool+rigidConstraint ParamType {} = True+rigidConstraint ParamSize {} = True+rigidConstraint UnknowableSize {} = True+rigidConstraint _ = False++-- | Replace 'AnyDim' dimensions that occur as 'PosImmediate' or+-- 'PosParam' with a fresh 'NamedDim'.+instantiateEmptyArrayDims ::+  MonadUnify m =>+  SrcLoc ->+  String ->+  Rigidity ->+  TypeBase (DimDecl VName) als ->+  m (TypeBase (DimDecl VName) als, [VName])+instantiateEmptyArrayDims tloc desc r = runWriterT . traverseDims onDim+  where+    onDim _ PosImmediate AnyDim = inst+    onDim _ PosParam AnyDim = inst+    onDim _ _ d = return d+    inst = do+      dim <- lift $ newDimVar tloc r desc+      tell [dim]+      return $ NamedDim $ qualName dim++-- | Is the given type variable the name of an abstract type or type+-- parameter, which we cannot substitute?+isRigid :: VName -> Constraints -> Bool+isRigid v constraints =+  maybe True (rigidConstraint . snd) $ M.lookup v constraints++-- | If the given type variable is nonrigid, what is its level?+isNonRigid :: VName -> Constraints -> Maybe Level+isNonRigid v constraints = do+  (lvl, c) <- M.lookup v constraints+  guard $ not $ rigidConstraint c+  return lvl++type UnifyDims m =+  BreadCrumbs -> [VName] -> (VName -> Maybe Int) -> DimDecl VName -> DimDecl VName -> m ()++flipUnifyDims :: UnifyDims m -> UnifyDims m+flipUnifyDims onDims bcs bound nonrigid t1 t2 =+  onDims bcs bound nonrigid t2 t1++unifyWith ::+  MonadUnify m =>+  UnifyDims m ->+  Usage ->+  BreadCrumbs ->+  StructType ->+  StructType ->+  m ()+unifyWith onDims usage = subunify False mempty+  where+    swap True x y = (y, x)+    swap False x y = (x, y)++    subunify ord bound bcs t1 t2 = do+      constraints <- getConstraints++      t1' <- normType t1+      t2' <- normType t2++      let nonrigid v = isNonRigid v constraints++          failure = matchError (srclocOf usage) mempty bcs t1' t2'++          -- Remove any of the intermediate dimensions we added just+          -- for unification purposes.+          unbound = applySubst f+            where+              f d+                | d `elem` bound = Just $ SizeSubst AnyDim+                | otherwise = Nothing++          link ord' v lvl =+            linkVarToType linkDims usage bcs v lvl . unbound+            where+              -- We may have to flip the order of future calls to+              -- onDims inside linkVarToType.+              linkDims+                | ord' = flipUnifyDims onDims+                | otherwise = onDims++          unifyTypeArg bcs' (TypeArgDim d1 _) (TypeArgDim d2 _) =+            onDims' bcs' (swap ord d1 d2)+          unifyTypeArg bcs' (TypeArgType t _) (TypeArgType arg_t _) =+            subunify ord bound bcs' t arg_t+          unifyTypeArg bcs' _ _ =+            unifyError+              usage+              mempty+              bcs'+              "Cannot unify a type argument with a dimension argument (or vice versa)."++          onDims' bcs' (d1, d2) =+            onDims+              bcs'+              bound+              nonrigid+              (applySubst (`lookupSubst` constraints) d1)+              (applySubst (`lookupSubst` constraints) d2)++      case (t1', t2') of+        ( Scalar (Record fs),+          Scalar (Record arg_fs)+          )+            | M.keys fs == M.keys arg_fs ->+              forM_ (M.toList $ M.intersectionWith (,) fs arg_fs) $ \(k, (k_t1, k_t2)) -> do+                let bcs' = breadCrumb (MatchingFields [k]) bcs+                subunify ord bound bcs' k_t1 k_t2+            | otherwise -> do+              let missing =+                    filter (`notElem` M.keys arg_fs) (M.keys fs)+                      ++ filter (`notElem` M.keys fs) (M.keys arg_fs)+              unifyError usage mempty bcs $+                "Unshared fields:" <+> commasep (map ppr missing) <> "."+        ( Scalar (TypeVar _ _ (TypeName _ tn) targs),+          Scalar (TypeVar _ _ (TypeName _ arg_tn) arg_targs)+          )+            | tn == arg_tn,+              length targs == length arg_targs -> do+              let bcs' = breadCrumb (Matching "When matching type arguments.") bcs+              zipWithM_ (unifyTypeArg bcs') targs arg_targs+        ( Scalar (TypeVar _ _ (TypeName [] v1) []),+          Scalar (TypeVar _ _ (TypeName [] v2) [])+          ) ->+            case (nonrigid v1, nonrigid v2) of+              (Nothing, Nothing) -> failure+              (Just lvl1, Nothing) -> link ord v1 lvl1 t2'+              (Nothing, Just lvl2) -> link (not ord) v2 lvl2 t1'+              (Just lvl1, Just lvl2)+                | lvl1 <= lvl2 -> link ord v1 lvl1 t2'+                | otherwise -> link (not ord) v2 lvl2 t1'+        (Scalar (TypeVar _ _ (TypeName [] v1) []), _)+          | Just lvl <- nonrigid v1 ->+            link ord v1 lvl t2'+        (_, Scalar (TypeVar _ _ (TypeName [] v2) []))+          | Just lvl <- nonrigid v2 ->+            link (not ord) v2 lvl t1'+        ( Scalar (Arrow _ p1 a1 b1),+          Scalar (Arrow _ p2 a2 b2)+          ) -> do+            let (r1, r2) = swap ord (Rigid RigidUnify) Nonrigid+            (a1', a1_dims) <- instantiateEmptyArrayDims (srclocOf usage) "anonymous" r1 a1+            (a2', a2_dims) <- instantiateEmptyArrayDims (srclocOf usage) "anonymous" r2 a2+            let bound' = bound <> mapMaybe pname [p1, p2] <> a1_dims <> a2_dims+            subunify+              (not ord)+              bound+              (breadCrumb (Matching "When matching parameter types.") bcs)+              a1'+              a2'+            subunify+              ord+              bound'+              (breadCrumb (Matching "When matching return types.") bcs)+              b1'+              b2'+            where+              (b1', b2') =+                -- Replace one parameter name with the other in the+                -- return type, in case of dependent types.  I.e.,+                -- we want type '(n: i32) -> [n]i32' to unify with+                -- type '(x: i32) -> [x]i32'.+                case (p1, p2) of+                  (Named p1', Named p2') ->+                    let f v+                          | v == p2' = Just $ SizeSubst $ NamedDim $ qualName p1'+                          | otherwise = Nothing+                     in (b1, applySubst f b2)+                  (_, _) ->+                    (b1, b2)++              pname (Named x) = Just x+              pname Unnamed = Nothing+        (Array {}, Array {})+          | ShapeDecl (t1_d : _) <- arrayShape t1',+            ShapeDecl (t2_d : _) <- arrayShape t2',+            Just t1'' <- peelArray 1 t1',+            Just t2'' <- peelArray 1 t2' -> do+            onDims' bcs (swap ord t1_d t2_d)+            subunify ord bound bcs t1'' t2''+        ( Scalar (Sum cs),+          Scalar (Sum arg_cs)+          )+            | M.keys cs == M.keys arg_cs ->+              unifySharedConstructors+                onDims+                usage+                bcs+                (map unbound <$> cs)+                (map unbound <$> arg_cs)+            | otherwise -> do+              let missing =+                    filter (`notElem` M.keys arg_cs) (M.keys cs)+                      ++ filter (`notElem` M.keys cs) (M.keys arg_cs)+              unifyError usage mempty bcs $+                "Unshared constructors:" <+> commasep (map (("#" <>) . ppr) missing) <> "."+        _+          | t1' == t2' -> return ()+          | otherwise -> failure++unifyDims :: MonadUnify m => Usage -> UnifyDims m+unifyDims _ _ _ _ d1 d2+  | d1 == d2 = return ()+unifyDims usage bcs _ nonrigid (NamedDim (QualName _ d1)) d2+  | Just lvl1 <- nonrigid d1 =+    linkVarToDim usage bcs d1 lvl1 d2+unifyDims usage bcs _ nonrigid d1 (NamedDim (QualName _ d2))+  | Just lvl2 <- nonrigid d2 =+    linkVarToDim usage bcs d2 lvl2 d1+unifyDims usage bcs _ _ d1 d2 = do+  notes <- (<>) <$> dimNotes usage d1 <*> dimNotes usage d2+  unifyError usage notes bcs $+    "Dimensions" <+> pquote (ppr d1)+      <+> "and"+      <+> pquote (ppr d2)+      <+> "do not match."++-- | Unifies two types.+unify :: MonadUnify m => Usage -> StructType -> StructType -> m ()+unify usage = unifyWith (unifyDims usage) usage noBreadCrumbs++-- | @expect super sub@ checks that @sub@ is a subtype of @super@.+expect :: MonadUnify m => Usage -> StructType -> StructType -> m ()+expect usage = unifyWith onDims usage noBreadCrumbs+  where+    onDims _ _ _ AnyDim _ = return ()+    onDims _ _ _ d1 d2+      | d1 == d2 = return ()+    onDims bcs bound nonrigid (NamedDim (QualName _ d1)) d2+      | Just lvl1 <- nonrigid d1,+        d2 /= AnyDim,+        not $ boundParam bound d2 =+        linkVarToDim usage bcs d1 lvl1 d2+    onDims bcs bound nonrigid d1 (NamedDim (QualName _ d2))+      | Just lvl2 <- nonrigid d2,+        not $ boundParam bound d1 =+        linkVarToDim usage bcs d2 lvl2 d1+    onDims bcs _ _ d1 d2 = do+      notes <- (<>) <$> dimNotes usage d1 <*> dimNotes usage d2+      unifyError usage notes bcs $+        "Dimensions" <+> pquote (ppr d1)+          <+> "and"+          <+> pquote (ppr d2)+          <+> "do not match."++    boundParam bound (NamedDim (QualName _ d)) = d `elem` bound+    boundParam _ _ = False++hasEmptyDims :: StructType -> Bool+hasEmptyDims = biany empty (const False)+  where+    empty AnyDim = True+    empty _ = False++occursCheck ::+  MonadUnify m =>+  Usage ->+  BreadCrumbs ->+  VName ->+  StructType ->+  m ()+occursCheck usage bcs vn tp =+  when (vn `S.member` typeVars tp) $+    unifyError usage mempty bcs $+      "Occurs check: cannot instantiate"+        <+> pprName vn+        <+> "with"+        <+> ppr tp <> "."++scopeCheck ::+  MonadUnify m =>+  Usage ->+  BreadCrumbs ->+  VName ->+  Level ->+  StructType ->+  m ()+scopeCheck usage bcs vn max_lvl tp = do+  constraints <- getConstraints+  checkType constraints tp+  where+    checkType constraints t =+      mapM_ (check constraints) $ typeVars t <> typeDimNames t++    check constraints v+      | Just (lvl, c) <- M.lookup v constraints,+        lvl > max_lvl =+        if rigidConstraint c+          then scopeViolation v+          else modifyConstraints $ M.insert v (max_lvl, c)+      | otherwise =+        return ()++    scopeViolation v = do+      notes <- typeNotes usage tp+      unifyError usage notes bcs $+        "Cannot unify type"+          </> indent 2 (ppr tp)+          </> "with"+          <+> pquote (pprName vn)+          <+> "(scope violation)."+          </> "This is because"+          <+> pquote (pprName v)+          <+> "is rigidly bound in a deeper scope."++linkVarToType ::+  MonadUnify m =>+  UnifyDims m ->+  Usage ->+  BreadCrumbs ->+  VName ->+  Level ->+  StructType ->+  m ()+linkVarToType onDims usage bcs vn lvl tp = do+  occursCheck usage bcs vn tp+  scopeCheck usage bcs vn lvl tp++  constraints <- getConstraints+  let tp' = removeUniqueness tp+  modifyConstraints $ M.insert vn (lvl, Constraint tp' usage)+  case snd <$> M.lookup vn constraints of+    Just (NoConstraint Unlifted unlift_usage) -> do+      let bcs' =+            breadCrumb+              ( Matching $+                  "When verifying that" <+> pquote (pprName vn)+                    <+> textwrap "is not instantiated with a function type, due to"+                    <+> ppr unlift_usage+              )+              bcs+      zeroOrderTypeWith usage bcs' tp'++      when (hasEmptyDims tp') $+        unifyError usage mempty bcs $+          "Type variable" <+> pprName vn+            <+> "cannot be instantiated with type containing anonymous sizes:"+            </> indent 2 (ppr tp)+            </> textwrap "This is usually because the size of an array returned by a higher-order function argument cannot be determined statically.  This can also be due to the return size being a value parameter.  Add type annotation to clarify."+    Just (Equality _) ->+      equalityType usage tp'+    Just (Overloaded ts old_usage)+      | tp `notElem` map (Scalar . Prim) ts ->+        case tp' of+          Scalar (TypeVar _ _ (TypeName [] v) [])+            | not $ isRigid v constraints ->+              linkVarToTypes usage v ts+          _ ->+            unifyError usage mempty bcs $+              "Cannot instantiate" <+> pquote (pprName vn)+                <+> "with type" </> indent 2 (ppr tp) </> "as"+                <+> pquote (pprName vn)+                <+> "must be one of"+                <+> commasep (map ppr ts)+                <+/> "due to"+                <+/> ppr old_usage <> "."+    Just (HasFields required_fields old_usage) ->+      case tp of+        Scalar (Record tp_fields)+          | all (`M.member` tp_fields) $ M.keys required_fields -> do+            required_fields' <- mapM normTypeFully required_fields+            let bcs' =+                  breadCrumb+                    ( Matching $+                        pprName vn+                          <+> "must be a record with at least the fields:"+                          </> indent 2 (ppr (Record required_fields'))+                          </> "due to"+                          <+> ppr old_usage <> "."+                    )+                    bcs+            mapM_ (uncurry $ unifyWith onDims usage bcs') $+              M.elems $+                M.intersectionWith (,) required_fields tp_fields+        Scalar (TypeVar _ _ (TypeName [] v) [])+          | not $ isRigid v constraints ->+            modifyConstraints $+              M.insert+                v+                (lvl, HasFields required_fields old_usage)+        _ ->+          unifyError usage mempty bcs $+            "Cannot instantiate" <+> pquote (pprName vn) <+> "with type"+              </> indent 2 (ppr tp)+              </> "as" <+> pquote (pprName vn) <+> "must be a record with fields"+              </> indent 2 (ppr (Record required_fields))+              </> "due to" <+> ppr old_usage <> "."+    Just (HasConstrs required_cs old_usage) ->+      case tp of+        Scalar (Sum ts)+          | all (`M.member` ts) $ M.keys required_cs ->+            unifySharedConstructors onDims usage bcs required_cs ts+        Scalar (TypeVar _ _ (TypeName [] v) [])+          | not $ isRigid v constraints -> do+            case M.lookup v constraints of+              Just (_, HasConstrs v_cs _) ->+                unifySharedConstructors onDims usage bcs required_cs v_cs+              _ -> return ()+            modifyConstraints $+              M.insertWith+                combineConstrs+                v+                (lvl, HasConstrs required_cs old_usage)+          where+            combineConstrs (_, HasConstrs cs1 usage1) (_, HasConstrs cs2 _) =+              (lvl, HasConstrs (M.union cs1 cs2) usage1)+            combineConstrs hasCs _ = hasCs+        _ -> noSumType+    _ -> return ()+  where+    noSumType =+      unifyError+        usage+        mempty+        bcs+        "Cannot unify a sum type with a non-sum type"++linkVarToDim ::+  MonadUnify m =>+  Usage ->+  BreadCrumbs ->+  VName ->+  Level ->+  DimDecl VName ->+  m ()+linkVarToDim usage bcs vn lvl dim = do+  constraints <- getConstraints++  case dim of+    NamedDim dim'+      | Just (dim_lvl, c) <- qualLeaf dim' `M.lookup` constraints,+        dim_lvl > lvl ->+        case c of+          ParamSize {} -> do+            notes <- dimNotes usage dim+            unifyError usage notes bcs $+              "Cannot unify size variable" <+> pquote (ppr dim')+                <+> "with"+                <+> pquote (pprName vn)+                <+> "(scope violation)."+                </> "This is because"+                <+> pquote (ppr dim')+                <+> "is rigidly bound in a deeper scope."+          _ -> modifyConstraints $ M.insert (qualLeaf dim') (lvl, c)+    _ -> return ()++  modifyConstraints $ M.insert vn (lvl, Size (Just dim) usage)++removeUniqueness :: TypeBase dim as -> TypeBase dim as+removeUniqueness (Scalar (Record ets)) =+  Scalar $ Record $ fmap removeUniqueness ets+removeUniqueness (Scalar (Arrow als p t1 t2)) =+  Scalar $ Arrow als p (removeUniqueness t1) (removeUniqueness t2)+removeUniqueness (Scalar (Sum cs)) =+  Scalar $ Sum $ (fmap . fmap) removeUniqueness cs+removeUniqueness t = t `setUniqueness` Nonunique++-- | Assert that this type must be one of the given primitive types.+mustBeOneOf :: MonadUnify m => [PrimType] -> Usage -> StructType -> m ()+mustBeOneOf [req_t] usage t = unify usage (Scalar (Prim req_t)) t+mustBeOneOf ts usage t = do+  t' <- normType t+  constraints <- getConstraints+  let isRigid' v = isRigid v constraints++  case t' of+    Scalar (TypeVar _ _ (TypeName [] v) [])+      | not $ isRigid' v -> linkVarToTypes usage v ts+    Scalar (Prim pt) | pt `elem` ts -> return ()+    _ -> failure+  where+    failure =+      unifyError usage mempty noBreadCrumbs $+        text "Cannot unify type" <+> pquote (ppr t)+          <+> "with any of " <> commasep (map ppr ts) <> "."++linkVarToTypes :: MonadUnify m => Usage -> VName -> [PrimType] -> m ()+linkVarToTypes usage vn ts = do+  vn_constraint <- M.lookup vn <$> getConstraints+  case vn_constraint of+    Just (lvl, Overloaded vn_ts vn_usage) ->+      case ts `intersect` vn_ts of+        [] ->+          unifyError usage mempty noBreadCrumbs $+            "Type constrained to one of"+              <+> commasep (map ppr ts)+              <+> "but also one of"+              <+> commasep (map ppr vn_ts)+              <+> "due to"+              <+> ppr vn_usage <> "."+        ts' -> modifyConstraints $ M.insert vn (lvl, Overloaded ts' usage)+    Just (_, HasConstrs _ vn_usage) ->+      unifyError usage mempty noBreadCrumbs $+        "Type constrained to one of" <+> commasep (map ppr ts)+          <> ", but also inferred to be sum type due to" <+> ppr vn_usage+          <> "."+    Just (_, HasFields _ vn_usage) ->+      unifyError usage mempty noBreadCrumbs $+        "Type constrained to one of" <+> commasep (map ppr ts)+          <> ", but also inferred to be record due to" <+> ppr vn_usage+          <> "."+    Just (lvl, _) -> modifyConstraints $ M.insert vn (lvl, Overloaded ts usage)+    Nothing ->+      unifyError usage mempty noBreadCrumbs $+        "Cannot constrain type to one of" <+> commasep (map ppr ts)++-- | Assert that this type must support equality.+equalityType ::+  (MonadUnify m, Pretty (ShapeDecl dim), Monoid as) =>+  Usage ->+  TypeBase dim as ->+  m ()+equalityType usage t = do+  unless (orderZero t) $+    unifyError usage mempty noBreadCrumbs $+      "Type " <+> pquote (ppr t) <+> "does not support equality (is higher-order)."+  mapM_ mustBeEquality $ typeVars t+  where+    mustBeEquality vn = do+      constraints <- getConstraints+      case M.lookup vn constraints of+        Just (_, Constraint (Scalar (TypeVar _ _ (TypeName [] vn') [])) _) ->+          mustBeEquality vn'+        Just (_, Constraint vn_t cusage)+          | not $ orderZero vn_t ->+            unifyError usage mempty noBreadCrumbs $+              "Type" <+> pquote (ppr t) <+> "does not support equality."+                </> "Constrained to be higher-order due to" <+> ppr cusage <+> "."+          | otherwise -> return ()+        Just (lvl, NoConstraint _ _) ->+          modifyConstraints $ M.insert vn (lvl, Equality usage)+        Just (_, Overloaded _ _) ->+          return () -- All primtypes support equality.+        Just (_, Equality {}) ->+          return ()+        Just (_, HasConstrs cs _) ->+          mapM_ (equalityType usage) $ concat $ M.elems cs+        _ ->+          unifyError usage mempty noBreadCrumbs $+            "Type" <+> pprName vn <+> "does not support equality."++zeroOrderTypeWith ::+  (MonadUnify m, Pretty (ShapeDecl dim), Monoid as) =>+  Usage ->+  BreadCrumbs ->+  TypeBase dim as ->+  m ()+zeroOrderTypeWith usage bcs t = do+  unless (orderZero t) $+    unifyError usage mempty bcs $+      "Type" </> indent 2 (ppr t) </> "found to be functional."+  mapM_ mustBeZeroOrder . S.toList . typeVars $ t+  where+    mustBeZeroOrder vn = do+      constraints <- getConstraints+      case M.lookup vn constraints of+        Just (lvl, NoConstraint _ _) ->+          modifyConstraints $ M.insert vn (lvl, NoConstraint Unlifted usage)+        Just (_, ParamType Lifted ploc) ->+          unifyError usage mempty bcs $+            "Type parameter"+              <+> pquote (pprName vn)+              <+> "at"+              <+> text (locStr ploc)+              <+> "may be a function."+        _ -> return ()++-- | Assert that this type must be zero-order.+zeroOrderType ::+  (MonadUnify m, Pretty (ShapeDecl dim), Monoid as) =>+  Usage ->+  String ->+  TypeBase dim as ->+  m ()+zeroOrderType usage desc =+  zeroOrderTypeWith usage $ breadCrumb bc noBreadCrumbs+  where+    bc = Matching $ "When checking" <+> textwrap desc++unifySharedConstructors ::+  MonadUnify m =>+  UnifyDims m ->+  Usage ->+  BreadCrumbs ->+  M.Map Name [StructType] ->+  M.Map Name [StructType] ->+  m ()+unifySharedConstructors onDims usage bcs cs1 cs2 =+  forM_ (M.toList $ M.intersectionWith (,) cs1 cs2) $ \(c, (f1, f2)) ->+    unifyConstructor c f1 f2+  where+    unifyConstructor c f1 f2+      | length f1 == length f2 = do+        let bcs' = breadCrumb (MatchingConstructor c) bcs+        zipWithM_ (unifyWith onDims usage bcs') f1 f2+      | otherwise =+        unifyError usage mempty bcs $+          "Cannot unify constructor" <+> pquote (pprName c) <> "."++-- | In @mustHaveConstr usage c t fs@, the type @t@ must have a+-- constructor named @c@ that takes arguments of types @ts@.+mustHaveConstr ::+  MonadUnify m =>+  Usage ->+  Name ->+  StructType ->+  [StructType] ->+  m ()+mustHaveConstr usage c t fs = do+  constraints <- getConstraints+  case t of+    Scalar (TypeVar _ _ (TypeName _ tn) [])+      | Just (lvl, NoConstraint {}) <- M.lookup tn constraints -> do+        mapM_ (scopeCheck usage noBreadCrumbs tn lvl) fs+        modifyConstraints $ M.insert tn (lvl, HasConstrs (M.singleton c fs) usage)+      | Just (lvl, HasConstrs cs _) <- M.lookup tn constraints ->+        case M.lookup c cs of+          Nothing -> modifyConstraints $ M.insert tn (lvl, HasConstrs (M.insert c fs cs) usage)+          Just fs'+            | length fs == length fs' -> zipWithM_ (unify usage) fs fs'+            | otherwise ->+              unifyError usage mempty noBreadCrumbs $+                "Different arity for constructor" <+> pquote (ppr c) <> "."+    Scalar (Sum cs) ->+      case M.lookup c cs of+        Nothing ->+          unifyError usage mempty noBreadCrumbs $+            "Constuctor" <+> pquote (ppr c) <+> "not present in type."+        Just fs'+          | length fs == length fs' -> zipWithM_ (unify usage) fs fs'+          | otherwise ->+            unifyError usage mempty noBreadCrumbs $+              "Different arity for constructor" <+> pquote (ppr c) <+> "."+    _ -> do+      unify usage t $ Scalar $ Sum $ M.singleton c fs+      return ()++mustHaveFieldWith ::+  MonadUnify m =>+  UnifyDims m ->+  Usage ->+  BreadCrumbs ->+  Name ->+  PatternType ->+  m PatternType+mustHaveFieldWith onDims usage bcs l t = do+  constraints <- getConstraints+  l_type <- newTypeVar (srclocOf usage) "t"+  let l_type' = toStruct l_type+  case t of+    Scalar (TypeVar _ _ (TypeName _ tn) [])+      | Just (lvl, NoConstraint {}) <- M.lookup tn constraints -> do+        scopeCheck usage bcs tn lvl l_type'+        modifyConstraints $ M.insert tn (lvl, HasFields (M.singleton l l_type') usage)+        return l_type+      | Just (lvl, HasFields fields _) <- M.lookup tn constraints -> do+        case M.lookup l fields of+          Just t' -> unifyWith onDims usage bcs l_type' t'+          Nothing ->+            modifyConstraints $+              M.insert+                tn+                (lvl, HasFields (M.insert l l_type' fields) usage)+        return l_type+    Scalar (Record fields)+      | Just t' <- M.lookup l fields -> do+        unify usage l_type' $ toStruct t'+        return t'+      | otherwise ->+        unifyError usage mempty bcs $+          "Attempt to access field" <+> pquote (ppr l) <+> " of value of type"+            <+> ppr (toStructural t) <> "."+    _ -> do+      unify usage (toStruct t) $ Scalar $ Record $ M.singleton l l_type'+      return l_type++-- | Assert that some type must have a field with this name and type.+mustHaveField ::+  MonadUnify m =>+  Usage ->+  Name ->+  PatternType ->+  m PatternType+mustHaveField usage = mustHaveFieldWith (unifyDims usage) usage noBreadCrumbs++-- | Replace dimension mismatches with AnyDim.+anyDimOnMismatch ::+  Monoid as =>+  TypeBase (DimDecl VName) as ->+  TypeBase (DimDecl VName) as ->+  (TypeBase (DimDecl VName) as, [(DimDecl VName, DimDecl VName)])+anyDimOnMismatch t1 t2 = runWriter $ matchDims onDims t1 t2+  where+    onDims d1 d2+      | d1 == d2 = return d1+      | otherwise = do+        tell [(d1, d2)]+        return AnyDim++newDimOnMismatch ::+  (Monoid as, MonadUnify m) =>+  SrcLoc ->+  TypeBase (DimDecl VName) as ->+  TypeBase (DimDecl VName) as ->+  m (TypeBase (DimDecl VName) as, [VName])+newDimOnMismatch loc t1 t2 = do+  (t, seen) <- runStateT (matchDims onDims t1 t2) mempty+  return (t, M.elems seen)+  where+    r = Rigid $ RigidCond (toStruct t1) (toStruct t2)+    onDims d1 d2+      | d1 == d2 = return d1+      | otherwise = do+        -- Remember mismatches we have seen before and reuse the+        -- same new size.+        maybe_d <- gets $ M.lookup (d1, d2)+        case maybe_d of+          Just d -> return $ NamedDim $ qualName d+          Nothing -> do+            d <- lift $ newDimVar loc r "differ"+            modify $ M.insert (d1, d2) d+            return $ NamedDim $ qualName d++-- | Like unification, but creates new size variables where mismatches+-- occur.  Returns the new dimensions thus created.+unifyMostCommon ::+  MonadUnify m =>+  Usage ->+  PatternType ->+  PatternType ->+  m (PatternType, [VName])+unifyMostCommon usage t1 t2 = do+  -- We are ignoring the dimensions here, because any mismatches+  -- should be turned into fresh size variables.+  let allOK _ _ _ _ _ = return ()+  unifyWith allOK usage noBreadCrumbs (toStruct t1) (toStruct t2)+  t1' <- normTypeFully t1+  t2' <- normTypeFully t2+  newDimOnMismatch (srclocOf usage) t1' t2'++-- Simple MonadUnify implementation.++type UnifyMState = (Constraints, Int)++newtype UnifyM a = UnifyM (StateT UnifyMState (Except TypeError) a)+  deriving+    ( Monad,+      Functor,+      Applicative,+      MonadState UnifyMState,+      MonadError TypeError+    )++newVar :: String -> UnifyM VName+newVar name = do+  (x, i) <- get+  put (x, i + 1)+  return $ VName (mkTypeVarName name i) i++instance MonadUnify UnifyM where+  getConstraints = gets fst+  putConstraints x = modify $ \(_, i) -> (x, i)++  newTypeVar loc name = do+    v <- newVar name+    modifyConstraints $ M.insert v (0, NoConstraint Lifted $ Usage Nothing loc)+    return $ Scalar $ TypeVar mempty Nonunique (typeName v) []++  newDimVar loc rigidity name = do+    dim <- newVar name+    case rigidity of+      Rigid src -> modifyConstraints $ M.insert dim (0, UnknowableSize loc src)+      Nonrigid -> modifyConstraints $ M.insert dim (0, Size Nothing $ Usage Nothing loc)+    return dim++  curLevel = pure 0++  unifyError loc notes bcs doc =+    throwError $ TypeError (srclocOf loc) notes $ doc <> ppr bcs++  matchError loc notes bcs t1 t2 =+    throwError $ TypeError (srclocOf loc) notes $ doc <> ppr bcs+    where+      doc =+        "Types"+          </> indent 2 (ppr t1)+          </> "and"+          </> indent 2 (ppr t2)+          </> "do not match."++-- | Construct a the name of a new type variable given a base+-- description and a tag number (note that this is distinct from+-- actually constructing a VName; the tag here is intended for human+-- consumption but the machine does not care).+mkTypeVarName :: String -> Int -> Name+mkTypeVarName desc i =+  nameFromString $ desc ++ mapMaybe subscript (show i)+  where+    subscript = flip lookup $ zip "0123456789" "₀₁₂₃₄₅₆₇₈₉"++runUnifyM :: [TypeParam] -> UnifyM a -> Either TypeError a+runUnifyM tparams (UnifyM m) = runExcept $ evalStateT m (constraints, 0)+  where+    constraints = M.fromList $ map f tparams+    f (TypeParamDim p loc) = (p, (0, Size Nothing $ Usage Nothing loc))+    f (TypeParamType l p loc) = (p, (0, NoConstraint l $ Usage Nothing loc))++-- | Perform a unification of two types outside a monadic context.+-- The type parameters are allowed to be instantiated; all other types+-- are considered rigid.+doUnification ::+  SrcLoc ->+  [TypeParam] ->+  StructType ->+  StructType ->+  Either TypeError StructType doUnification loc tparams t1 t2 = runUnifyM tparams $ do   let rsrc = RigidUnify   (t1', _) <- instantiateEmptyArrayDims loc "n" (Rigid rsrc) t1
src/Language/Futhark/Warnings.hs view
@@ -1,21 +1,21 @@ {-# LANGUAGE Safe #-}+ -- | A very simple representation of collections of warnings. -- Warnings have a position (so they can be ordered), and their -- 'Show'-instance produces a human-readable string. module Language.Futhark.Warnings-  ( Warnings-  , singleWarning-  , singleWarning'-  ) where+  ( Warnings,+    singleWarning,+    singleWarning',+  )+where +import Data.List (intercalate, sortOn) import Data.Monoid-import Data.List (sortOn, intercalate)--import Prelude--import Language.Futhark.Core (locStr, prettyStacktrace) import Futhark.Util.Console (inRed) import Futhark.Util.Loc+import Language.Futhark.Core (locStr, prettyStacktrace)+import Prelude  -- | The warnings produced by the compiler.  The 'Show' instance -- produces a human-readable description.@@ -31,17 +31,20 @@   show (Warnings []) = ""   show (Warnings ws) =     intercalate "\n\n" ws' ++ "\n"-    where ws' = map showWarning $ sortOn (rep . wloc) ws-          wloc (x, _, _) = locOf x-          rep NoLoc = ("", 0)-          rep (Loc p _) = (posFile p, posCoff p)-          showWarning (loc, [], w) =-            inRed ("Warning at " ++ locStr loc ++ ":") ++ "\n" ++-            intercalate "\n" (map ("  "<>) $ lines w)-          showWarning (loc, locs, w) =-            inRed ("Warning at\n" ++-                   prettyStacktrace 0 (map locStr (loc:locs))) ++-            intercalate "\n" (map ("  "<>) $ lines w)+    where+      ws' = map showWarning $ sortOn (rep . wloc) ws+      wloc (x, _, _) = locOf x+      rep NoLoc = ("", 0)+      rep (Loc p _) = (posFile p, posCoff p)+      showWarning (loc, [], w) =+        inRed ("Warning at " ++ locStr loc ++ ":") ++ "\n"+          ++ intercalate "\n" (map ("  " <>) $ lines w)+      showWarning (loc, locs, w) =+        inRed+          ( "Warning at\n"+              ++ prettyStacktrace 0 (map locStr (loc : locs))+          )+          ++ intercalate "\n" (map ("  " <>) $ lines w)  -- | A single warning at the given location. singleWarning :: SrcLoc -> String -> Warnings
src/futhark.hs view
@@ -1,108 +1,105 @@ {-# LANGUAGE OverloadedStrings #-}+ -- | The futhark command line tool. module Main (main) where -import Data.Maybe import Control.Exception import Control.Monad import Data.List (sortOn)+import Data.Maybe import qualified Data.Text as T import qualified Data.Text.IO as T-import GHC.IO.Encoding (setLocaleEncoding)-import System.IO-import System.Exit-import System.Environment--import Prelude--import Futhark.Error-import Futhark.Util.Options-import Futhark.Util (maxinum)--import qualified Futhark.CLI.Dev as Dev+import qualified Futhark.CLI.Autotune as Autotune+import qualified Futhark.CLI.Bench as Bench import qualified Futhark.CLI.C as C-import qualified Futhark.CLI.OpenCL as OpenCL import qualified Futhark.CLI.CUDA as CCUDA-import qualified Futhark.CLI.Python as Python-import qualified Futhark.CLI.PyOpenCL as PyOpenCL-import qualified Futhark.CLI.Test as Test-import qualified Futhark.CLI.Bench as Bench import qualified Futhark.CLI.Check as Check-import qualified Futhark.CLI.Dataset as Dataset import qualified Futhark.CLI.Datacmp as Datacmp-import qualified Futhark.CLI.Pkg as Pkg+import qualified Futhark.CLI.Dataset as Dataset+import qualified Futhark.CLI.Dev as Dev import qualified Futhark.CLI.Doc as Doc-import qualified Futhark.CLI.REPL as REPL-import qualified Futhark.CLI.Run as Run import qualified Futhark.CLI.Misc as Misc-import qualified Futhark.CLI.Autotune as Autotune+import qualified Futhark.CLI.OpenCL as OpenCL+import qualified Futhark.CLI.Pkg as Pkg+import qualified Futhark.CLI.PyOpenCL as PyOpenCL+import qualified Futhark.CLI.Python as Python import qualified Futhark.CLI.Query as Query+import qualified Futhark.CLI.REPL as REPL+import qualified Futhark.CLI.Run as Run+import qualified Futhark.CLI.Test as Test+import Futhark.Error+import Futhark.Util (maxinum)+import Futhark.Util.Options+import GHC.IO.Encoding (setLocaleEncoding)+import System.Environment+import System.Exit+import System.IO+import Prelude  type Command = String -> [String] -> IO ()  commands :: [(String, (Command, String))]-commands = sortOn fst-           [ ("dev", (Dev.main, "Run compiler passes directly."))--           , ("repl", (REPL.main, "Run interactive Read-Eval-Print-Loop."))-           , ("run", (Run.main, "Run a program through the (slow!) interpreter."))--           , ("c", (C.main, "Compile to sequential C."))-           , ("opencl", (OpenCL.main, "Compile to C calling OpenCL."))-           , ("cuda", (CCUDA.main, "Compile to C calling CUDA."))--           , ("python", (Python.main, "Compile to sequential Python."))-           , ("pyopencl", (PyOpenCL.main, "Compile to Python calling PyOpenCL."))--           , ("test", (Test.main, "Test Futhark programs."))-           , ("bench", (Bench.main, "Benchmark Futhark programs."))--           , ("dataset", (Dataset.main, "Generate random test data."))-           , ("datacmp", (Datacmp.main, "Compare Futhark data files for equality."))-           , ("dataget", (Misc.mainDataget, "Extract test data."))--           , ("doc", (Doc.main, "Generate documentation for Futhark code."))-           , ("pkg", (Pkg.main, "Manage local packages."))--           , ("check", (Check.main, "Type check a program."))-           , ("imports", (Misc.mainImports, "Print all non-builtin imported Futhark files."))-           , ("autotune", (Autotune.main, "Autotune threshold parameters."))-           , ("query", (Query.main, "Query semantic information about program."))-           ]+commands =+  sortOn+    fst+    [ ("dev", (Dev.main, "Run compiler passes directly.")),+      ("repl", (REPL.main, "Run interactive Read-Eval-Print-Loop.")),+      ("run", (Run.main, "Run a program through the (slow!) interpreter.")),+      ("c", (C.main, "Compile to sequential C.")),+      ("opencl", (OpenCL.main, "Compile to C calling OpenCL.")),+      ("cuda", (CCUDA.main, "Compile to C calling CUDA.")),+      ("python", (Python.main, "Compile to sequential Python.")),+      ("pyopencl", (PyOpenCL.main, "Compile to Python calling PyOpenCL.")),+      ("test", (Test.main, "Test Futhark programs.")),+      ("bench", (Bench.main, "Benchmark Futhark programs.")),+      ("dataset", (Dataset.main, "Generate random test data.")),+      ("datacmp", (Datacmp.main, "Compare Futhark data files for equality.")),+      ("dataget", (Misc.mainDataget, "Extract test data.")),+      ("doc", (Doc.main, "Generate documentation for Futhark code.")),+      ("pkg", (Pkg.main, "Manage local packages.")),+      ("check", (Check.main, "Type check a program.")),+      ("imports", (Misc.mainImports, "Print all non-builtin imported Futhark files.")),+      ("autotune", (Autotune.main, "Autotune threshold parameters.")),+      ("query", (Query.main, "Query semantic information about program."))+    ]  msg :: String-msg = unlines $-      ["<command> options...", "Commands:", ""] ++-      [ "   " <> cmd <> replicate (k - length cmd) ' ' <> desc-      | (cmd, (_, desc)) <- commands ]-  where k = maxinum (map (length . fst) commands) + 3+msg =+  unlines $+    ["<command> options...", "Commands:", ""]+      ++ [ "   " <> cmd <> replicate (k - length cmd) ' ' <> desc+           | (cmd, (_, desc)) <- commands+         ]+  where+    k = maxinum (map (length . fst) commands) + 3  -- | Catch all IO exceptions and print a better error message if they -- happen. reportingIOErrors :: IO () -> IO () reportingIOErrors = flip catches [Handler onExit, Handler onICE, Handler onError]-  where onExit :: ExitCode -> IO ()-        onExit = throwIO+  where+    onExit :: ExitCode -> IO ()+    onExit = throwIO -        onICE :: InternalError -> IO ()-        onICE (Error CompilerLimitation s) = do-          T.hPutStrLn stderr "Known compiler limitation encountered.  Sorry."-          T.hPutStrLn stderr "Revise your program or try a different Futhark compiler."-          T.hPutStrLn stderr s-        onICE (Error CompilerBug s) = do-          T.hPutStrLn stderr "Internal compiler error."-          T.hPutStrLn stderr "Please report this at https://github.com/diku-dk/futhark/issues."-          T.hPutStrLn stderr s+    onICE :: InternalError -> IO ()+    onICE (Error CompilerLimitation s) = do+      T.hPutStrLn stderr "Known compiler limitation encountered.  Sorry."+      T.hPutStrLn stderr "Revise your program or try a different Futhark compiler."+      T.hPutStrLn stderr s+    onICE (Error CompilerBug s) = do+      T.hPutStrLn stderr "Internal compiler error."+      T.hPutStrLn stderr "Please report this at https://github.com/diku-dk/futhark/issues."+      T.hPutStrLn stderr s -        onError :: SomeException -> IO ()-        onError e-          | Just UserInterrupt <- asyncExceptionFromException e =-              return () -- This corresponds to CTRL-C, which is not an error.-          | otherwise = do-              T.hPutStrLn stderr "Internal compiler error (unhandled IO exception)."-              T.hPutStrLn stderr "Please report this at https://github.com/diku-dk/futhark/issues"-              T.hPutStrLn stderr $ T.pack $ show e-              exitWith $ ExitFailure 1+    onError :: SomeException -> IO ()+    onError e+      | Just UserInterrupt <- asyncExceptionFromException e =+        return () -- This corresponds to CTRL-C, which is not an error.+      | otherwise = do+        T.hPutStrLn stderr "Internal compiler error (unhandled IO exception)."+        T.hPutStrLn stderr "Please report this at https://github.com/diku-dk/futhark/issues"+        T.hPutStrLn stderr $ T.pack $ show e+        exitWith $ ExitFailure 1  main :: IO () main = reportingIOErrors $ do@@ -112,6 +109,6 @@   args <- getArgs   prog <- getProgName   case args of-    cmd:args'+    cmd : args'       | Just (m, _) <- lookup cmd commands -> m (unwords [prog, cmd]) args'     _ -> mainWithOptions () [] msg (const . const Nothing) prog args
unittests/Futhark/BenchTests.hs view
@@ -1,13 +1,12 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}+ module Futhark.BenchTests (tests) where  import qualified Data.Text as T-+import Futhark.Bench import Test.Tasty import Test.Tasty.QuickCheck -import Futhark.Bench- instance Arbitrary RunResult where   arbitrary = RunResult . getPositive <$> arbitrary @@ -15,11 +14,15 @@ printable = getPrintableString <$> arbitrary  instance Arbitrary DataResult where-  arbitrary = DataResult-              <$> printable-              <*> oneof [Left <$> arbText,-                         Right <$> ((,) <$> arbitrary <*> arbText)]-    where arbText = T.pack <$> printable+  arbitrary =+    DataResult+      <$> printable+      <*> oneof+        [ Left <$> arbText,+          Right <$> ((,) <$> arbitrary <*> arbText)+        ]+    where+      arbText = T.pack <$> printable  -- XXX: we restrict this generator to single datasets to we don't have -- to worry about duplicates.@@ -28,8 +31,9 @@  encodeDecodeJSON :: TestTree encodeDecodeJSON = testProperty "encoding and decoding are inverse" prop-  where prop :: BenchResult -> Bool-        prop brs = decodeBenchResults (encodeBenchResults [brs]) == Right [brs]+  where+    prop :: BenchResult -> Bool+    prop brs = decodeBenchResults (encodeBenchResults [brs]) == Right [brs]  tests :: TestTree tests = testGroup "Futhark.BenchTests" [encodeDecodeJSON]
unittests/Futhark/IR/Mem/IxFun/Alg.hs view
@@ -1,40 +1,48 @@ -- | A simple index operation representation.  Every operation corresponds to a -- constructor. module Futhark.IR.Mem.IxFun.Alg-  ( IxFun(..)-  , iota-  , offsetIndex-  , permute-  , rotate-  , reshape-  , slice-  , rebase-  , shape-  , index+  ( IxFun (..),+    iota,+    offsetIndex,+    permute,+    rotate,+    reshape,+    slice,+    rebase,+    shape,+    index,   ) where -import Prelude hiding (mod)--import Futhark.IR.Syntax-  (ShapeChange, DimChange(..), Slice, sliceDims, DimIndex(..), unitSlice)-import Futhark.IR.Prop import Futhark.IR.Pretty ()+import Futhark.IR.Prop+import Futhark.IR.Syntax+  ( DimChange (..),+    DimIndex (..),+    ShapeChange,+    Slice,+    sliceDims,+    unitSlice,+  ) import Futhark.Util.IntegralExp import Futhark.Util.Pretty+import Prelude hiding (mod)  type Shape num = [num]+ type Indices num = [num]+ type Permutation = [Int] -data IxFun num = Direct (Shape num)-               | Permute (IxFun num) Permutation-               | Rotate (IxFun num) (Indices num)-               | Index (IxFun num) (Slice num)-               | Reshape (IxFun num) (ShapeChange num)-               | OffsetIndex (IxFun num) num-               | Rebase (IxFun num) (IxFun num)-               deriving (Eq, Show)+data IxFun num+  = Direct (Shape num)+  | Permute (IxFun num) Permutation+  | Rotate (IxFun num) (Indices num)+  | Index (IxFun num) (Slice num)+  | Reshape (IxFun num) (ShapeChange num)+  | OffsetIndex (IxFun num) num+  | Rebase (IxFun num) (IxFun num)+  deriving (Eq, Show)  instance Pretty num => Pretty (IxFun num) where   ppr (Direct dims) =@@ -43,14 +51,13 @@   ppr (Rotate fun offsets) = ppr fun <> brackets (commasep $ map ((text "+" <>) . ppr) offsets)   ppr (Index fun is) = ppr fun <> brackets (commasep $ map ppr is)   ppr (Reshape fun oldshape) =-    ppr fun <> text "->reshape" <>-    parens (commasep (map ppr oldshape))+    ppr fun <> text "->reshape"+      <> parens (commasep (map ppr oldshape))   ppr (OffsetIndex fun i) =     ppr fun <> text "->offset_index" <> parens (ppr i)   ppr (Rebase new_base fun) =     text "rebase(" <> ppr new_base <> text ", " <> ppr fun <> text ")" - iota :: Shape num -> IxFun num iota = Direct @@ -72,8 +79,10 @@ reshape :: IxFun num -> ShapeChange num -> IxFun num reshape = Reshape -shape :: IntegralExp num =>-         IxFun num -> Shape num+shape ::+  IntegralExp num =>+  IxFun num ->+  Shape num shape (Direct dims) =   dims shape (Permute ixfun perm) =@@ -89,46 +98,53 @@ shape (Rebase _ ixfun) =   shape ixfun -index :: (IntegralExp num, Eq num) =>-         IxFun num -> Indices num -> num+index ::+  (IntegralExp num, Eq num) =>+  IxFun num ->+  Indices num ->+  num index (Direct dims) is =   sum $ zipWith (*) is slicesizes-  where slicesizes = drop 1 $ sliceSizes dims+  where+    slicesizes = drop 1 $ sliceSizes dims index (Permute fun perm) is_new =   index fun is_old-  where is_old = rearrangeShape (rearrangeInverse perm) is_new+  where+    is_old = rearrangeShape (rearrangeInverse perm) is_new index (Rotate fun offsets) is =   index fun $ zipWith mod (zipWith (+) is offsets) dims-  where dims = shape fun+  where+    dims = shape fun index (Index fun js) is =   index fun (adjust js is)-  where adjust (DimFix j:js') is' = j : adjust js' is'-        adjust (DimSlice j _ s:js') (i:is') = j + i * s : adjust js' is'-        adjust _ _ = []+  where+    adjust (DimFix j : js') is' = j : adjust js' is'+    adjust (DimSlice j _ s : js') (i : is') = j + i * s : adjust js' is'+    adjust _ _ = [] index (Reshape fun newshape) is =   let new_indices = reshapeIndex (shape fun) (newDims newshape) is-  in index fun new_indices+   in index fun new_indices index (OffsetIndex fun i) is =   case shape fun of     d : ds ->-      index (Index fun (DimSlice i (d-i) 1 : map (unitSlice 0) ds)) is+      index (Index fun (DimSlice i (d - i) 1 : map (unitSlice 0) ds)) is     [] -> error "index: OffsetIndex: underlying index function has rank zero" index (Rebase new_base fun) is =   let fun' = case fun of-               Direct old_shape ->-                 if old_shape == shape new_base-                 then new_base-                 else reshape new_base $ map DimCoercion old_shape-               Permute ixfun perm ->-                 permute (rebase new_base ixfun) perm-               Rotate ixfun offsets ->-                 rotate (rebase new_base ixfun) offsets-               Index ixfun iis ->-                 slice (rebase new_base ixfun) iis-               Reshape ixfun new_shape ->-                 reshape (rebase new_base ixfun) new_shape-               OffsetIndex ixfun s ->-                 offsetIndex (rebase new_base ixfun) s-               r@Rebase{} ->-                 r-  in index fun' is+        Direct old_shape ->+          if old_shape == shape new_base+            then new_base+            else reshape new_base $ map DimCoercion old_shape+        Permute ixfun perm ->+          permute (rebase new_base ixfun) perm+        Rotate ixfun offsets ->+          rotate (rebase new_base ixfun) offsets+        Index ixfun iis ->+          slice (rebase new_base ixfun) iis+        Reshape ixfun new_shape ->+          reshape (rebase new_base ixfun) new_shape+        OffsetIndex ixfun s ->+          offsetIndex (rebase new_base ixfun) s+        r@Rebase {} ->+          r+   in index fun' is
unittests/Futhark/IR/Mem/IxFunTests.hs view
@@ -1,49 +1,46 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}+ module Futhark.IR.Mem.IxFunTests-  ( tests+  ( tests,   ) where -import Prelude hiding (span)-import qualified Prelude as P import qualified Data.List as DL--import Test.Tasty-import Test.Tasty.HUnit--import Futhark.IR.Syntax-import Futhark.IR.Syntax.Core()-import qualified Futhark.Util.Pretty as PR-import qualified Futhark.Util.IntegralExp as IE import qualified Futhark.IR.Mem.IxFun as IxFunLMAD import qualified Futhark.IR.Mem.IxFun.Alg as IxFunAlg--import qualified Futhark.IR.Mem.IxFunWrapper as IxFunWrap import Futhark.IR.Mem.IxFunWrapper-+import qualified Futhark.IR.Mem.IxFunWrapper as IxFunWrap+import Futhark.IR.Syntax+import Futhark.IR.Syntax.Core ()+import qualified Futhark.Util.IntegralExp as IE+import qualified Futhark.Util.Pretty as PR+import Test.Tasty+import Test.Tasty.HUnit+import Prelude hiding (span)+import qualified Prelude as P  instance IE.IntegralExp Int where   quot = P.quot-  rem  = P.rem-  div  = P.div-  mod  = P.mod-  sgn  = Just . P.signum--  fromInt8  = fromInteger . toInteger-  fromInt16 = fromInteger . toInteger-  fromInt32 = fromInteger . toInteger-  fromInt64 = fromInteger . toInteger+  rem = P.rem+  div = P.div+  mod = P.mod+  sgn = Just . P.signum  allPoints :: [Int] -> [[Int]] allPoints dims =-    let total = product dims-        strides = drop 1 $ DL.reverse $ scanl (*) 1 $ DL.reverse dims-    in map (unflatInd strides) [0..total-1]-    where unflatInd :: [Int] -> Int -> [Int]-          unflatInd strides x = fst $-            foldl (\(res, acc) span ->-                     (res ++ [acc `P.div` span], acc `P.mod` span))-            ([], x) strides+  let total = product dims+      strides = drop 1 $ DL.reverse $ scanl (*) 1 $ DL.reverse dims+   in map (unflatInd strides) [0 .. total -1]+  where+    unflatInd :: [Int] -> Int -> [Int]+    unflatInd strides x =+      fst $+        foldl+          ( \(res, acc) span ->+              (res ++ [acc `P.div` span], acc `P.mod` span)+          )+          ([], x)+          strides  compareIxFuns :: IxFunLMAD.IxFun Int -> IxFunAlg.IxFun Int -> Assertion compareIxFuns ixfunLMAD ixfunAlg =@@ -52,15 +49,29 @@       points = allPoints lmadShape       resLMAD = map (IxFunLMAD.index ixfunLMAD) points       resAlg = map (IxFunAlg.index ixfunAlg) points-      errorMessage = "lmad ixfun:  " ++ PR.pretty ixfunLMAD ++ "\n" ++-                     "alg ixfun:   " ++ PR.pretty ixfunAlg ++ "\n" ++-                     "lmad shape:  " ++ show lmadShape ++ "\n" ++-                     "alg shape:   " ++ show algShape ++ "\n" ++-                     "lmad points length: " ++ show (length resLMAD) ++ "\n" ++-                     "alg points length:  " ++ show (length resAlg) ++ "\n" ++-                     "lmad points: " ++ show resLMAD ++ "\n" ++-                     "alg points:  " ++ show resAlg-  in (lmadShape == algShape && resLMAD == resAlg) @? errorMessage+      errorMessage =+        "lmad ixfun:  " ++ PR.pretty ixfunLMAD ++ "\n"+          ++ "alg ixfun:   "+          ++ PR.pretty ixfunAlg+          ++ "\n"+          ++ "lmad shape:  "+          ++ show lmadShape+          ++ "\n"+          ++ "alg shape:   "+          ++ show algShape+          ++ "\n"+          ++ "lmad points length: "+          ++ show (length resLMAD)+          ++ "\n"+          ++ "alg points length:  "+          ++ show (length resAlg)+          ++ "\n"+          ++ "lmad points: "+          ++ show resLMAD+          ++ "\n"+          ++ "alg points:  "+          ++ show resAlg+   in (lmadShape == algShape && resLMAD == resAlg) @? errorMessage  compareOps :: IxFunWrap.IxFun Int -> Assertion compareOps (ixfunLMAD, ixfunAlg) = compareIxFuns ixfunLMAD ixfunAlg@@ -68,254 +79,317 @@ -- XXX: Clean this up. n :: Int n = 19-slice3 :: [DimIndex Int]-slice3 = [ DimSlice 2 (n `P.div` 3) 3-         , DimFix (n `P.div` 2)-         , DimSlice 1 (n `P.div` 2) 2-         ] +slice3 :: [DimIndex Int]+slice3 =+  [ DimSlice 2 (n `P.div` 3) 3,+    DimFix (n `P.div` 2),+    DimSlice 1 (n `P.div` 2) 2+  ]  -- Actual tests. tests :: TestTree-tests = testGroup "IxFunTests"-        $ concat-        [ test_iota-        , test_slice_iota-        , test_reshape_slice_iota1-        , test_permute_slice_iota-        , test_rotate_rotate_permute_slice_iota-        , test_slice_rotate_permute_slice_iota1-        , test_slice_rotate_permute_slice_iota2-        , test_slice_rotate_permute_slice_iota3-        , test_permute_rotate_slice_permute_slice_iota-        , test_reshape_rotate_iota-        , test_reshape_permute_iota-        , test_reshape_slice_iota2-        , test_reshape_slice_iota3-        , test_complex1-        , test_complex2-        , test_rebase1-        , test_rebase2-        , test_rebase3-        , test_rebase4_5-        ]+tests =+  testGroup "IxFunTests" $+    concat+      [ test_iota,+        test_slice_iota,+        test_reshape_slice_iota1,+        test_permute_slice_iota,+        test_rotate_rotate_permute_slice_iota,+        test_slice_rotate_permute_slice_iota1,+        test_slice_rotate_permute_slice_iota2,+        test_slice_rotate_permute_slice_iota3,+        test_permute_rotate_slice_permute_slice_iota,+        test_reshape_rotate_iota,+        test_reshape_permute_iota,+        test_reshape_slice_iota2,+        test_reshape_slice_iota3,+        test_complex1,+        test_complex2,+        test_rebase1,+        test_rebase2,+        test_rebase3,+        test_rebase4_5+      ]  singleton :: TestTree -> [TestTree] singleton = (: [])  test_iota :: [TestTree]-test_iota = singleton $ testCase "iota" $ compareOps $-  iota [n]+test_iota =+  singleton $+    testCase "iota" $+      compareOps $+        iota [n]  test_slice_iota :: [TestTree]-test_slice_iota = singleton $ testCase "slice . iota" $ compareOps $-  slice (iota [n, n, n]) slice3+test_slice_iota =+  singleton $+    testCase "slice . iota" $+      compareOps $+        slice (iota [n, n, n]) slice3  test_reshape_slice_iota1 :: [TestTree]-test_reshape_slice_iota1 = singleton $ testCase "reshape . slice . iota 1" $ compareOps $-  reshape (slice (iota [n, n, n]) slice3)-  [DimNew (n `P.div` 2), DimNew (n `P.div` 3)]+test_reshape_slice_iota1 =+  singleton $+    testCase "reshape . slice . iota 1" $+      compareOps $+        reshape+          (slice (iota [n, n, n]) slice3)+          [DimNew (n `P.div` 2), DimNew (n `P.div` 3)]  test_permute_slice_iota :: [TestTree]-test_permute_slice_iota = singleton $ testCase "permute . slice . iota" $ compareOps $-  permute (slice (iota [n, n, n]) slice3) [1, 0]+test_permute_slice_iota =+  singleton $+    testCase "permute . slice . iota" $+      compareOps $+        permute (slice (iota [n, n, n]) slice3) [1, 0]  test_rotate_rotate_permute_slice_iota :: [TestTree] test_rotate_rotate_permute_slice_iota =-  singleton $ testCase "rotate . rotate . permute . slice . iota" $ compareOps $-  let ixfun = permute (slice (iota [n, n, n]) slice3) [1, 0]-  in rotate (rotate ixfun [2, 1]) [1, 2]+  singleton $+    testCase "rotate . rotate . permute . slice . iota" $+      compareOps $+        let ixfun = permute (slice (iota [n, n, n]) slice3) [1, 0]+         in rotate (rotate ixfun [2, 1]) [1, 2]  test_slice_rotate_permute_slice_iota1 :: [TestTree] test_slice_rotate_permute_slice_iota1 =-  singleton $ testCase "slice . rotate . permute . slice . iota 1" $ compareOps $-  let slice2 = [ DimSlice 0 n 1-               , DimSlice 1 (n `P.div` 2) 2-               , DimSlice 0 n 1-               ]-      slice13 = [ DimSlice 2 (n `P.div` 3) 3-                , DimSlice 0 (n `P.div` 2) 1-                , DimSlice 1 (n `P.div` 2) 2-                ]-      ixfun = permute (slice (iota [n, n, n]) slice2) [2, 1, 0]-      ixfun' = slice (rotate ixfun [3, 1, 2]) slice13-  in ixfun'+  singleton $+    testCase "slice . rotate . permute . slice . iota 1" $+      compareOps $+        let slice2 =+              [ DimSlice 0 n 1,+                DimSlice 1 (n `P.div` 2) 2,+                DimSlice 0 n 1+              ]+            slice13 =+              [ DimSlice 2 (n `P.div` 3) 3,+                DimSlice 0 (n `P.div` 2) 1,+                DimSlice 1 (n `P.div` 2) 2+              ]+            ixfun = permute (slice (iota [n, n, n]) slice2) [2, 1, 0]+            ixfun' = slice (rotate ixfun [3, 1, 2]) slice13+         in ixfun'  test_slice_rotate_permute_slice_iota2 :: [TestTree] test_slice_rotate_permute_slice_iota2 =-  singleton $ testCase "slice . rotate . permute . slice . iota 2" $ compareOps $-  let slice2 = [ DimSlice 0 (n `P.div` 2) 1-               , DimFix   (n `P.div` 2)-               , DimSlice 0 (n `P.div` 3) 1-               ]-      slice13 = [ DimSlice 2 (n `P.div` 3) 3-                , DimSlice 0 n 1-                , DimSlice 1 (n `P.div` 2) 2-                ]-      ixfun = permute (slice (iota [n, n, n]) slice13) [2, 1, 0]-      ixfun' = slice (rotate ixfun [3, 1, 2]) slice2-  in ixfun'+  singleton $+    testCase "slice . rotate . permute . slice . iota 2" $+      compareOps $+        let slice2 =+              [ DimSlice 0 (n `P.div` 2) 1,+                DimFix (n `P.div` 2),+                DimSlice 0 (n `P.div` 3) 1+              ]+            slice13 =+              [ DimSlice 2 (n `P.div` 3) 3,+                DimSlice 0 n 1,+                DimSlice 1 (n `P.div` 2) 2+              ]+            ixfun = permute (slice (iota [n, n, n]) slice13) [2, 1, 0]+            ixfun' = slice (rotate ixfun [3, 1, 2]) slice2+         in ixfun'  test_slice_rotate_permute_slice_iota3 :: [TestTree] test_slice_rotate_permute_slice_iota3 =-  singleton $ testCase "slice . rotate . permute . slice . iota 3" $ compareOps $-  -- full-slice of (-1) stride-  let ixfun = permute (slice (iota [n, n, n]) slice3) [1, 0]-      ixfun' = rotate ixfun [2, 1]+  singleton $+    testCase "slice . rotate . permute . slice . iota 3" $+      compareOps $+        -- full-slice of (-1) stride+        let ixfun = permute (slice (iota [n, n, n]) slice3) [1, 0]+            ixfun' = rotate ixfun [2, 1] -      (n1, m1) = case IxFunLMAD.shape (fst ixfun') of-                   [a, b] -> (a, b)-                   _ ->  error "expecting 2 dimensions at this point!"-      negslice = [DimSlice 0 n1 1, DimSlice (m1 - 1) m1 (-1)]-      ixfun'' = rotate (slice ixfun' negslice) [1,2]-  in ixfun''+            (n1, m1) = case IxFunLMAD.shape (fst ixfun') of+              [a, b] -> (a, b)+              _ -> error "expecting 2 dimensions at this point!"+            negslice = [DimSlice 0 n1 1, DimSlice (m1 - 1) m1 (-1)]+            ixfun'' = rotate (slice ixfun' negslice) [1, 2]+         in ixfun''  test_permute_rotate_slice_permute_slice_iota :: [TestTree] test_permute_rotate_slice_permute_slice_iota =-  singleton $ testCase "permute . rotate . slice . permute . slice . iota" $ compareOps $-  -- contiguousness-  let slice33 = [ DimFix (n `P.div` 2)-                , DimSlice (n - 1) (n `P.div` 3) (-1)-                , DimSlice 0 n 1-                ]-      ixfun = permute (slice (iota [n, n, n]) slice33) [1, 0]-      m = n `P.div` 3-      slice1 = [DimSlice (n - 1) n (-1), DimSlice 2 (m - 2) 1]-      ixfun' = permute (rotate (slice ixfun slice1) [1, 2]) [1, 0]-  in ixfun'+  singleton $+    testCase "permute . rotate . slice . permute . slice . iota" $+      compareOps $+        -- contiguousness+        let slice33 =+              [ DimFix (n `P.div` 2),+                DimSlice (n - 1) (n `P.div` 3) (-1),+                DimSlice 0 n 1+              ]+            ixfun = permute (slice (iota [n, n, n]) slice33) [1, 0]+            m = n `P.div` 3+            slice1 = [DimSlice (n - 1) n (-1), DimSlice 2 (m - 2) 1]+            ixfun' = permute (rotate (slice ixfun slice1) [1, 2]) [1, 0]+         in ixfun'  test_reshape_rotate_iota :: [TestTree] test_reshape_rotate_iota =   -- negative reshape test-  singleton $ testCase "reshape . rotate . iota" $ compareOps $-  let newdims = [DimNew (n * n), DimCoercion n]-  in reshape (rotate (iota [n, n, n]) [1, 0, 0]) newdims+  singleton $+    testCase "reshape . rotate . iota" $+      compareOps $+        let newdims = [DimNew (n * n), DimCoercion n]+         in reshape (rotate (iota [n, n, n]) [1, 0, 0]) newdims  test_reshape_permute_iota :: [TestTree] test_reshape_permute_iota =   -- negative reshape test-  singleton $ testCase "reshape . permute . iota" $ compareOps $-  let newdims = [DimNew (n * n), DimCoercion n]-  in reshape (permute (iota [n, n, n]) [1, 2, 0]) newdims+  singleton $+    testCase "reshape . permute . iota" $+      compareOps $+        let newdims = [DimNew (n * n), DimCoercion n]+         in reshape (permute (iota [n, n, n]) [1, 2, 0]) newdims  test_reshape_slice_iota2 :: [TestTree] test_reshape_slice_iota2 =   -- negative reshape test-  singleton $ testCase "reshape . slice . iota 2" $ compareOps $-  let newdims = [DimNew (n*n), DimCoercion n]-      slc = [ DimFix (n `P.div` 2)-            , DimSlice (n-1) n (-1)-            , DimSlice 0 n 1-            , DimSlice (n-1) n (-1)-            ]-  in reshape (slice (iota [n, n, n, n]) slc) newdims+  singleton $+    testCase "reshape . slice . iota 2" $+      compareOps $+        let newdims = [DimNew (n * n), DimCoercion n]+            slc =+              [ DimFix (n `P.div` 2),+                DimSlice (n -1) n (-1),+                DimSlice 0 n 1,+                DimSlice (n -1) n (-1)+              ]+         in reshape (slice (iota [n, n, n, n]) slc) newdims  test_reshape_slice_iota3 :: [TestTree] test_reshape_slice_iota3 =   -- negative reshape test-  singleton $ testCase "reshape . slice . iota 3" $ compareOps $-  let newdims = [DimNew (n*n), DimCoercion n]-      slc = [ DimFix (n `P.div` 2)-            , DimSlice 0 n 1-            , DimSlice 0 (n `P.div` 2) 1-            , DimSlice 0 n 1-            ]-  in reshape (slice (iota [n, n, n, n]) slc) newdims+  singleton $+    testCase "reshape . slice . iota 3" $+      compareOps $+        let newdims = [DimNew (n * n), DimCoercion n]+            slc =+              [ DimFix (n `P.div` 2),+                DimSlice 0 n 1,+                DimSlice 0 (n `P.div` 2) 1,+                DimSlice 0 n 1+              ]+         in reshape (slice (iota [n, n, n, n]) slc) newdims  test_complex1 :: [TestTree] test_complex1 =-  singleton $ testCase "reshape . permute . rotate . slice . permute . slice . iota 1" $ compareOps $-  let newdims = [ DimCoercion n-                , DimCoercion n-                , DimNew n-                , DimCoercion ((n `P.div` 3) - 2)-                ]-      slice33 = [ DimSlice (n-1) (n `P.div` 3) (-1)-                , DimSlice (n-1) n (-1)-                , DimSlice (n-1) n (-1)-                , DimSlice 0 n 1-                ]-      ixfun = permute (slice (iota [n, n, n, n, n]) slice33) [3, 1, 2, 0]-      m = n `P.div` 3-      slice1 = [DimSlice 0 n 1, DimSlice (n-1) n (-1), DimSlice (n-1) n (-1), DimSlice 1 (m-2) (-1)]-      ixfun' = reshape (rotate (slice ixfun slice1) [1, 2, 3, 4]) newdims-  in ixfun'+  singleton $+    testCase "reshape . permute . rotate . slice . permute . slice . iota 1" $+      compareOps $+        let newdims =+              [ DimCoercion n,+                DimCoercion n,+                DimNew n,+                DimCoercion ((n `P.div` 3) - 2)+              ]+            slice33 =+              [ DimSlice (n -1) (n `P.div` 3) (-1),+                DimSlice (n -1) n (-1),+                DimSlice (n -1) n (-1),+                DimSlice 0 n 1+              ]+            ixfun = permute (slice (iota [n, n, n, n, n]) slice33) [3, 1, 2, 0]+            m = n `P.div` 3+            slice1 = [DimSlice 0 n 1, DimSlice (n -1) n (-1), DimSlice (n -1) n (-1), DimSlice 1 (m -2) (-1)]+            ixfun' = reshape (rotate (slice ixfun slice1) [1, 2, 3, 4]) newdims+         in ixfun'  test_complex2 :: [TestTree] test_complex2 =-  singleton $ testCase "reshape . permute . rotate . slice . permute . slice . iota 2" $ compareOps $-  let newdims = [ DimCoercion n-                , DimNew (n*n)-                , DimCoercion ((n `P.div` 3) - 2)]-      slc2 = [ DimFix (n `P.div` 2)-             , DimSlice (n-1) (n `P.div` 3) (-1)-             , DimSlice (n-1) n (-1)-             , DimSlice (n-1) n (-1)-             , DimSlice 0 n 1-             ]-      ixfun = permute (slice (iota [n, n, n, n, n]) slc2) [3, 1, 2, 0]-      m = n `P.div` 3-      slice1 = [DimSlice 0 n 1, DimSlice (n-1) n (-1), DimSlice (n-1) n (-1), DimSlice 1 (m-2) (-1)]-      ixfun' = reshape (rotate (slice ixfun slice1) [1, 0, 0, 2]) newdims-  in ixfun'+  singleton $+    testCase "reshape . permute . rotate . slice . permute . slice . iota 2" $+      compareOps $+        let newdims =+              [ DimCoercion n,+                DimNew (n * n),+                DimCoercion ((n `P.div` 3) - 2)+              ]+            slc2 =+              [ DimFix (n `P.div` 2),+                DimSlice (n -1) (n `P.div` 3) (-1),+                DimSlice (n -1) n (-1),+                DimSlice (n -1) n (-1),+                DimSlice 0 n 1+              ]+            ixfun = permute (slice (iota [n, n, n, n, n]) slc2) [3, 1, 2, 0]+            m = n `P.div` 3+            slice1 = [DimSlice 0 n 1, DimSlice (n -1) n (-1), DimSlice (n -1) n (-1), DimSlice 1 (m -2) (-1)]+            ixfun' = reshape (rotate (slice ixfun slice1) [1, 0, 0, 2]) newdims+         in ixfun'  test_rebase1 :: [TestTree] test_rebase1 =-  singleton $ testCase "rebase 1" $ compareOps $-    let slice_base = [ DimFix (n `P.div` 2)-                     , DimSlice 2 (n-2) 1-                     , DimSlice 3 (n-3) 1-                     ]-        ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]-        ixfn_orig = rotate (permute (iota [n-3, n-2]) [1, 0]) [1, 2]-        ixfn_rebase = rebase ixfn_base ixfn_orig-    in ixfn_rebase+  singleton $+    testCase "rebase 1" $+      compareOps $+        let slice_base =+              [ DimFix (n `P.div` 2),+                DimSlice 2 (n -2) 1,+                DimSlice 3 (n -3) 1+              ]+            ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]+            ixfn_orig = rotate (permute (iota [n -3, n -2]) [1, 0]) [1, 2]+            ixfn_rebase = rebase ixfn_base ixfn_orig+         in ixfn_rebase  test_rebase2 :: [TestTree] test_rebase2 =-  singleton $ testCase "rebase 2" $ compareOps $-    let slice_base = [ DimFix (n `P.div` 2)-                     , DimSlice (n-1) (n-2) (-1)-                     , DimSlice (n-1) (n-3) (-1)-                     ]-        slice_orig = [ DimSlice (n-4) (n-3) (-1)-                     , DimSlice (n-3) (n-2) (-1)-                     ]-        ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]-        ixfn_orig = rotate (permute (slice (iota [n-3, n-2]) slice_orig) [1, 0]) [1, 2]-        ixfn_rebase = rebase ixfn_base ixfn_orig-    in ixfn_rebase+  singleton $+    testCase "rebase 2" $+      compareOps $+        let slice_base =+              [ DimFix (n `P.div` 2),+                DimSlice (n -1) (n -2) (-1),+                DimSlice (n -1) (n -3) (-1)+              ]+            slice_orig =+              [ DimSlice (n -4) (n -3) (-1),+                DimSlice (n -3) (n -2) (-1)+              ]+            ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]+            ixfn_orig = rotate (permute (slice (iota [n -3, n -2]) slice_orig) [1, 0]) [1, 2]+            ixfn_rebase = rebase ixfn_base ixfn_orig+         in ixfn_rebase  test_rebase3 :: [TestTree] test_rebase3 =-  singleton $ testCase "rebase full orig but not monotonic" $ compareOps $-  let n2 = (n-2) `P.div` 3-      n3 = (n-3) `P.div` 2-      slice_base = [ DimFix (n `P.div` 2)-                   , DimSlice (n-1) n2 (-3)-                   , DimSlice (n-1) n3 (-2)-                   ]-      slice_orig = [ DimSlice (n3-1) n3 (-1)-                   , DimSlice (n2-1) n2 (-1)-                   ]-      ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]-      ixfn_orig = rotate (permute (slice (iota [n3, n2]) slice_orig) [1, 0]) [1, 2]-      ixfn_rebase = rebase ixfn_base ixfn_orig-  in ixfn_rebase+  singleton $+    testCase "rebase full orig but not monotonic" $+      compareOps $+        let n2 = (n -2) `P.div` 3+            n3 = (n -3) `P.div` 2+            slice_base =+              [ DimFix (n `P.div` 2),+                DimSlice (n -1) n2 (-3),+                DimSlice (n -1) n3 (-2)+              ]+            slice_orig =+              [ DimSlice (n3 -1) n3 (-1),+                DimSlice (n2 -1) n2 (-1)+              ]+            ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]+            ixfn_orig = rotate (permute (slice (iota [n3, n2]) slice_orig) [1, 0]) [1, 2]+            ixfn_rebase = rebase ixfn_base ixfn_orig+         in ixfn_rebase  test_rebase4_5 :: [TestTree] test_rebase4_5 =-  let n2 = (n-2) `P.div` 3-      n3 = (n-3) `P.div` 2-      slice_base = [ DimFix (n `P.div` 2)-                   , DimSlice (n-1) n2 (-3)-                   , DimSlice 3 n3 2-                   ]-      slice_orig = [ DimSlice (n3-1) n3 (-1)-                   , DimSlice 0 n2 1-                   ]+  let n2 = (n -2) `P.div` 3+      n3 = (n -3) `P.div` 2+      slice_base =+        [ DimFix (n `P.div` 2),+          DimSlice (n -1) n2 (-3),+          DimSlice 3 n3 2+        ]+      slice_orig =+        [ DimSlice (n3 -1) n3 (-1),+          DimSlice 0 n2 1+        ]       ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]       ixfn_orig = rotate (permute (slice (iota [n3, n2]) slice_orig) [1, 0]) [1, 2]-  in [ testCase "rebase mixed monotonicities" $ compareOps $-       rebase ixfn_base ixfn_orig-     ]+   in [ testCase "rebase mixed monotonicities" $+          compareOps $+            rebase ixfn_base ixfn_orig+      ]
unittests/Futhark/IR/Mem/IxFunWrapper.hs view
@@ -1,48 +1,66 @@ -- | Perform index function operations in both algebraic and LMAD -- representations. module Futhark.IR.Mem.IxFunWrapper-  ( IxFun-  , iota-  , permute-  , rotate-  , reshape-  , slice-  , rebase+  ( IxFun,+    iota,+    permute,+    rotate,+    reshape,+    slice,+    rebase,   ) where -import Futhark.Util.IntegralExp-import Futhark.IR.Syntax (ShapeChange, Slice) import qualified Futhark.IR.Mem.IxFun as I import qualified Futhark.IR.Mem.IxFun.Alg as IA-+import Futhark.IR.Syntax (ShapeChange, Slice)+import Futhark.Util.IntegralExp  type Shape num = [num]+ type Indices num = [num]+ type Permutation = [Int]  type IxFun num = (I.IxFun num, IA.IxFun num) -iota :: IntegralExp num =>-        Shape num -> IxFun num+iota ::+  IntegralExp num =>+  Shape num ->+  IxFun num iota x = (I.iota x, IA.iota x) -permute :: IntegralExp num =>-           IxFun num -> Permutation -> IxFun num+permute ::+  IntegralExp num =>+  IxFun num ->+  Permutation ->+  IxFun num permute (l, a) x = (I.permute l x, IA.permute a x) -rotate :: (Eq num, IntegralExp num) =>-          IxFun num -> Indices num -> IxFun num+rotate ::+  (Eq num, IntegralExp num) =>+  IxFun num ->+  Indices num ->+  IxFun num rotate (l, a) x = (I.rotate l x, IA.rotate a x) -reshape :: (Eq num, IntegralExp num) =>-           IxFun num -> ShapeChange num -> IxFun num+reshape ::+  (Eq num, IntegralExp num) =>+  IxFun num ->+  ShapeChange num ->+  IxFun num reshape (l, a) x = (I.reshape l x, IA.reshape a x) -slice :: (Eq num, IntegralExp num) =>-         IxFun num -> Slice num -> IxFun num+slice ::+  (Eq num, IntegralExp num) =>+  IxFun num ->+  Slice num ->+  IxFun num slice (l, a) x = (I.slice l x, IA.slice a x) -rebase :: (Eq num, IntegralExp num) =>-          IxFun num -> IxFun num -> IxFun num+rebase ::+  (Eq num, IntegralExp num) =>+  IxFun num ->+  IxFun num ->+  IxFun num rebase (l, a) (l1, a1) = (I.rebase l l1, IA.rebase a a1)
unittests/Futhark/IR/PrimitiveTests.hs view
@@ -1,54 +1,77 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -fno-warn-orphans #-}+ module Futhark.IR.PrimitiveTests-       ( tests-       , arbitraryPrimValOfType-       )-       where+  ( tests,+    arbitraryPrimValOfType,+  )+where  import Control.Applicative-+import Futhark.IR.Primitive+import Language.SexpGrammar import Test.QuickCheck import Test.Tasty import Test.Tasty.HUnit- import Prelude -import Futhark.IR.Primitive- tests :: TestTree-tests = testGroup "PrimitiveTests" propPrimValuesHaveRightType+tests = testGroup "PrimitiveTests" [propPrimValuesHaveRightType, doUnOpTests] -propPrimValuesHaveRightType :: [TestTree]-propPrimValuesHaveRightType = [ testCase (show t ++ " has blank of right type") $-                                primValueType (blankPrimValue t) @?= t-                              | t <- [minBound..maxBound]-                              ]+propPrimValuesHaveRightType :: TestTree+propPrimValuesHaveRightType =+  testGroup+    "propPrimValuesHaveRightTypes"+    [ testCase (show t ++ " has blank of right type") $+        primValueType (blankPrimValue t) @?= t+      | t <- [minBound .. maxBound]+    ] +doUnOpTests :: TestTree+doUnOpTests =+  testGroup+    "doUnOp"+    [ testCase "not" $+        let unop = decode @UnOp "(complement i32)"+            val = decode @PrimValue "42i32"+            res = doUnOp <$> unop <*> val+         in res @?= Right (Just (IntValue (Int32Value (-43))))+    ]+ instance Arbitrary IntType where-  arbitrary = elements [minBound..maxBound]+  arbitrary = elements [minBound .. maxBound]  instance Arbitrary FloatType where-  arbitrary = elements [minBound..maxBound]+  arbitrary = elements [minBound .. maxBound]  instance Arbitrary PrimType where-  arbitrary = elements [minBound..maxBound]+  arbitrary = elements [minBound .. maxBound]  instance Arbitrary IntValue where-  arbitrary = oneof [ Int8Value <$> arbitrary-                    , Int16Value <$> arbitrary-                    , Int32Value <$> arbitrary-                    , Int64Value <$> arbitrary ]+  arbitrary =+    oneof+      [ Int8Value <$> arbitrary,+        Int16Value <$> arbitrary,+        Int32Value <$> arbitrary,+        Int64Value <$> arbitrary+      ]  instance Arbitrary FloatValue where-  arbitrary = oneof [ Float32Value <$> arbitrary-                    , Float64Value <$> arbitrary ]+  arbitrary =+    oneof+      [ Float32Value <$> arbitrary,+        Float64Value <$> arbitrary+      ]  instance Arbitrary PrimValue where-  arbitrary = oneof [ IntValue <$> arbitrary-                    , FloatValue <$> arbitrary-                    , BoolValue <$> arbitrary-                    , pure Checked-                    ]+  arbitrary =+    oneof+      [ IntValue <$> arbitrary,+        FloatValue <$> arbitrary,+        BoolValue <$> arbitrary,+        pure Checked+      ]  arbitraryPrimValOfType :: PrimType -> Gen PrimValue arbitraryPrimValOfType (IntType Int8) = IntValue . Int8Value <$> arbitrary
unittests/Futhark/IR/Prop/RearrangeTests.hs view
@@ -1,55 +1,54 @@-module Futhark.IR.Prop.RearrangeTests-       ( tests )-       where+module Futhark.IR.Prop.RearrangeTests (tests) where  import Control.Applicative-+import Futhark.IR.Prop.Rearrange import Test.Tasty import Test.Tasty.HUnit import Test.Tasty.QuickCheck- import Prelude -import Futhark.IR.Prop.Rearrange- tests :: TestTree-tests = testGroup "RearrangeTests" $-        isMapTransposeTests ++-        [isMapTransposeProp]+tests =+  testGroup "RearrangeTests" $+    isMapTransposeTests+      ++ [isMapTransposeProp]  isMapTransposeTests :: [TestTree] isMapTransposeTests =   [ testCase (unwords ["isMapTranspose", show perm, "==", show dres]) $-    isMapTranspose perm @?= dres-  | (perm, dres) <- [ ([0,1,4,5,2,3], Just (2,2,2))-                    , ([1,0,4,5,2,3], Nothing)-                    , ([1,0], Just (0, 1, 1))-                    , ([0,2,1], Just (1, 1, 1))-                    , ([0,1,2], Nothing)-                    , ([1,0,2], Nothing)-                    ]+      isMapTranspose perm @?= dres+    | (perm, dres) <-+        [ ([0, 1, 4, 5, 2, 3], Just (2, 2, 2)),+          ([1, 0, 4, 5, 2, 3], Nothing),+          ([1, 0], Just (0, 1, 1)),+          ([0, 2, 1], Just (1, 1, 1)),+          ([0, 1, 2], Nothing),+          ([1, 0, 2], Nothing)+        ]   ]  newtype Permutation = Permutation [Int]-                    deriving (Eq, Ord, Show)+  deriving (Eq, Ord, Show)  instance Arbitrary Permutation where   arbitrary = do     Positive n <- arbitrary-    Permutation <$> shuffle [0..n-1]+    Permutation <$> shuffle [0 .. n -1]  isMapTransposeProp :: TestTree isMapTransposeProp = testProperty "isMapTranspose corresponds to a map of transpose" prop-  where prop :: Permutation -> Bool-        prop (Permutation perm) =-          case isMapTranspose perm of-            Nothing -> True-            Just (r1, r2, r3) ->-              and [r1 >= 0,-                   r2 > 0,-                   r3 > 0,-                   r1 + r2 + r3 == length perm,-                   let (mapped, notmapped) =splitAt r1 perm-                       (pretrans, posttrans) = splitAt r2 notmapped-                   in mapped ++ posttrans ++ pretrans == [0..length perm-1]-                  ]+  where+    prop :: Permutation -> Bool+    prop (Permutation perm) =+      case isMapTranspose perm of+        Nothing -> True+        Just (r1, r2, r3) ->+          and+            [ r1 >= 0,+              r2 > 0,+              r3 > 0,+              r1 + r2 + r3 == length perm,+              let (mapped, notmapped) = splitAt r1 perm+                  (pretrans, posttrans) = splitAt r2 notmapped+               in mapped ++ posttrans ++ pretrans == [0 .. length perm -1]+            ]
unittests/Futhark/IR/Prop/ReshapeTests.hs view
@@ -1,74 +1,74 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}+ module Futhark.IR.Prop.ReshapeTests-       ( tests-       )-       where+  ( tests,+  )+where  import Control.Applicative-+import Futhark.IR.Prop.Constants+import Futhark.IR.Prop.Reshape+import Futhark.IR.Syntax import Test.Tasty import Test.Tasty.HUnit import Test.Tasty.QuickCheck- import Prelude -import Futhark.IR.Prop.Reshape-import Futhark.IR.Syntax-import Futhark.IR.Prop.Constants- tests :: TestTree-tests = testGroup "ReshapeTests" $-        fuseReshapeTests ++-        informReshapeTests ++-        reshapeOuterTests ++-        reshapeInnerTests ++-        [ fuseReshapeProp-        , informReshapeProp-        ]+tests =+  testGroup "ReshapeTests" $+    fuseReshapeTests+      ++ informReshapeTests+      ++ reshapeOuterTests+      ++ reshapeInnerTests+      ++ [ fuseReshapeProp,+           informReshapeProp+         ]  fuseReshapeTests :: [TestTree] fuseReshapeTests =   [ testCase (unwords ["fuseReshape ", show d1, show d2]) $-    fuseReshape (d1 :: ShapeChange Int) d2 @?= dres -- type signature to avoid warning-  | (d1, d2, dres) <- [ ([DimCoercion 1], [DimNew 1], [DimCoercion 1])-                      , ([DimNew 1], [DimCoercion 1], [DimNew 1])-                      , ([DimCoercion 1, DimNew 2], [DimNew 1, DimNew 2], [DimCoercion 1, DimNew 2])-                      , ([DimNew 1, DimNew 2], [DimCoercion 1, DimNew 2], [DimNew 1, DimNew 2])-                      ]+      fuseReshape (d1 :: ShapeChange Int) d2 @?= dres -- type signature to avoid warning+    | (d1, d2, dres) <-+        [ ([DimCoercion 1], [DimNew 1], [DimCoercion 1]),+          ([DimNew 1], [DimCoercion 1], [DimNew 1]),+          ([DimCoercion 1, DimNew 2], [DimNew 1, DimNew 2], [DimCoercion 1, DimNew 2]),+          ([DimNew 1, DimNew 2], [DimCoercion 1, DimNew 2], [DimNew 1, DimNew 2])+        ]   ]  informReshapeTests :: [TestTree] informReshapeTests =   [ testCase (unwords ["informReshape ", show shape, show sc, show sc_res]) $-    informReshape (shape :: [Int]) sc @?= sc_res -- type signature to avoid warning-  | (shape, sc, sc_res) <--    [ ([1, 2], [DimNew 1, DimNew 3], [DimCoercion 1, DimNew 3])-    , ([2, 2], [DimNew 1, DimNew 3], [DimNew 1, DimNew 3])-    ]+      informReshape (shape :: [Int]) sc @?= sc_res -- type signature to avoid warning+    | (shape, sc, sc_res) <-+        [ ([1, 2], [DimNew 1, DimNew 3], [DimCoercion 1, DimNew 3]),+          ([2, 2], [DimNew 1, DimNew 3], [DimNew 1, DimNew 3])+        ]   ]  reshapeOuterTests :: [TestTree] reshapeOuterTests =   [ testCase (unwords ["reshapeOuter", show sc, show n, show shape, "==", show sc_res]) $-    reshapeOuter (intShapeChange sc) n (intShape shape) @?= intShapeChange sc_res-  | (sc, n, shape, sc_res) <--    [ ([DimNew 1], 1, [4, 3], [DimNew 1, DimCoercion 3])-    , ([DimNew 1], 2, [4, 3], [DimNew 1])-    , ([DimNew 2, DimNew 2], 1, [4, 3], [DimNew 2, DimNew 2, DimNew 3])-    , ([DimNew 2, DimNew 2], 2, [4, 3], [DimNew 2, DimNew 2])-    ]+      reshapeOuter (intShapeChange sc) n (intShape shape) @?= intShapeChange sc_res+    | (sc, n, shape, sc_res) <-+        [ ([DimNew 1], 1, [4, 3], [DimNew 1, DimCoercion 3]),+          ([DimNew 1], 2, [4, 3], [DimNew 1]),+          ([DimNew 2, DimNew 2], 1, [4, 3], [DimNew 2, DimNew 2, DimNew 3]),+          ([DimNew 2, DimNew 2], 2, [4, 3], [DimNew 2, DimNew 2])+        ]   ]  reshapeInnerTests :: [TestTree] reshapeInnerTests =   [ testCase (unwords ["reshapeInner", show sc, show n, show shape, "==", show sc_res]) $-    reshapeInner (intShapeChange sc) n (intShape shape) @?= intShapeChange sc_res-  | (sc, n, shape, sc_res) <--    [ ([DimNew 1], 1, [4, 3], [DimCoercion 4, DimNew 1])-    , ([DimNew 1], 0, [4, 3], [DimNew 1])-    , ([DimNew 2, DimNew 2], 1, [4, 3], [DimNew 4, DimNew 2, DimNew 2])-    , ([DimNew 2, DimNew 2], 0, [4, 3], [DimNew 2, DimNew 2])-    ]+      reshapeInner (intShapeChange sc) n (intShape shape) @?= intShapeChange sc_res+    | (sc, n, shape, sc_res) <-+        [ ([DimNew 1], 1, [4, 3], [DimCoercion 4, DimNew 1]),+          ([DimNew 1], 0, [4, 3], [DimNew 1]),+          ([DimNew 2, DimNew 2], 1, [4, 3], [DimNew 4, DimNew 2, DimNew 2]),+          ([DimNew 2, DimNew 2], 0, [4, 3], [DimNew 2, DimNew 2])+        ]   ]  intShape :: [Int] -> Shape@@ -79,15 +79,19 @@  fuseReshapeProp :: TestTree fuseReshapeProp = testProperty "fuseReshape result matches second argument" prop-  where prop :: ShapeChange Int -> ShapeChange Int -> Bool-        prop sc1 sc2 = map newDim (fuseReshape sc1 sc2) == map newDim sc2+  where+    prop :: ShapeChange Int -> ShapeChange Int -> Bool+    prop sc1 sc2 = map newDim (fuseReshape sc1 sc2) == map newDim sc2  informReshapeProp :: TestTree informReshapeProp = testProperty "informReshape result matches second argument" prop-  where prop :: [Int] -> ShapeChange Int -> Bool-        prop sc1 sc2 = map newDim (informReshape sc1 sc2) == map newDim sc2+  where+    prop :: [Int] -> ShapeChange Int -> Bool+    prop sc1 sc2 = map newDim (informReshape sc1 sc2) == map newDim sc2  instance Arbitrary d => Arbitrary (DimChange d) where-  arbitrary = oneof [ DimNew <$> arbitrary-                    , DimCoercion <$> arbitrary-                    ]+  arbitrary =+    oneof+      [ DimNew <$> arbitrary,+        DimCoercion <$> arbitrary+      ]
unittests/Futhark/IR/PropTests.hs view
@@ -1,16 +1,19 @@ {-# LANGUAGE FlexibleInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-}+ module Futhark.IR.PropTests-  ( tests+  ( tests,   ) where -import Test.Tasty--import qualified Futhark.IR.Prop.ReshapeTests import qualified Futhark.IR.Prop.RearrangeTests+import qualified Futhark.IR.Prop.ReshapeTests+import Test.Tasty  tests :: TestTree-tests = testGroup "PropTests"-        [Futhark.IR.Prop.ReshapeTests.tests,-         Futhark.IR.Prop.RearrangeTests.tests]+tests =+  testGroup+    "PropTests"+    [ Futhark.IR.Prop.ReshapeTests.tests,+      Futhark.IR.Prop.RearrangeTests.tests+    ]
unittests/Futhark/IR/Syntax/CoreTests.hs view
@@ -1,67 +1,69 @@ {-# LANGUAGE FlexibleInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-}-module Futhark.IR.Syntax.CoreTests-       ( tests )-       where -import Control.Applicative+module Futhark.IR.Syntax.CoreTests (tests) where +import Control.Applicative+import Futhark.IR.Pretty ()+import Futhark.IR.PrimitiveTests ()+import Futhark.IR.Syntax.Core+import Language.Futhark.CoreTests ()+import Test.QuickCheck import Test.Tasty import Test.Tasty.HUnit-import Test.QuickCheck- import Prelude -import Language.Futhark.CoreTests ()-import Futhark.IR.PrimitiveTests()-import Futhark.IR.Syntax.Core-import Futhark.IR.Pretty ()- tests :: TestTree tests = testGroup "Internal CoreTests" subShapeTests  subShapeTests :: [TestTree] subShapeTests =-  [ shape [free 1, free 2] `isSubShapeOf` shape [free 1, free 2]-  , shape [free 1, free 3] `isNotSubShapeOf` shape [free 1, free 2]-  , shape [free 1] `isNotSubShapeOf` shape [free 1, free 2]-  , shape [free 1, free 2] `isSubShapeOf` shape [free 1, Ext 3]-  , shape [Ext 1, Ext 2] `isNotSubShapeOf` shape [Ext 1, Ext 1]-  , shape [Ext 1, Ext 1] `isSubShapeOf` shape [Ext 1, Ext 2]+  [ shape [free 1, free 2] `isSubShapeOf` shape [free 1, free 2],+    shape [free 1, free 3] `isNotSubShapeOf` shape [free 1, free 2],+    shape [free 1] `isNotSubShapeOf` shape [free 1, free 2],+    shape [free 1, free 2] `isSubShapeOf` shape [free 1, Ext 3],+    shape [Ext 1, Ext 2] `isNotSubShapeOf` shape [Ext 1, Ext 1],+    shape [Ext 1, Ext 1] `isSubShapeOf` shape [Ext 1, Ext 2]   ]-  where shape :: [ExtSize] -> ExtShape-        shape = Shape+  where+    shape :: [ExtSize] -> ExtShape+    shape = Shape -        free :: Int -> ExtSize-        free = Free . Constant . IntValue . Int32Value . fromIntegral+    free :: Int -> ExtSize+    free = Free . Constant . IntValue . Int32Value . fromIntegral -        isSubShapeOf shape1 shape2 =-          subShapeTest shape1 shape2 True-        isNotSubShapeOf shape1 shape2 =-          subShapeTest shape1 shape2 False+    isSubShapeOf shape1 shape2 =+      subShapeTest shape1 shape2 True+    isNotSubShapeOf shape1 shape2 =+      subShapeTest shape1 shape2 False -        subShapeTest :: ExtShape -> ExtShape -> Bool -> TestTree-        subShapeTest shape1 shape2 expected =-          testCase ("subshapeOf " ++ pretty shape1 ++ " " ++-                    pretty shape2 ++ " == " ++-                    show expected) $-          shape1 `subShapeOf` shape2 @?= expected+    subShapeTest :: ExtShape -> ExtShape -> Bool -> TestTree+    subShapeTest shape1 shape2 expected =+      testCase+        ( "subshapeOf " ++ pretty shape1 ++ " "+            ++ pretty shape2+            ++ " == "+            ++ show expected+        )+        $ shape1 `subShapeOf` shape2 @?= expected  instance Arbitrary NoUniqueness where   arbitrary = pure NoUniqueness  instance (Arbitrary shape, Arbitrary u) => Arbitrary (TypeBase shape u) where   arbitrary =-    oneof [ Prim <$> arbitrary-          , Array <$> arbitrary <*> arbitrary <*> arbitrary-          ]+    oneof+      [ Prim <$> arbitrary,+        Array <$> arbitrary <*> arbitrary <*> arbitrary+      ]  instance Arbitrary Ident where   arbitrary = Ident <$> arbitrary <*> arbitrary  instance Arbitrary Rank where-  arbitrary = Rank <$> elements [1..9]+  arbitrary = Rank <$> elements [1 .. 9]  instance Arbitrary Shape where-  arbitrary = Shape . map intconst <$> listOf1 (elements [1..9])-    where intconst = Constant . IntValue . Int32Value+  arbitrary = Shape . map intconst <$> listOf1 (elements [1 .. 9])+    where+      intconst = Constant . IntValue . Int32Value
unittests/Futhark/IR/SyntaxTests.hs view
@@ -1,7 +1,6 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}-module Futhark.IR.SyntaxTests-  ()-where++module Futhark.IR.SyntaxTests () where  -- There isn't anything to test in this module.  At some point, maybe -- we can put some Arbitrary instances here.
unittests/Futhark/Pkg/SolveTests.hs view
@@ -1,59 +1,88 @@ {-# LANGUAGE OverloadedStrings #-}+ module Futhark.Pkg.SolveTests (tests) where  import qualified Data.Map as M-import qualified Data.Text as T import Data.Monoid-+import qualified Data.Text as T+import Futhark.Pkg.Solve+import Futhark.Pkg.Types import Test.Tasty import Test.Tasty.HUnit--import Futhark.Pkg.Types-import Futhark.Pkg.Solve- import Prelude  semverE :: T.Text -> SemVer semverE s = case parseVersion s of-              Left err -> error $ T.unpack s <>-                          " is not a valid version number: " <>-                          errorBundlePretty err-              Right x -> x+  Left err ->+    error $+      T.unpack s+        <> " is not a valid version number: "+        <> errorBundlePretty err+  Right x -> x  -- | A world of packages and interdependencies for testing the solver -- without touching the outside world. testEnv :: PkgRevDepInfo-testEnv = M.fromList $ concatMap frob-  [ ("athas", [ ("foo", [ ("0.1.0", [])-                        , ("0.2.0", [("athas/bar", "1.0.0")])-                        , ("0.3.0", [])])-              , ("foo@v2", [ ("2.0.0", [("athas/quux", "0.1.0")])])-              , ("bar", [ ("1.0.0", [])])-              , ("baz", [ ("0.1.0", [("athas/foo", "0.3.0")])])-              , ("quux", [ ("0.1.0", [ ("athas/foo", "0.2.0")-                                     , ("athas/baz", "0.1.0") ])])-              , ("quux_perm", [ ("0.1.0", [ ("athas/baz", "0.1.0")-                                          , ("athas/foo", "0.2.0")])])-              , ("x_bar", [ ("1.0.0", [("athas/bar", "1.0.0")])])-              , ("x_foo", [ ("1.0.0", [("athas/foo", "0.3.0")])])-              , ("tricky", [ ("1.0.0", [ ("athas/foo", "0.2.0")-                                       , ("athas/x_foo", "1.0.0")])])-              ])--  -- Some mutually recursive packages.-  , ("nasty", [ ("foo", [ ("1.0.0", [("nasty/bar", "1.0.0")])])-              , ("bar", [ ("1.0.0", [("nasty/foo", "1.0.0")])])])-  ]-  where frob (user, repos) = do-          (repo, repo_revs) <- repos-          (rev, deps) <- repo_revs-          let rev' = semverE rev-              onDep (dp, dv) = (dp, (semverE dv, Nothing))-              deps' = PkgRevDeps $ M.fromList $ map onDep deps-          return ((user <> "/" <> repo, rev'), deps')+testEnv =+  M.fromList $+    concatMap+      frob+      [ ( "athas",+          [ ( "foo",+              [ ("0.1.0", []),+                ("0.2.0", [("athas/bar", "1.0.0")]),+                ("0.3.0", [])+              ]+            ),+            ("foo@v2", [("2.0.0", [("athas/quux", "0.1.0")])]),+            ("bar", [("1.0.0", [])]),+            ("baz", [("0.1.0", [("athas/foo", "0.3.0")])]),+            ( "quux",+              [ ( "0.1.0",+                  [ ("athas/foo", "0.2.0"),+                    ("athas/baz", "0.1.0")+                  ]+                )+              ]+            ),+            ( "quux_perm",+              [ ( "0.1.0",+                  [ ("athas/baz", "0.1.0"),+                    ("athas/foo", "0.2.0")+                  ]+                )+              ]+            ),+            ("x_bar", [("1.0.0", [("athas/bar", "1.0.0")])]),+            ("x_foo", [("1.0.0", [("athas/foo", "0.3.0")])]),+            ( "tricky",+              [ ( "1.0.0",+                  [ ("athas/foo", "0.2.0"),+                    ("athas/x_foo", "1.0.0")+                  ]+                )+              ]+            )+          ]+        ),+        -- Some mutually recursive packages.+        ( "nasty",+          [ ("foo", [("1.0.0", [("nasty/bar", "1.0.0")])]),+            ("bar", [("1.0.0", [("nasty/foo", "1.0.0")])])+          ]+        )+      ]+  where+    frob (user, repos) = do+      (repo, repo_revs) <- repos+      (rev, deps) <- repo_revs+      let rev' = semverE rev+          onDep (dp, dv) = (dp, (semverE dv, Nothing))+          deps' = PkgRevDeps $ M.fromList $ map onDep deps+      return ((user <> "/" <> repo, rev'), deps')  newtype SolverRes = SolverRes BuildList-                    deriving (Eq)+  deriving (Eq)  instance Show SolverRes where   show (SolverRes bl) = T.unpack $ prettyBuildList bl@@ -61,49 +90,55 @@ solverTest :: PkgPath -> T.Text -> Either T.Text [(PkgPath, T.Text)] -> TestTree solverTest p v expected =   testCase (T.unpack $ p <> "-" <> prettySemVer v') $-  fmap SolverRes (solveDepsPure testEnv target)-  @?= expected'-  where target = PkgRevDeps $ M.singleton p (v', Nothing)-        v' = semverE v-        expected' = SolverRes . BuildList . M.fromList . map onRes <$> expected-        onRes (dp, dv) = (dp, semverE dv)+    fmap SolverRes (solveDepsPure testEnv target)+      @?= expected'+  where+    target = PkgRevDeps $ M.singleton p (v', Nothing)+    v' = semverE v+    expected' = SolverRes . BuildList . M.fromList . map onRes <$> expected+    onRes (dp, dv) = (dp, semverE dv)  tests :: TestTree-tests = testGroup "SolveTests"-  [-    solverTest "athas/foo" "0.1.0" $-    Right [ ("athas/foo", "0.1.0")]--  , solverTest "athas/foo" "0.2.0" $-    Right [ ("athas/foo", "0.2.0")-          , ("athas/bar", "1.0.0")]--  , solverTest "athas/quux" "0.1.0" $-    Right [ ("athas/quux", "0.1.0")-          , ("athas/foo", "0.3.0")-          , ("athas/baz", "0.1.0")]--  , solverTest "athas/quux_perm" "0.1.0" $-    Right [ ("athas/quux_perm", "0.1.0")-          , ("athas/foo", "0.3.0")-          , ("athas/baz", "0.1.0")]--  , solverTest "athas/foo@v2" "2.0.0" $-    Right [ ("athas/foo@v2", "2.0.0")-          , ("athas/quux", "0.1.0")-          , ("athas/foo", "0.3.0")-          , ("athas/baz", "0.1.0")+tests =+  testGroup+    "SolveTests"+    [ solverTest "athas/foo" "0.1.0" $+        Right [("athas/foo", "0.1.0")],+      solverTest "athas/foo" "0.2.0" $+        Right+          [ ("athas/foo", "0.2.0"),+            ("athas/bar", "1.0.0")+          ],+      solverTest "athas/quux" "0.1.0" $+        Right+          [ ("athas/quux", "0.1.0"),+            ("athas/foo", "0.3.0"),+            ("athas/baz", "0.1.0")+          ],+      solverTest "athas/quux_perm" "0.1.0" $+        Right+          [ ("athas/quux_perm", "0.1.0"),+            ("athas/foo", "0.3.0"),+            ("athas/baz", "0.1.0")+          ],+      solverTest "athas/foo@v2" "2.0.0" $+        Right+          [ ("athas/foo@v2", "2.0.0"),+            ("athas/quux", "0.1.0"),+            ("athas/foo", "0.3.0"),+            ("athas/baz", "0.1.0")+          ],+      solverTest "athas/foo@v3" "3.0.0" $+        Left "Unknown package/version: athas/foo@v3-3.0.0",+      solverTest "nasty/foo" "1.0.0" $+        Right+          [ ("nasty/foo", "1.0.0"),+            ("nasty/bar", "1.0.0")+          ],+      solverTest "athas/tricky" "1.0.0" $+        Right+          [ ("athas/tricky", "1.0.0"),+            ("athas/foo", "0.3.0"),+            ("athas/x_foo", "1.0.0")           ]--  , solverTest "athas/foo@v3" "3.0.0" $-    Left "Unknown package/version: athas/foo@v3-3.0.0"--  , solverTest "nasty/foo" "1.0.0" $-    Right [ ("nasty/foo", "1.0.0")-          , ("nasty/bar", "1.0.0")]--  , solverTest "athas/tricky" "1.0.0" $-    Right [ ("athas/tricky", "1.0.0")-          , ("athas/foo", "0.3.0")-          , ("athas/x_foo", "1.0.0")]-  ]+    ]
unittests/Language/Futhark/CoreTests.hs view
@@ -1,15 +1,14 @@ {-# LANGUAGE FlexibleInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-}-module Language.Futhark.CoreTests ()-where -import Test.QuickCheck+module Language.Futhark.CoreTests () where +import Futhark.IR.PrimitiveTests () import Language.Futhark.Core-import Futhark.IR.PrimitiveTests()+import Test.QuickCheck  instance Arbitrary Name where-  arbitrary = nameFromString <$> listOf1 (elements ['a'..'z'])+  arbitrary = nameFromString <$> listOf1 (elements ['a' .. 'z'])  instance Arbitrary VName where   arbitrary = VName <$> arbitrary <*> arbitrary
unittests/Language/Futhark/SyntaxTests.hs view
@@ -1,38 +1,38 @@-{-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE FlexibleInstances #-}-module Language.Futhark.SyntaxTests (tests)-where--import Control.Applicative+{-# OPTIONS_GHC -fno-warn-orphans #-} -import Prelude+module Language.Futhark.SyntaxTests (tests) where +import Control.Applicative+import Futhark.IR.PrimitiveTests ()+import Language.Futhark.Syntax import Test.QuickCheck import Test.Tasty--import Language.Futhark.Syntax--import Futhark.IR.PrimitiveTests()+import Prelude  tests :: TestTree tests = testGroup "Source SyntaxTests" []  instance Arbitrary BinOp where-  arbitrary = elements [minBound..maxBound]+  arbitrary = elements [minBound .. maxBound]  instance Arbitrary Uniqueness where   arbitrary = elements [Unique, Nonunique]  instance Arbitrary PrimType where-  arbitrary = oneof [ Signed <$> arbitrary-                    , Unsigned <$> arbitrary-                    , FloatType <$> arbitrary-                    , pure Bool-                    ]+  arbitrary =+    oneof+      [ Signed <$> arbitrary,+        Unsigned <$> arbitrary,+        FloatType <$> arbitrary,+        pure Bool+      ]  instance Arbitrary PrimValue where-  arbitrary = oneof [ SignedValue <$> arbitrary-                    , UnsignedValue <$> arbitrary-                    , FloatValue <$> arbitrary-                    , BoolValue <$> arbitrary-                    ]+  arbitrary =+    oneof+      [ SignedValue <$> arbitrary,+        UnsignedValue <$> arbitrary,+        FloatValue <$> arbitrary,+        BoolValue <$> arbitrary+      ]
unittests/futhark_tests.hs view
@@ -1,26 +1,26 @@ module Main (main) where -import qualified Language.Futhark.SyntaxTests import qualified Futhark.BenchTests-import qualified Futhark.IR.Syntax.CoreTests-import qualified Futhark.IR.PropTests import qualified Futhark.IR.Mem.IxFunTests-import qualified Futhark.Pkg.SolveTests import qualified Futhark.IR.PrimitiveTests-+import qualified Futhark.IR.PropTests+import qualified Futhark.IR.Syntax.CoreTests+import qualified Futhark.Pkg.SolveTests+import qualified Language.Futhark.SyntaxTests import Test.Tasty  allTests :: TestTree allTests =-  testGroup ""-  [ Language.Futhark.SyntaxTests.tests-  , Futhark.BenchTests.tests-  , Futhark.IR.PropTests.tests-  , Futhark.IR.Syntax.CoreTests.tests-  , Futhark.Pkg.SolveTests.tests-  , Futhark.IR.Mem.IxFunTests.tests-  , Futhark.IR.PrimitiveTests.tests-  ]+  testGroup+    ""+    [ Language.Futhark.SyntaxTests.tests,+      Futhark.BenchTests.tests,+      Futhark.IR.PropTests.tests,+      Futhark.IR.Syntax.CoreTests.tests,+      Futhark.Pkg.SolveTests.tests,+      Futhark.IR.Mem.IxFunTests.tests,+      Futhark.IR.PrimitiveTests.tests+    ]  main :: IO () main = defaultMain allTests