packages feed

futhark 0.25.9 → 0.25.10

raw patch · 62 files changed

+1843/−2459 lines, 62 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Futhark.CodeGen.Backends.GenericC.Code: compileExpToName :: String -> PrimType -> Exp -> CompilerM op s VName
- Futhark.CodeGen.Backends.GenericPython: compilePrimTypeExt :: PrimType -> Signedness -> String
- Futhark.CodeGen.ImpGen: comment :: Text -> ImpM rep r op () -> ImpM rep r op ()
- Futhark.IR.Mem: instance (GHC.Classes.Eq d, GHC.Classes.Eq ret, GHC.Classes.Eq u) => GHC.Classes.Eq (Futhark.IR.Mem.MemInfo d u ret)
- Futhark.IR.Mem: instance (GHC.Classes.Ord d, GHC.Classes.Ord ret, GHC.Classes.Ord u) => GHC.Classes.Ord (Futhark.IR.Mem.MemInfo d u ret)
- Futhark.IR.Mem: instance (GHC.Show.Show d, GHC.Show.Show ret, GHC.Show.Show u) => GHC.Show.Show (Futhark.IR.Mem.MemInfo d u ret)
- Futhark.IR.Syntax.Core: instance (GHC.Classes.Eq shape, GHC.Classes.Eq u) => GHC.Classes.Eq (Futhark.IR.Syntax.Core.TypeBase shape u)
- Futhark.IR.Syntax.Core: instance (GHC.Classes.Ord shape, GHC.Classes.Ord u) => GHC.Classes.Ord (Futhark.IR.Syntax.Core.TypeBase shape u)
- Futhark.IR.Syntax.Core: instance (GHC.Show.Show shape, GHC.Show.Show u) => GHC.Show.Show (Futhark.IR.Syntax.Core.TypeBase shape u)
- Futhark.Optimise.InPlaceLowering: inPlaceLoweringGPU :: Pass GPU GPU
- Futhark.Optimise.InPlaceLowering: inPlaceLoweringMC :: Pass MC MC
- Futhark.Optimise.InPlaceLowering: inPlaceLoweringSeq :: Pass Seq Seq
- Futhark.Optimise.InPlaceLowering: instance Control.Monad.Reader.Class.MonadReader (Futhark.Optimise.InPlaceLowering.TopDown rep) (Futhark.Optimise.InPlaceLowering.ForwardingM rep)
- Futhark.Optimise.InPlaceLowering: instance Control.Monad.State.Class.MonadState Futhark.FreshNames.VNameSource (Futhark.Optimise.InPlaceLowering.ForwardingM rep)
- Futhark.Optimise.InPlaceLowering: instance Control.Monad.Writer.Class.MonadWriter (Futhark.Optimise.InPlaceLowering.BottomUp rep) (Futhark.Optimise.InPlaceLowering.ForwardingM rep)
- Futhark.Optimise.InPlaceLowering: instance Futhark.MonadFreshNames.MonadFreshNames (Futhark.Optimise.InPlaceLowering.ForwardingM rep)
- Futhark.Optimise.InPlaceLowering: instance Futhark.Optimise.InPlaceLowering.Constraints rep => Futhark.IR.Prop.Scope.HasScope (Futhark.IR.Aliases.Aliases rep) (Futhark.Optimise.InPlaceLowering.ForwardingM rep)
- Futhark.Optimise.InPlaceLowering: instance GHC.Base.Applicative (Futhark.Optimise.InPlaceLowering.ForwardingM rep)
- Futhark.Optimise.InPlaceLowering: instance GHC.Base.Functor (Futhark.Optimise.InPlaceLowering.ForwardingM rep)
- Futhark.Optimise.InPlaceLowering: instance GHC.Base.Monad (Futhark.Optimise.InPlaceLowering.ForwardingM rep)
- Futhark.Optimise.InPlaceLowering: instance GHC.Base.Monoid (Futhark.Optimise.InPlaceLowering.BottomUp rep)
- Futhark.Optimise.InPlaceLowering: instance GHC.Base.Semigroup (Futhark.Optimise.InPlaceLowering.BottomUp rep)
- Futhark.Optimise.InPlaceLowering.LowerIntoStm: DesiredUpdate :: VName -> dec -> Certs -> VName -> Slice SubExp -> VName -> DesiredUpdate dec
- Futhark.Optimise.InPlaceLowering.LowerIntoStm: [updateCerts] :: DesiredUpdate dec -> Certs
- Futhark.Optimise.InPlaceLowering.LowerIntoStm: [updateIndices] :: DesiredUpdate dec -> Slice SubExp
- Futhark.Optimise.InPlaceLowering.LowerIntoStm: [updateName] :: DesiredUpdate dec -> VName
- Futhark.Optimise.InPlaceLowering.LowerIntoStm: [updateSource] :: DesiredUpdate dec -> VName
- Futhark.Optimise.InPlaceLowering.LowerIntoStm: [updateType] :: DesiredUpdate dec -> dec
- Futhark.Optimise.InPlaceLowering.LowerIntoStm: [updateValue] :: DesiredUpdate dec -> VName
- Futhark.Optimise.InPlaceLowering.LowerIntoStm: data DesiredUpdate dec
- Futhark.Optimise.InPlaceLowering.LowerIntoStm: instance GHC.Base.Functor Futhark.Optimise.InPlaceLowering.LowerIntoStm.DesiredUpdate
- Futhark.Optimise.InPlaceLowering.LowerIntoStm: instance GHC.Show.Show dec => GHC.Show.Show (Futhark.Optimise.InPlaceLowering.LowerIntoStm.DesiredUpdate dec)
- Futhark.Optimise.InPlaceLowering.LowerIntoStm: instance GHC.Show.Show dec => GHC.Show.Show (Futhark.Optimise.InPlaceLowering.LowerIntoStm.LoopResultSummary dec)
- Futhark.Optimise.InPlaceLowering.LowerIntoStm: lowerUpdate :: (MonadFreshNames m, Buildable rep, LetDec rep ~ Type, AliasableRep rep) => LowerUpdate rep m
- Futhark.Optimise.InPlaceLowering.LowerIntoStm: lowerUpdateGPU :: MonadFreshNames m => LowerUpdate GPU m
- Futhark.Optimise.InPlaceLowering.LowerIntoStm: type LowerUpdate rep m = Scope (Aliases rep) -> Stm (Aliases rep) -> [DesiredUpdate (LetDec (Aliases rep))] -> Maybe (m [Stm (Aliases rep)])
- Futhark.Optimise.InPlaceLowering.SubstituteIndices: substituteIndices :: (MonadFreshNames m, BuilderOps rep, Buildable rep, Aliased rep) => IndexSubstitutions -> Stms rep -> m (IndexSubstitutions, Stms rep)
- Futhark.Optimise.InPlaceLowering.SubstituteIndices: type IndexSubstitution = (Certs, VName, Type, Slice SubExp)
- Futhark.Optimise.InPlaceLowering.SubstituteIndices: type IndexSubstitutions = [(VName, IndexSubstitution)]
- Language.Futhark.Interpreter: instance Control.Monad.State.Class.MonadState Language.Futhark.Interpreter.Sizes Language.Futhark.Interpreter.EvalM
- Language.Futhark.Pretty: instance (GHC.Classes.Eq vn, Language.Futhark.Pretty.IsName vn, Language.Futhark.Pretty.Annot f) => Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.SigBindBase f vn)
- Language.Futhark.Pretty: instance (GHC.Classes.Eq vn, Language.Futhark.Pretty.IsName vn, Language.Futhark.Pretty.Annot f) => Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.SigExpBase f vn)
- Language.Futhark.Prop: type SigBind = SigBindBase Info VName
- Language.Futhark.Prop: type SigExp = SigExpBase Info VName
- Language.Futhark.Prop: type UncheckedSigBind = SigBindBase NoInfo Name
- Language.Futhark.Prop: type UncheckedSigExp = SigExpBase NoInfo Name
- Language.Futhark.Semantic: FunSig :: TySet -> Mod -> MTy -> FunSig
- Language.Futhark.Semantic: [envSigTable] :: Env -> Map VName MTy
- Language.Futhark.Semantic: [funSigAbs] :: FunSig -> TySet
- Language.Futhark.Semantic: [funSigMod] :: FunSig -> Mod
- Language.Futhark.Semantic: [funSigMty] :: FunSig -> MTy
- Language.Futhark.Semantic: data FunSig
- Language.Futhark.Semantic: instance GHC.Show.Show Language.Futhark.Semantic.FunSig
- Language.Futhark.Syntax: SigArrow :: Maybe vn -> SigExpBase f vn -> SigExpBase f vn -> SrcLoc -> SigExpBase f vn
- Language.Futhark.Syntax: SigBind :: vn -> SigExpBase f vn -> Maybe DocComment -> SrcLoc -> SigBindBase f vn
- Language.Futhark.Syntax: SigDec :: SigBindBase f vn -> DecBase f vn
- Language.Futhark.Syntax: SigParens :: SigExpBase f vn -> SrcLoc -> SigExpBase f vn
- Language.Futhark.Syntax: SigSpecs :: [SpecBase f vn] -> SrcLoc -> SigExpBase f vn
- Language.Futhark.Syntax: SigVar :: QualName vn -> f (Map VName VName) -> SrcLoc -> SigExpBase f vn
- Language.Futhark.Syntax: SigWith :: SigExpBase f vn -> TypeRefBase f vn -> SrcLoc -> SigExpBase f vn
- Language.Futhark.Syntax: [modSignature] :: ModBindBase f vn -> Maybe (SigExpBase f vn, f (Map VName VName))
- Language.Futhark.Syntax: [sigDoc] :: SigBindBase f vn -> Maybe DocComment
- Language.Futhark.Syntax: [sigExp] :: SigBindBase f vn -> SigExpBase f vn
- Language.Futhark.Syntax: [sigLoc] :: SigBindBase f vn -> SrcLoc
- Language.Futhark.Syntax: [sigName] :: SigBindBase f vn -> vn
- Language.Futhark.Syntax: data SigBindBase f vn
- Language.Futhark.Syntax: data SigExpBase f vn
- Language.Futhark.Syntax: instance (GHC.Classes.Eq dim, GHC.Classes.Eq u) => GHC.Classes.Eq (Language.Futhark.Syntax.ScalarTypeBase dim u)
- Language.Futhark.Syntax: instance (GHC.Classes.Eq dim, GHC.Classes.Eq u) => GHC.Classes.Eq (Language.Futhark.Syntax.TypeBase dim u)
- Language.Futhark.Syntax: instance (GHC.Classes.Ord dim, GHC.Classes.Ord u) => GHC.Classes.Ord (Language.Futhark.Syntax.ScalarTypeBase dim u)
- Language.Futhark.Syntax: instance (GHC.Classes.Ord dim, GHC.Classes.Ord u) => GHC.Classes.Ord (Language.Futhark.Syntax.TypeBase dim u)
- Language.Futhark.Syntax: instance (GHC.Show.Show dim, GHC.Show.Show u) => GHC.Show.Show (Language.Futhark.Syntax.ScalarTypeBase dim u)
- Language.Futhark.Syntax: instance (GHC.Show.Show dim, GHC.Show.Show u) => GHC.Show.Show (Language.Futhark.Syntax.TypeBase dim u)
- Language.Futhark.Syntax: instance Data.Loc.Located (Language.Futhark.Syntax.SigBindBase f vn)
- Language.Futhark.Syntax: instance Data.Loc.Located (Language.Futhark.Syntax.SigExpBase f vn)
- Language.Futhark.Syntax: instance GHC.Show.Show (Language.Futhark.Syntax.SigBindBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName)
- Language.Futhark.Syntax: instance GHC.Show.Show (Language.Futhark.Syntax.SigBindBase Language.Futhark.Syntax.NoInfo Language.Futhark.Core.Name)
- Language.Futhark.Syntax: instance GHC.Show.Show (Language.Futhark.Syntax.SigExpBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName)
- Language.Futhark.Syntax: instance GHC.Show.Show (Language.Futhark.Syntax.SigExpBase Language.Futhark.Syntax.NoInfo Language.Futhark.Core.Name)
- Language.Futhark.TypeChecker.Monad: FunSig :: TySet -> Mod -> MTy -> FunSig
- Language.Futhark.TypeChecker.Monad: [envSigTable] :: Env -> Map VName MTy
- Language.Futhark.TypeChecker.Monad: [funSigAbs] :: FunSig -> TySet
- Language.Futhark.TypeChecker.Monad: [funSigMod] :: FunSig -> Mod
- Language.Futhark.TypeChecker.Monad: [funSigMty] :: FunSig -> MTy
- Language.Futhark.TypeChecker.Monad: data FunSig
+ Futhark.CodeGen.ImpCode: SizeCache :: SizeClass
+ Futhark.CodeGen.ImpCode: SizeRegisters :: SizeClass
+ Futhark.CodeGen.ImpCode.OpenCL: [openClMacroDefs] :: Program -> [(Name, KernelConstExp)]
+ Futhark.CodeGen.ImpCode.OpenCL: type KernelConstExp = PrimExp KernelConst
+ Futhark.CodeGen.ImpGen: newVName :: MonadFreshNames m => String -> m VName
+ Futhark.CodeGen.ImpGen.GPU.Base: [kAttrConstExps] :: KernelAttrs -> Map VName KernelConstExp
+ Futhark.CodeGen.ImpGen.GPU.Base: getChunkSize :: [Type] -> KernelConstExp
+ Futhark.CodeGen.ImpGen.GPU.Base: isPrimParam :: Typed p => Param p -> Bool
+ Futhark.CodeGen.ImpGen.GPU.Base: kernelConstToExp :: KernelConstExp -> CallKernelGen Exp
+ Futhark.IR.GPU.Sizes: SizeCache :: SizeClass
+ Futhark.IR.GPU.Sizes: SizeRegisters :: SizeClass
+ Futhark.IR.Mem: instance (GHC.Classes.Eq d, GHC.Classes.Eq u, GHC.Classes.Eq ret) => GHC.Classes.Eq (Futhark.IR.Mem.MemInfo d u ret)
+ Futhark.IR.Mem: instance (GHC.Classes.Ord d, GHC.Classes.Ord u, GHC.Classes.Ord ret) => GHC.Classes.Ord (Futhark.IR.Mem.MemInfo d u ret)
+ Futhark.IR.Mem: instance (GHC.Show.Show d, GHC.Show.Show u, GHC.Show.Show ret) => GHC.Show.Show (Futhark.IR.Mem.MemInfo d u ret)
+ Futhark.IR.Syntax.Core: instance (GHC.Classes.Eq u, GHC.Classes.Eq shape) => GHC.Classes.Eq (Futhark.IR.Syntax.Core.TypeBase shape u)
+ Futhark.IR.Syntax.Core: instance (GHC.Classes.Ord u, GHC.Classes.Ord shape) => GHC.Classes.Ord (Futhark.IR.Syntax.Core.TypeBase shape u)
+ Futhark.IR.Syntax.Core: instance (GHC.Show.Show u, GHC.Show.Show shape) => GHC.Show.Show (Futhark.IR.Syntax.Core.TypeBase shape u)
+ Futhark.Util.IntegralExp: nextMul :: IntegralExp e => e -> e -> e
+ Language.Futhark.Interpreter: instance Control.Monad.State.Class.MonadState Language.Futhark.Interpreter.Exts Language.Futhark.Interpreter.EvalM
+ Language.Futhark.Pretty: instance (GHC.Classes.Eq vn, Language.Futhark.Pretty.IsName vn, Language.Futhark.Pretty.Annot f) => Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.ModTypeBindBase f vn)
+ Language.Futhark.Pretty: instance (GHC.Classes.Eq vn, Language.Futhark.Pretty.IsName vn, Language.Futhark.Pretty.Annot f) => Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.ModTypeExpBase f vn)
+ Language.Futhark.Prop: type ModTypeBind = ModTypeBindBase Info VName
+ Language.Futhark.Prop: type ModTypeExp = ModTypeExpBase Info VName
+ Language.Futhark.Prop: type UncheckedModTypeBind = ModTypeBindBase NoInfo Name
+ Language.Futhark.Prop: type UncheckedModTypeExp = ModTypeExpBase NoInfo Name
+ Language.Futhark.Semantic: FunModType :: TySet -> Mod -> MTy -> FunModType
+ Language.Futhark.Semantic: [envModTypeTable] :: Env -> Map VName MTy
+ Language.Futhark.Semantic: [funModTypeAbs] :: FunModType -> TySet
+ Language.Futhark.Semantic: [funModTypeMod] :: FunModType -> Mod
+ Language.Futhark.Semantic: [funModTypeMty] :: FunModType -> MTy
+ Language.Futhark.Semantic: data FunModType
+ Language.Futhark.Semantic: instance GHC.Show.Show Language.Futhark.Semantic.FunModType
+ Language.Futhark.Syntax: ModTypeArrow :: Maybe vn -> ModTypeExpBase f vn -> ModTypeExpBase f vn -> SrcLoc -> ModTypeExpBase f vn
+ Language.Futhark.Syntax: ModTypeBind :: vn -> ModTypeExpBase f vn -> Maybe DocComment -> SrcLoc -> ModTypeBindBase f vn
+ Language.Futhark.Syntax: ModTypeDec :: ModTypeBindBase f vn -> DecBase f vn
+ Language.Futhark.Syntax: ModTypeParens :: ModTypeExpBase f vn -> SrcLoc -> ModTypeExpBase f vn
+ Language.Futhark.Syntax: ModTypeSpecs :: [SpecBase f vn] -> SrcLoc -> ModTypeExpBase f vn
+ Language.Futhark.Syntax: ModTypeVar :: QualName vn -> f (Map VName VName) -> SrcLoc -> ModTypeExpBase f vn
+ Language.Futhark.Syntax: ModTypeWith :: ModTypeExpBase f vn -> TypeRefBase f vn -> SrcLoc -> ModTypeExpBase f vn
+ Language.Futhark.Syntax: [modTypeDoc] :: ModTypeBindBase f vn -> Maybe DocComment
+ Language.Futhark.Syntax: [modTypeExp] :: ModTypeBindBase f vn -> ModTypeExpBase f vn
+ Language.Futhark.Syntax: [modTypeLoc] :: ModTypeBindBase f vn -> SrcLoc
+ Language.Futhark.Syntax: [modTypeName] :: ModTypeBindBase f vn -> vn
+ Language.Futhark.Syntax: [modType] :: ModBindBase f vn -> Maybe (ModTypeExpBase f vn, f (Map VName VName))
+ Language.Futhark.Syntax: data ModTypeBindBase f vn
+ Language.Futhark.Syntax: data ModTypeExpBase f vn
+ Language.Futhark.Syntax: instance (GHC.Classes.Eq u, GHC.Classes.Eq dim) => GHC.Classes.Eq (Language.Futhark.Syntax.ScalarTypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Classes.Eq u, GHC.Classes.Eq dim) => GHC.Classes.Eq (Language.Futhark.Syntax.TypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Classes.Ord u, GHC.Classes.Ord dim) => GHC.Classes.Ord (Language.Futhark.Syntax.ScalarTypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Classes.Ord u, GHC.Classes.Ord dim) => GHC.Classes.Ord (Language.Futhark.Syntax.TypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Show.Show u, GHC.Show.Show dim) => GHC.Show.Show (Language.Futhark.Syntax.ScalarTypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Show.Show u, GHC.Show.Show dim) => GHC.Show.Show (Language.Futhark.Syntax.TypeBase dim u)
+ Language.Futhark.Syntax: instance Data.Loc.Located (Language.Futhark.Syntax.ModTypeBindBase f vn)
+ Language.Futhark.Syntax: instance Data.Loc.Located (Language.Futhark.Syntax.ModTypeExpBase f vn)
+ Language.Futhark.Syntax: instance GHC.Show.Show (Language.Futhark.Syntax.ModTypeBindBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName)
+ Language.Futhark.Syntax: instance GHC.Show.Show (Language.Futhark.Syntax.ModTypeBindBase Language.Futhark.Syntax.NoInfo Language.Futhark.Core.Name)
+ Language.Futhark.Syntax: instance GHC.Show.Show (Language.Futhark.Syntax.ModTypeExpBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName)
+ Language.Futhark.Syntax: instance GHC.Show.Show (Language.Futhark.Syntax.ModTypeExpBase Language.Futhark.Syntax.NoInfo Language.Futhark.Core.Name)
+ Language.Futhark.TypeChecker.Monad: FunModType :: TySet -> Mod -> MTy -> FunModType
+ Language.Futhark.TypeChecker.Monad: [envModTypeTable] :: Env -> Map VName MTy
+ Language.Futhark.TypeChecker.Monad: [funModTypeAbs] :: FunModType -> TySet
+ Language.Futhark.TypeChecker.Monad: [funModTypeMod] :: FunModType -> Mod
+ Language.Futhark.TypeChecker.Monad: [funModTypeMty] :: FunModType -> MTy
+ Language.Futhark.TypeChecker.Monad: data FunModType
- Futhark.CodeGen.Backends.GPU: generateGPUBoilerplate :: Text -> Text -> [Name] -> [PrimType] -> [FailureMsg] -> CompilerM OpenCL () ()
+ Futhark.CodeGen.Backends.GPU: generateGPUBoilerplate :: Text -> [(Name, KernelConstExp)] -> Text -> [Name] -> [PrimType] -> [FailureMsg] -> CompilerM OpenCL () ()
- Futhark.CodeGen.Backends.PyOpenCL.Boilerplate: openClInit :: [PrimType] -> String -> ParamMap -> [FailureMsg] -> Text
+ Futhark.CodeGen.Backends.PyOpenCL.Boilerplate: openClInit :: [(Name, KernelConstExp)] -> [PrimType] -> String -> ParamMap -> [FailureMsg] -> Text
- Futhark.CodeGen.ImpCode.GPU: SizeConst :: Name -> KernelConst
+ Futhark.CodeGen.ImpCode.GPU: SizeConst :: Name -> SizeClass -> KernelConst
- Futhark.CodeGen.ImpCode.OpenCL: Program :: Text -> Text -> Map KernelName KernelSafety -> [PrimType] -> ParamMap -> [FailureMsg] -> Definitions OpenCL -> Program
+ Futhark.CodeGen.ImpCode.OpenCL: Program :: Text -> Text -> [(Name, KernelConstExp)] -> Map KernelName KernelSafety -> [PrimType] -> ParamMap -> [FailureMsg] -> Definitions OpenCL -> Program
- Futhark.CodeGen.ImpCode.OpenCL: SizeConst :: Name -> KernelConst
+ Futhark.CodeGen.ImpCode.OpenCL: SizeConst :: Name -> SizeClass -> KernelConst
- Futhark.CodeGen.ImpGen.GPU.Base: KernelAttrs :: Bool -> Bool -> Count NumGroups SubExp -> Count GroupSize SubExp -> KernelAttrs
+ Futhark.CodeGen.ImpGen.GPU.Base: KernelAttrs :: Bool -> Bool -> Count NumGroups SubExp -> Count GroupSize SubExp -> Map VName KernelConstExp -> KernelAttrs
- Futhark.IR.Syntax: class () => Pretty a
+ Futhark.IR.Syntax: class Pretty a
- Futhark.Pkg.Types: data () => Chunk
+ Futhark.Pkg.Types: data Chunk
- Futhark.Pkg.Types: data () => SemVer
+ Futhark.Pkg.Types: data SemVer
- Futhark.Pkg.Types: newtype () => Release
+ Futhark.Pkg.Types: newtype Release
- Futhark.Test: data () => Mismatch
+ Futhark.Test: data Mismatch
- Futhark.Test: data () => Value
+ Futhark.Test: data Value
- Futhark.Util.Pretty: data () => AnsiStyle
+ Futhark.Util.Pretty: data AnsiStyle
- Futhark.Util.Pretty: data () => Color
+ Futhark.Util.Pretty: data Color
- Futhark.Util.Table: data () => AnsiStyle
+ Futhark.Util.Table: data AnsiStyle
- Futhark.Util.Table: data () => Color
+ Futhark.Util.Table: data Color
- Language.Futhark.Core: class () => Located a
+ Language.Futhark.Core: class Located a
- Language.Futhark.Core: data () => Half
+ Language.Futhark.Core: data Half
- Language.Futhark.Core: data () => Int16
+ Language.Futhark.Core: data Int16
- Language.Futhark.Core: data () => Int32
+ Language.Futhark.Core: data Int32
- Language.Futhark.Core: data () => Int64
+ Language.Futhark.Core: data Int64
- Language.Futhark.Core: data () => Int8
+ Language.Futhark.Core: data Int8
- Language.Futhark.Core: data () => Loc
+ Language.Futhark.Core: data Loc
- Language.Futhark.Core: data () => SrcLoc
+ Language.Futhark.Core: data SrcLoc
- Language.Futhark.Core: data () => Word16
+ Language.Futhark.Core: data Word16
- Language.Futhark.Core: data () => Word32
+ Language.Futhark.Core: data Word32
- Language.Futhark.Core: data () => Word64
+ Language.Futhark.Core: data Word64
- Language.Futhark.Core: data () => Word8
+ Language.Futhark.Core: data Word8
- Language.Futhark.Parser.Monad: data () => L a
+ Language.Futhark.Parser.Monad: data L a
- Language.Futhark.Primitive: data () => Half
+ Language.Futhark.Primitive: data Half
- Language.Futhark.Primitive: data () => Int16
+ Language.Futhark.Primitive: data Int16
- Language.Futhark.Primitive: data () => Int32
+ Language.Futhark.Primitive: data Int32
- Language.Futhark.Primitive: data () => Int64
+ Language.Futhark.Primitive: data Int64
- Language.Futhark.Primitive: data () => Int8
+ Language.Futhark.Primitive: data Int8
- Language.Futhark.Primitive: data () => Word16
+ Language.Futhark.Primitive: data Word16
- Language.Futhark.Primitive: data () => Word32
+ Language.Futhark.Primitive: data Word32
- Language.Futhark.Primitive: data () => Word64
+ Language.Futhark.Primitive: data Word64
- Language.Futhark.Primitive: data () => Word8
+ Language.Futhark.Primitive: data Word8
- Language.Futhark.Query: data () => Pos
+ Language.Futhark.Query: data Pos
- Language.Futhark.Semantic: ModFun :: FunSig -> Mod
+ Language.Futhark.Semantic: ModFun :: FunModType -> Mod
- Language.Futhark.Syntax: IncludeSpec :: SigExpBase f vn -> SrcLoc -> SpecBase f vn
+ Language.Futhark.Syntax: IncludeSpec :: ModTypeExpBase f vn -> SrcLoc -> SpecBase f vn
- Language.Futhark.Syntax: ModAscript :: ModExpBase f vn -> SigExpBase f vn -> f (Map VName VName) -> SrcLoc -> ModExpBase f vn
+ Language.Futhark.Syntax: ModAscript :: ModExpBase f vn -> ModTypeExpBase f vn -> f (Map VName VName) -> SrcLoc -> ModExpBase f vn
- Language.Futhark.Syntax: ModBind :: vn -> [ModParamBase f vn] -> Maybe (SigExpBase f vn, f (Map VName VName)) -> ModExpBase f vn -> Maybe DocComment -> SrcLoc -> ModBindBase f vn
+ Language.Futhark.Syntax: ModBind :: vn -> [ModParamBase f vn] -> Maybe (ModTypeExpBase f vn, f (Map VName VName)) -> ModExpBase f vn -> Maybe DocComment -> SrcLoc -> ModBindBase f vn
- Language.Futhark.Syntax: ModLambda :: ModParamBase f vn -> Maybe (SigExpBase f vn, f (Map VName VName)) -> ModExpBase f vn -> SrcLoc -> ModExpBase f vn
+ Language.Futhark.Syntax: ModLambda :: ModParamBase f vn -> Maybe (ModTypeExpBase f vn, f (Map VName VName)) -> ModExpBase f vn -> SrcLoc -> ModExpBase f vn
- Language.Futhark.Syntax: ModParam :: vn -> SigExpBase f vn -> f [VName] -> SrcLoc -> ModParamBase f vn
+ Language.Futhark.Syntax: ModParam :: vn -> ModTypeExpBase f vn -> f [VName] -> SrcLoc -> ModParamBase f vn
- Language.Futhark.Syntax: ModSpec :: vn -> SigExpBase f vn -> Maybe DocComment -> SrcLoc -> SpecBase f vn
+ Language.Futhark.Syntax: ModSpec :: vn -> ModTypeExpBase f vn -> Maybe DocComment -> SrcLoc -> SpecBase f vn
- Language.Futhark.Syntax: [modParamType] :: ModParamBase f vn -> SigExpBase f vn
+ Language.Futhark.Syntax: [modParamType] :: ModParamBase f vn -> ModTypeExpBase f vn
- Language.Futhark.TypeChecker.Modules: applyFunctor :: Loc -> FunSig -> MTy -> TypeM (MTy, Map VName VName, Map VName VName)
+ Language.Futhark.TypeChecker.Modules: applyFunctor :: Loc -> FunModType -> MTy -> TypeM (MTy, Map VName VName, Map VName VName)
- Language.Futhark.TypeChecker.Monad: ModFun :: FunSig -> Mod
+ Language.Futhark.TypeChecker.Monad: ModFun :: FunModType -> Mod
- Language.Futhark.TypeChecker.Types: substTypesAny :: Monoid as => (VName -> Maybe (Subst (RetTypeBase Size as))) -> TypeBase Size as -> TypeBase Size as
+ Language.Futhark.TypeChecker.Types: substTypesAny :: Monoid u => (VName -> Maybe (Subst (RetTypeBase Size u))) -> TypeBase Size u -> TypeBase Size u

Files

CHANGELOG.md view
@@ -5,6 +5,39 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.25.10]++### Added++* Faster non-commutative reductions in the GPU backends. Work by+  Anders Holst and Christian Påbøl Jacobsen.++### Fixed++* Interpreter crash for certain complicated size expressions involving+  internal bindings (#2053).++* Incorrect type checking of `let` binding with explicit size+  quantification, where size appears in type of body (#2048).++* GPU code generation for non-commutative non-segmented reductions+  with array operands (#2051).++* Histogram with non-vectorised reduction operators (#2056). (But it+  is probably not a good idea to write such programs.)++* Futhark's LSP server should work better with Eglot.++* Incorrect copy removal inside histograms could cause compiler error+  (#2058).++* CUDA backend now correctly queries for available shared memory,+  which affects performance (hopefully positively).++* `futhark literate` now switches to the directory containing the+  `.fut` file before executing its contents. This fixes accessing+  files through relative paths.+ ## [0.25.9]  ### Added
docs/c-api.rst view
@@ -141,9 +141,7 @@    cache was hit succesfully, but you can enable logging to see what    happens. -   The lifespan of ``fname`` must exceed the lifespan of the-   configuration object.  Pass ``NULL`` to disable caching (this is-   the default).+   Pass ``NULL`` to disable caching (this is the default).  Context -------
docs/language-reference.rst view
@@ -576,6 +576,9 @@   ``t``.  To pass a single array-typed parameter, enclose it in   parens. +* The bodies of ``let``, ``if``, and ``loop`` extend as far to the+  right as possible.+ * The following table describes the precedence and associativity of   infix operators in both expressions and type expressions.  All   operators in the same row have the same precedence.  The rows are@@ -836,10 +839,10 @@      Company any two values of numeric type for equality. -  ```symbol```+  ```qualname``` -    Use ``symbol``, which may be any non-operator function name, as an-    infix operator.+    Use ``qualname``, which may be any non-operator function name, as+    an infix operator.  ``x && y`` ..........@@ -1499,14 +1502,14 @@    mod_param: "(" `name` ":" `mod_type_exp` ")"    mod_type_bind: "module" "type" `name` "=" `mod_type_exp` -Futhark supports an ML-style higher-order module system.  *Modules*-can contain types, functions, and other modules and module types.-*Module types* are used to classify the contents of modules, and-*parametric modules* are used to abstract over modules (essentially-module-level functions).  In Standard ML, modules, module types and-parametric modules are called structs, signatures, and functors,-respectively.  Module names exist in the same name space as values,-but module types are their own name space.+Futhark supports an ML-style higher-order module system. *Modules* can+contain types, functions, and other modules and module types. *Module+types* are used to classify the contents of modules, and *parametric+modules* are used to abstract over modules (essentially module-level+functions). In Standard ML, modules, module types and parametric+modules are called *structs*, *signatures*, and *functors*,+respectively. Module names exist in the same name space as values, but+module types are their own name space.  Module bindings ~~~~~~~~~~~~~~~
futhark.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name:           futhark-version:        0.25.9+version:        0.25.10 synopsis:       An optimising compiler for a functional, array-oriented language.  description:    Futhark is a small programming language designed to be compiled to@@ -313,9 +313,6 @@       Futhark.Optimise.Fusion.TryFusion       Futhark.Optimise.GenRedOpt       Futhark.Optimise.HistAccs-      Futhark.Optimise.InPlaceLowering-      Futhark.Optimise.InPlaceLowering.LowerIntoStm-      Futhark.Optimise.InPlaceLowering.SubstituteIndices       Futhark.Optimise.InliningDeadFun       Futhark.Optimise.MemoryBlockMerging       Futhark.Optimise.MemoryBlockMerging.GreedyColoring
rts/c/backends/cuda.h view
@@ -289,6 +289,8 @@   size_t max_threshold;   size_t max_local_memory;   size_t max_bespoke;+  size_t max_registers;+  size_t max_cache;    size_t lockstep_width; @@ -429,6 +431,10 @@   int arch_set = 0, num_extra_opts;   struct futhark_context_config *cfg = ctx->cfg; +  char** macro_names;+  int64_t* macro_vals;+  int num_macros = gpu_macros(ctx, &macro_names, &macro_vals);+   // nvrtc cannot handle multiple -arch options.  Hence, if one of the   // extra_opts is -arch, we have to be careful not to do our usual   // automatic generation.@@ -441,7 +447,7 @@     }   } -  size_t i = 0, n_opts_alloc = 20 + num_extra_opts + cfg->num_tuning_params;+  size_t i = 0, n_opts_alloc = 20 + num_macros + num_extra_opts + cfg->num_tuning_params;   char **opts = (char**) malloc(n_opts_alloc * sizeof(char *));   if (!arch_set) {     opts[i++] = strdup("-arch");@@ -457,6 +463,17 @@   opts[i++] = msgprintf("-D%s=%d",                         "max_group_size",                         (int)ctx->max_group_size);+  opts[i++] = msgprintf("-D%s=%d",+                        "max_local_memory",+                        (int)ctx->max_local_memory);+  opts[i++] = msgprintf("-D%s=%d",+                        "max_registers",+                        (int)ctx->max_registers);++  for (int j = 0; j < num_macros; j++) {+    opts[i++] = msgprintf("-D%s=%zu", macro_names[j], macro_vals[j]);+  }+   for (int j = 0; j < cfg->num_tuning_params; j++) {     opts[i++] = msgprintf("-D%s=%zu", cfg->tuning_param_vars[j],                           cfg->tuning_params[j]);@@ -485,6 +502,9 @@   opts[i++] = msgprintf("-DTR_TILE_DIM=%d", TR_TILE_DIM);   opts[i++] = msgprintf("-DTR_ELEMS_PER_THREAD=%d", TR_ELEMS_PER_THREAD); +  free(macro_names);+  free(macro_vals);+   *n_opts = i;   *opts_out = opts; }@@ -798,12 +818,19 @@    free_list_init(&ctx->gpu_free_list); -  ctx->max_local_memory = device_query(ctx->dev, MAX_SHARED_MEMORY_PER_BLOCK);+  // MAX_SHARED_MEMORY_PER_BLOCK gives bogus numbers (48KiB); probably+  // for backwards compatibility.  Add _OPTIN and you seem to get the+  // right number.+  ctx->max_local_memory =+    device_query(ctx->dev, MAX_SHARED_MEMORY_PER_BLOCK_OPTIN) -+    device_query(ctx->dev, RESERVED_SHARED_MEMORY_PER_BLOCK);   ctx->max_group_size = device_query(ctx->dev, MAX_THREADS_PER_BLOCK);   ctx->max_grid_size = device_query(ctx->dev, MAX_GRID_DIM_X);   ctx->max_tile_size = sqrt(ctx->max_group_size);   ctx->max_threshold = 0;   ctx->max_bespoke = 0;+  ctx->max_registers = device_query(ctx->dev, MAX_REGISTERS_PER_BLOCK);+  ctx->max_cache = device_query(ctx->dev, L2_CACHE_SIZE);   ctx->lockstep_width = device_query(ctx->dev, WARP_SIZE);   CUDA_SUCCEED_FATAL(cuStreamCreate(&ctx->stream, CU_STREAM_DEFAULT));   cuda_size_setup(ctx);@@ -853,6 +880,10 @@     fprintf(ctx->log, "Creating kernel %s.\n", name);   }   CUDA_SUCCEED_FATAL(cuModuleGetFunction(kernel, ctx->module, name));+  // Unless the below is set, the kernel is limited to 48KiB of memory.+  CUDA_SUCCEED_FATAL(cuFuncSetAttribute(*kernel,+                                        cudaFuncAttributeMaxDynamicSharedMemorySize,+                                        ctx->max_local_memory)); }  static void gpu_free_kernel(struct futhark_context *ctx,
rts/c/backends/hip.h view
@@ -263,6 +263,8 @@   size_t max_threshold;   size_t max_local_memory;   size_t max_bespoke;+  size_t max_registers;+  size_t max_cache;    size_t lockstep_width; @@ -452,6 +454,10 @@   int arch_set = 0, num_extra_opts;   struct futhark_context_config *cfg = ctx->cfg; +  char** macro_names;+  int64_t* macro_vals;+  int num_macros = gpu_macros(ctx, &macro_names, &macro_vals);+   for (num_extra_opts = 0; extra_opts[num_extra_opts] != NULL; num_extra_opts++) {     if (strstr(extra_opts[num_extra_opts], "--gpu-architecture")         == extra_opts[num_extra_opts]) {@@ -459,7 +465,7 @@     }   } -  size_t i = 0, n_opts_alloc = 20 + num_extra_opts + cfg->num_tuning_params;+  size_t i = 0, n_opts_alloc = 20 + num_macros + num_extra_opts + cfg->num_tuning_params;   char **opts = (char**) malloc(n_opts_alloc * sizeof(char *));   if (!arch_set) {     hipDeviceProp_t props;@@ -473,6 +479,17 @@   opts[i++] = msgprintf("-D%s=%d",                         "max_group_size",                         (int)ctx->max_group_size);+  opts[i++] = msgprintf("-D%s=%d",+                        "max_local_memory",+                        (int)ctx->max_local_memory);+  opts[i++] = msgprintf("-D%s=%d",+                        "max_registers",+                        (int)ctx->max_registers);++  for (int j = 0; j < num_macros; j++) {+    opts[i++] = msgprintf("-D%s=%zu", macro_names[j], macro_vals[j]);+  }+   for (int j = 0; j < cfg->num_tuning_params; j++) {     opts[i++] = msgprintf("-D%s=%zu", cfg->tuning_param_vars[j],                           cfg->tuning_params[j]);@@ -488,6 +505,9 @@   opts[i++] = msgprintf("-DTR_TILE_DIM=%d", TR_TILE_DIM);   opts[i++] = msgprintf("-DTR_ELEMS_PER_THREAD=%d", TR_ELEMS_PER_THREAD); +  free(macro_names);+  free(macro_vals);+   *n_opts = i;   *opts_out = opts; }@@ -659,6 +679,8 @@   ctx->max_tile_size = sqrt(ctx->max_group_size);   ctx->max_threshold = 0;   ctx->max_bespoke = 0;+  ctx->max_registers = device_query(ctx->dev, hipDeviceAttributeMaxRegistersPerBlock);+  ctx->max_cache = device_query(ctx->dev, hipDeviceAttributeL2CacheSize);   // FIXME: in principle we should query hipDeviceAttributeWarpSize   // from the device, which will provide 64 on AMD GPUs.   // Unfortunately, we currently do nasty implicit intra-warp
rts/c/backends/opencl.h view
@@ -528,6 +528,8 @@   size_t max_tile_size;   size_t max_threshold;   size_t max_local_memory;+  size_t max_registers;+  size_t max_cache;    size_t lockstep_width; @@ -579,10 +581,18 @@     compile_opts_size += strlen(ctx->cfg->tuning_param_names[i]) + 20;   } +  char** macro_names;+  int64_t* macro_vals;+  int num_macros = gpu_macros(ctx, &macro_names, &macro_vals);+   for (int i = 0; extra_build_opts[i] != NULL; i++) {     compile_opts_size += strlen(extra_build_opts[i] + 1);   } +  for (int i = 0; i < num_macros; i++) {+    compile_opts_size += strlen(macro_names[i]) + 1 + 20;+  }+   char *compile_opts = (char*) malloc(compile_opts_size);    int w = snprintf(compile_opts, compile_opts_size,@@ -594,6 +604,16 @@                 "max_group_size",                 (int)ctx->max_group_size); +  w += snprintf(compile_opts+w, compile_opts_size-w,+                "-D%s=%d ",+                "max_local_memory",+                (int)ctx->max_local_memory);++  w += snprintf(compile_opts+w, compile_opts_size-w,+                "-D%s=%d ",+                "max_registers",+                (int)ctx->max_registers);+   for (int i = 0; i < ctx->cfg->num_tuning_params; i++) {     w += snprintf(compile_opts+w, compile_opts_size-w,                   "-D%s=%d ",@@ -606,6 +626,11 @@                   "%s ", extra_build_opts[i]);   } +  for (int i = 0; i < num_macros; i++) {+    w += snprintf(compile_opts+w, compile_opts_size-w,+                  "-D%s=%zu ", macro_names[i], macro_vals[i]);+  }+   w += snprintf(compile_opts+w, compile_opts_size-w,                 "-DTR_BLOCK_DIM=%d -DTR_TILE_DIM=%d -DTR_ELEMS_PER_THREAD=%d ",                 TR_BLOCK_DIM, TR_TILE_DIM, TR_ELEMS_PER_THREAD);@@ -616,6 +641,9 @@     w += snprintf(compile_opts+w, compile_opts_size-w, "-DEMULATE_F16 ");   } +  free(macro_names);+  free(macro_vals);+   return compile_opts; } @@ -784,6 +812,20 @@     }     ctx->cfg->default_tile_size = max_tile_size;   }+++  cl_ulong cache_size;+  OPENCL_SUCCEED_FATAL(clGetDeviceInfo(device_option.device, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE,+                                       sizeof(cache_size), &cache_size, NULL));++  if (cache_size == 0) {+    // Some code assumes nonzero cache.+    cache_size = 1024*1024;+  }++  ctx->max_cache = cache_size;++  ctx->max_registers = 1<<16; // I cannot find a way to query for this.    ctx->max_group_size = max_group_size;   ctx->max_tile_size = max_tile_size; // No limit.
rts/c/context.h view
@@ -130,8 +130,8 @@   ctx->profiling_paused = 0;   ctx->error = NULL;   ctx->log = stderr;+  set_tuning_params(ctx);   if (backend_context_setup(ctx) == 0) {-    set_tuning_params(ctx);     setup_program(ctx);     init_constants(ctx);     (void)futhark_context_clear_caches(ctx);
rts/c/gpu_prototypes.h view
@@ -5,6 +5,9 @@ #define TR_TILE_DIM (TR_BLOCK_DIM*2) #define TR_ELEMS_PER_THREAD 8 +// Must be defined by the user.+static int gpu_macros(struct futhark_context *ctx, char*** names, int64_t** values);+ struct builtin_kernels* init_builtin_kernels(struct futhark_context* ctx); void free_builtin_kernels(struct futhark_context* ctx, struct builtin_kernels* kernels); static int gpu_free_all(struct futhark_context *ctx);
rts/python/opencl.py view
@@ -102,6 +102,16 @@     return sizes  +def to_c_str_rep(x):+    if type(x) is bool or type(x) is np.bool_:+        if x:+            return "true"+        else:+            return "false"+    else:+        return str(x)++ def initialise_opencl_object(     self,     program_src="",@@ -119,6 +129,7 @@     required_types=[],     all_sizes={},     user_sizes={},+    constants=[], ):     if command_queue is None:         self.ctx = get_prefered_context(@@ -283,6 +294,10 @@                 v,             )             for (s, v) in self.sizes.items()+        ]++        build_options += [+            "-D{}={}".format(s, to_c_str_rep(f())) for (s, f) in constants         ]          if self.platform.name == "Oclgrind":
src/Futhark/Actions.hs view
@@ -166,7 +166,7 @@   Action     { actionName = "Compile imperative kernels",       actionDescription = "Translate program into imperative IL with kernels and write it on standard output.",-      actionProcedure = liftIO . putStrLn . prettyString . snd <=< ImpGenGPU.compileProgOpenCL+      actionProcedure = liftIO . putStrLn . prettyString . snd <=< ImpGenGPU.compileProgHIP     }  -- | Convert the program to CPU multicore ImpCode and print it to stdout.
src/Futhark/CLI/Defs.hs view
@@ -33,7 +33,7 @@     defsInDec (LocalDec d _) = defsInDec d     defsInDec (OpenDec me _) = defsInModExp me     defsInDec (ModDec mb) = defsInModExp $ modExp mb-    defsInDec SigDec {} = mempty+    defsInDec ModTypeDec {} = mempty     defsInDec ImportDec {} = mempty      defsInModExp ModVar {} = mempty
src/Futhark/CLI/Dev.hs view
@@ -35,7 +35,6 @@ import Futhark.Optimise.DoubleBuffer import Futhark.Optimise.Fusion import Futhark.Optimise.HistAccs-import Futhark.Optimise.InPlaceLowering import Futhark.Optimise.InliningDeadFun import Futhark.Optimise.MemoryBlockMerging qualified as MemoryBlockMerging import Futhark.Optimise.ReduceDeviceSyncs (reduceDeviceSyncs)@@ -330,23 +329,6 @@     long = [passLongOption pass]     pass = Seq.explicitAllocations -iplOption :: String -> FutharkOption-iplOption short =-  passOption (passDescription pass) (UntypedPass perform) short long-  where-    perform (GPU prog) config =-      GPU-        <$> runPipeline (onePass inPlaceLoweringGPU) 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- cseOption :: String -> FutharkOption cseOption short =   passOption (passDescription pass) (UntypedPass perform) short long@@ -616,7 +598,6 @@     kernelsPassOption reduceDeviceSyncs [],     typedPassOption soacsProg GPU extractKernels [],     typedPassOption soacsProg MC extractMulticore [],-    iplOption [],     allocateOption "a",     kernelsMemPassOption doubleBufferGPU [],     mcMemPassOption doubleBufferMC [],
src/Futhark/CLI/LSP.hs view
@@ -39,7 +39,7 @@ syncOptions :: TextDocumentSyncOptions syncOptions =   TextDocumentSyncOptions-    { _openClose = Just True,+    { _openClose = Just False,       _change = Just TextDocumentSyncKind_Incremental,       _willSave = Just False,       _willSaveWaitUntil = Just False,
src/Futhark/CLI/Literate.hs view
@@ -45,7 +45,9 @@   ( copyFile,     createDirectoryIfMissing,     doesFileExist,+    getCurrentDirectory,     removePathForcibly,+    setCurrentDirectory,   ) import System.Environment (getExecutablePath) import System.Exit@@ -675,18 +677,15 @@  data Env = Env   { envImgDir :: FilePath,-    -- | Image dir relative to program.-    envRelImgDir :: FilePath,     envOpts :: Options,     envServer :: ScriptServer,     envHash :: T.Text   } -newFileWorker :: Env -> (Maybe FilePath, FilePath) -> (FilePath -> ScriptM ()) -> ScriptM (FilePath, FilePath)-newFileWorker env (fname_desired, template) m = do+newFile :: Env -> (Maybe FilePath, FilePath) -> (FilePath -> ScriptM ()) -> ScriptM FilePath+newFile env (fname_desired, template) m = do   let fname_base = fromMaybe (T.unpack (envHash env) <> "-" <> template) fname_desired       fname = envImgDir env </> fname_base-      fname_rel = envRelImgDir env </> fname_base   exists <- liftIO $ doesFileExist fname   liftIO $ createDirectoryIfMissing True $ envImgDir env   when (exists && scriptVerbose (envOpts env) > 0) $@@ -698,14 +697,11 @@         "Generating new file: " <> T.pack fname     m fname   modify $ \s -> s {stateFiles = S.insert fname $ stateFiles s}-  pure (fname, fname_rel)--newFile :: Env -> (Maybe FilePath, FilePath) -> (FilePath -> ScriptM ()) -> ScriptM FilePath-newFile env f m = snd <$> newFileWorker env f m+  pure fname  newFileContents :: Env -> (Maybe FilePath, FilePath) -> (FilePath -> ScriptM ()) -> ScriptM T.Text newFileContents env f m =-  liftIO . T.readFile . fst =<< newFileWorker env f m+  liftIO . T.readFile =<< newFile env f m  processDirective :: Env -> Directive -> ScriptM T.Text processDirective env (DirectiveBrief d) =@@ -1165,8 +1161,8 @@           system futhark ["hash", prog] mempty        let mdfile = fromMaybe (prog `replaceExtension` "md") $ scriptOutput opts-          imgdir_rel = dropExtension (takeFileName mdfile) <> "-img"-          imgdir = takeDirectory mdfile </> imgdir_rel+          prog_dir = takeDirectory prog+          imgdir = dropExtension (takeFileName mdfile) <> "-img"           run_options = scriptExtraOptions opts           onLine "call" l = T.putStrLn l           onLine _ _ = pure ()@@ -1178,16 +1174,22 @@                     else const . const $ pure ()               } +      orig_dir <- getCurrentDirectory+       withScriptServer cfg $ \server -> do         let env =               Env                 { envServer = server,                   envOpts = opts,                   envHash = proghash,-                  envImgDir = imgdir,-                  envRelImgDir = imgdir_rel+                  envImgDir = imgdir                 }++        when (scriptVerbose opts > 0) $ do+          T.hPutStrLn stderr $ "Executing from " <> T.pack prog_dir+        setCurrentDirectory prog_dir+         (failure, md) <- processScript env script-        T.writeFile mdfile md+        T.writeFile (orig_dir </> mdfile) md         when (failure == Failure) exitFailure     _ -> Nothing
src/Futhark/CLI/Misc.hs view
@@ -107,7 +107,9 @@       [ "You're welcome!",         "Tell all your friends about Futhark!",         "Likewise!",-        "And thank you in return for trying the language!"+        "And thank you in return for trying the language!",+        "It's our pleasure!",+        "Have fun with Futhark!"       ]  -- | @futhark tokens@
src/Futhark/CodeGen/Backends/CCUDA.hs view
@@ -29,13 +29,15 @@  mkBoilerplate ::   T.Text ->+  [(Name, KernelConstExp)] ->   M.Map Name KernelSafety ->   [PrimType] ->   [FailureMsg] ->   GC.CompilerM OpenCL () ()-mkBoilerplate cuda_program kernels types failures = do+mkBoilerplate cuda_program macros kernels types failures = do   generateGPUBoilerplate     cuda_program+    macros     backendsCudaH     (M.keys kernels)     types@@ -67,7 +69,7 @@                           fprintf(stderr, "%s: %s\n", optarg, strerror(errno));                           exit(1);                         }-                        exit(1);}|]+                        exit(0);}|]            },          Option            { optionLongName = "load-cuda",@@ -113,7 +115,7 @@ compileProg :: (MonadFreshNames m) => T.Text -> Prog GPUMem -> m (ImpGen.Warnings, GC.CParts) compileProg version prog = do   ( ws,-    Program cuda_code cuda_prelude kernels types params failures prog'+    Program cuda_code cuda_prelude macros kernels types params failures prog'     ) <-     ImpGen.compileProg prog   (ws,)@@ -122,7 +124,7 @@       version       params       operations-      (mkBoilerplate (cuda_prelude <> cuda_code) kernels types failures)+      (mkBoilerplate (cuda_prelude <> cuda_code) macros kernels types failures)       cuda_includes       (Space "device", [Space "device", DefaultSpace])       cliOptions
src/Futhark/CodeGen/Backends/COpenCL.hs view
@@ -77,13 +77,15 @@  mkBoilerplate ::   T.Text ->+  [(Name, KernelConstExp)] ->   M.Map Name KernelSafety ->   [PrimType] ->   [FailureMsg] ->   GC.CompilerM OpenCL () ()-mkBoilerplate opencl_program kernels types failures = do+mkBoilerplate opencl_program macros kernels types failures = do   generateGPUBoilerplate     opencl_program+    macros     backendsOpenclH     (M.keys kernels)     types@@ -187,7 +189,7 @@ compileProg :: (MonadFreshNames m) => T.Text -> Prog GPUMem -> m (ImpGen.Warnings, GC.CParts) compileProg version prog = do   ( ws,-    Program opencl_code opencl_prelude kernels types params failures prog'+    Program opencl_code opencl_prelude macros kernels types params failures prog'     ) <-     ImpGen.compileProg prog   (ws,)@@ -196,7 +198,7 @@       version       params       operations-      (mkBoilerplate (opencl_prelude <> opencl_code) kernels types failures)+      (mkBoilerplate (opencl_prelude <> opencl_code) macros kernels types failures)       opencl_includes       (Space "device", [Space "device", DefaultSpace])       cliOptions
src/Futhark/CodeGen/Backends/GPU.hs view
@@ -12,12 +12,13 @@ where  import Control.Monad+import Control.Monad.Identity import Data.Bifunctor (bimap) import Data.Map qualified as M import Data.Text qualified as T import Futhark.CodeGen.Backends.GenericC qualified as GC import Futhark.CodeGen.Backends.GenericC.Options-import Futhark.CodeGen.Backends.GenericC.Pretty (idText)+import Futhark.CodeGen.Backends.GenericC.Pretty (expText, idText) import Futhark.CodeGen.Backends.SimpleRep (primStorageType, toStorage) import Futhark.CodeGen.ImpCode.OpenCL import Futhark.CodeGen.RTS.C (gpuH, gpuPrototypesH)@@ -66,9 +67,12 @@           ([C.cinit|&ctx->global_failure_args|], [C.cinit|sizeof(ctx->global_failure_args)|])         ] +getParamByKey :: Name -> C.Exp+getParamByKey key = [C.cexp|*ctx->tuning_params.$id:key|]+ kernelConstToExp :: KernelConst -> C.Exp-kernelConstToExp (SizeConst key) =-  [C.cexp|*ctx->tuning_params.$id:key|]+kernelConstToExp (SizeConst key _) =+  getParamByKey key kernelConstToExp (SizeMaxConst size_class) =   [C.cexp|ctx->$id:field|]   where@@ -133,13 +137,11 @@         )  callKernel :: GC.OpCompiler OpenCL ()-callKernel (GetSize v key) = do-  let e = kernelConstToExp $ SizeConst key-  GC.stm [C.cstm|$id:v = $exp:e;|]+callKernel (GetSize v key) =+  GC.stm [C.cstm|$id:v = $exp:(getParamByKey key);|] callKernel (CmpSizeLe v key x) = do-  let e = kernelConstToExp $ SizeConst key   x' <- GC.compileExp x-  GC.stm [C.cstm|$id:v = $exp:e <= $exp:x';|]+  GC.stm [C.cstm|$id:v = $exp:(getParamByKey key) <= $exp:x';|]   -- Output size information if logging is enabled.  The autotuner   -- depends on the format of this output, so use caution if changing   -- it.@@ -381,16 +383,20 @@                   return strdup("Unknown error.  This is a compiler bug.");                 }|] +compileConstExp :: KernelConstExp -> C.Exp+compileConstExp e = runIdentity $ GC.compilePrimExp (pure . kernelConstToExp) e+ -- | Called after most code has been generated to generate the bulk of -- the boilerplate. generateGPUBoilerplate ::   T.Text ->+  [(Name, KernelConstExp)] ->   T.Text ->   [Name] ->   [PrimType] ->   [FailureMsg] ->   GC.CompilerM OpenCL () ()-generateGPUBoilerplate gpu_program backendH kernels types failures = do+generateGPUBoilerplate gpu_program macros backendH kernels types failures = do   createKernels kernels   let gpu_program_fragments =         -- Some C compilers limit the size of literal strings, so@@ -402,6 +408,13 @@         | FloatType Float64 `elem` types = [C.cexp|1|]         | otherwise = [C.cexp|0|]       max_failure_args = foldl max 0 $ map (errorMsgNumArgs . failureError) failures++      setMacro i (name, e) =+        [C.cstm|{names[$int:i] = $string:(nameToString name);+                 values[$int:i] = $esc:e';}|]+        where+          e' = T.unpack $ expText $ compileConstExp e+   mapM_     GC.earlyDecl     [C.cunit|static const int max_failure_args = $int:max_failure_args;@@ -410,6 +423,17 @@              $esc:(T.unpack gpuPrototypesH)              $esc:(T.unpack backendH)              $esc:(T.unpack gpuH)+             static int gpu_macros(struct futhark_context *ctx, char*** names_out, typename int64_t** values_out) {+               int num_macros = $int:(length macros);+               char** names = malloc(num_macros * sizeof(char*));+               typename int64_t* values = malloc(num_macros * sizeof(int64_t));++               $stms:(zipWith setMacro [(0::Int)..] macros)++               *names_out = names;+               *values_out = values;+               return num_macros;+             }             |]   GC.earlyDecl $ failureMsgFunction failures 
src/Futhark/CodeGen/Backends/GenericC/Code.hs view
@@ -4,7 +4,6 @@ module Futhark.CodeGen.Backends.GenericC.Code   ( compilePrimExp,     compileExp,-    compileExpToName,     compileCode,     compileDest,     compileArg,@@ -44,15 +43,6 @@       onPart (ErrorVal (FloatType Float64) x) = ("%f",) <$> compileExp x   (formatstrs, formatargs) <- mapAndUnzipM onPart parts   pure (mconcat formatstrs, formatargs)--compileExpToName :: String -> PrimType -> Exp -> CompilerM op s VName-compileExpToName _ _ (LeafExp v _) =-  pure v-compileExpToName desc t e = do-  desc' <- newVName desc-  e' <- compileExp e-  decl [C.cdecl|$ty:(primTypeToCType t) $id:desc' = $e';|]-  pure desc'  compileExp :: Exp -> CompilerM op s C.Exp compileExp = compilePrimExp $ \v -> pure [C.cexp|$id:v|]
src/Futhark/CodeGen/Backends/GenericPython.hs view
@@ -14,7 +14,6 @@     compileCode,     compilePrimValue,     compilePrimType,-    compilePrimTypeExt,     compilePrimToNp,     compilePrimToExtNp,     fromStorage,@@ -1026,24 +1025,6 @@     FloatType Float64 -> "ct.c_double"     Imp.Bool -> "ct.c_bool"     Unit -> "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.Unsigned) -> "ct.c_uint8"-    (IntType Int16, Imp.Unsigned) -> "ct.c_uint16"-    (IntType Int32, Imp.Unsigned) -> "ct.c_uint32"-    (IntType Int64, Imp.Unsigned) -> "ct.c_uint64"-    (IntType Int8, _) -> "ct.c_int8"-    (IntType Int16, _) -> "ct.c_int16"-    (IntType Int32, _) -> "ct.c_int32"-    (IntType Int64, _) -> "ct.c_int64"-    (FloatType Float16, _) -> "ct.c_uint16"-    (FloatType Float32, _) -> "ct.c_float"-    (FloatType Float64, _) -> "ct.c_double"-    (Imp.Bool, _) -> "ct.c_bool"-    (Unit, _) -> "ct.c_byte"  -- | The Numpy type corresponding to a 'PrimType'. compilePrimToNp :: Imp.PrimType -> String
src/Futhark/CodeGen/Backends/HIP.hs view
@@ -29,13 +29,15 @@  mkBoilerplate ::   T.Text ->+  [(Name, KernelConstExp)] ->   M.Map Name KernelSafety ->   [PrimType] ->   [FailureMsg] ->   GC.CompilerM OpenCL () ()-mkBoilerplate hip_program kernels types failures = do+mkBoilerplate hip_program macros kernels types failures = do   generateGPUBoilerplate     hip_program+    macros     backendsHipH     (M.keys kernels)     types@@ -95,7 +97,7 @@ compileProg :: (MonadFreshNames m) => T.Text -> Prog GPUMem -> m (ImpGen.Warnings, GC.CParts) compileProg version prog = do   ( ws,-    Program hip_code hip_prelude kernels types params failures prog'+    Program hip_code hip_prelude macros kernels types params failures prog'     ) <-     ImpGen.compileProg prog   (ws,)@@ -104,7 +106,7 @@       version       params       operations-      (mkBoilerplate (hip_prelude <> hip_code) kernels types failures)+      (mkBoilerplate (hip_prelude <> hip_code) macros kernels types failures)       hip_includes       (Space "device", [Space "device", DefaultSpace])       cliOptions
src/Futhark/CodeGen/Backends/PyOpenCL.hs view
@@ -33,6 +33,7 @@     Imp.Program       opencl_code       opencl_prelude+      macros       kernels       types       sizes@@ -89,7 +90,7 @@             "default_threshold=default_threshold",             "sizes=sizes"           ]-          [Escape $ openClInit types assign sizes failures]+          [Escape $ openClInit macros types assign sizes failures]       options =         [ Option             { optionLongName = "platform",@@ -202,9 +203,12 @@ asLong :: PyExp -> PyExp asLong x = simpleCall "np.int64" [x] +getParamByKey :: Name -> PyExp+getParamByKey key = Index (Var "self.sizes") (IdxExp $ String $ prettyText key)+ kernelConstToExp :: Imp.KernelConst -> PyExp-kernelConstToExp (Imp.SizeConst key) =-  Index (Var "self.sizes") (IdxExp $ String $ prettyText key)+kernelConstToExp (Imp.SizeConst key _) =+  getParamByKey key kernelConstToExp (Imp.SizeMaxConst size_class) =   Var $ "self.max_" <> prettyString size_class @@ -215,13 +219,11 @@ callKernel :: OpCompiler Imp.OpenCL () callKernel (Imp.GetSize v key) = do   v' <- compileVar v-  stm $ Assign v' $ kernelConstToExp $ Imp.SizeConst key+  stm $ Assign v' $ getParamByKey key callKernel (Imp.CmpSizeLe v key x) = do   v' <- compileVar v   x' <- compileExp x-  stm $-    Assign v' $-      BinOp "<=" (kernelConstToExp (Imp.SizeConst key)) x'+  stm $ Assign v' $ BinOp "<=" (getParamByKey key) x' callKernel (Imp.GetSizeMax v size_class) = do   v' <- compileVar v   stm $ Assign v' $ kernelConstToExp $ Imp.SizeMaxConst size_class
src/Futhark/CodeGen/Backends/PyOpenCL/Boilerplate.hs view
@@ -15,6 +15,8 @@   ( ErrorMsg (..),     ErrorMsgPart (..),     FailureMsg (..),+    KernelConst (..),+    KernelConstExp,     ParamMap,     PrimType (..),     errorMsgArgTypes,@@ -28,15 +30,28 @@ errorMsgNumArgs :: ErrorMsg a -> Int errorMsgNumArgs = length . errorMsgArgTypes +getParamByKey :: Name -> PyExp+getParamByKey key = Index (Var "self.sizes") (IdxExp $ String $ prettyText key)++kernelConstToExp :: KernelConst -> PyExp+kernelConstToExp (SizeConst key _) =+  getParamByKey key+kernelConstToExp (SizeMaxConst size_class) =+  Var $ "self.max_" <> prettyString size_class++compileConstExp :: KernelConstExp -> PyExp+compileConstExp e = runIdentity $ Py.compilePrimExp (pure . kernelConstToExp) e+ -- | Python code (as a string) that calls the -- @initiatialize_opencl_object@ procedure.  Should be put in the -- class constructor.-openClInit :: [PrimType] -> String -> ParamMap -> [FailureMsg] -> T.Text-openClInit types assign sizes failures =+openClInit :: [(Name, KernelConstExp)] -> [PrimType] -> String -> ParamMap -> [FailureMsg] -> T.Text+openClInit constants types assign sizes failures =   [text| size_heuristics=$size_heuristics self.global_failure_args_max = $max_num_args self.failure_msgs=$failure_msgs+constants = $constants' program = initialise_opencl_object(self,                                    program_src=fut_opencl_src,                                    build_options=build_options,@@ -52,7 +67,8 @@                                    size_heuristics=size_heuristics,                                    required_types=$types',                                    user_sizes=sizes,-                                   all_sizes=$sizes')+                                   all_sizes=$sizes',+                                   constants=constants) $assign' |]   where@@ -62,6 +78,12 @@     sizes' = prettyText $ sizeClassesToPython sizes     max_num_args = prettyText $ foldl max 0 $ map (errorMsgNumArgs . failureError) failures     failure_msgs = prettyText $ List $ map formatFailure failures+    onConstant (name, e) =+      Tuple+        [ String (nameToText name),+          Lambda "" (compileConstExp e)+        ]+    constants' = prettyText $ List $ map onConstant constants  formatFailure :: FailureMsg -> PyExp formatFailure (FailureMsg (ErrorMsg parts) backtrace) =
src/Futhark/CodeGen/ImpCode/GPU.hs view
@@ -34,7 +34,7 @@  -- | A run-time constant related to kernels. data KernelConst-  = SizeConst Name+  = SizeConst Name SizeClass   | SizeMaxConst SizeClass   deriving (Eq, Ord, Show) @@ -62,15 +62,14 @@     -- | 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+    -- | 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,     -- | If true, multi-versioning branches will consider this kernel-    -- when considering the local memory requirements.  Set this to+    -- when considering the local memory requirements. Set this to     -- false for kernels that do their own checking.     kernelCheckLocalMemory :: Bool   }@@ -86,11 +85,13 @@   deriving (Eq, Ord, Show)  instance Pretty KernelConst where-  pretty (SizeConst key) = "get_size" <> parens (pretty key)-  pretty (SizeMaxConst size_class) = "get_max_size" <> parens (pretty size_class)+  pretty (SizeConst key size_class) =+    "get_size" <> parens (commasep [pretty key, pretty size_class])+  pretty (SizeMaxConst size_class) =+    "get_max_size" <> parens (pretty size_class)  instance FreeIn KernelConst where-  freeIn' (SizeConst _) = mempty+  freeIn' SizeConst {} = mempty   freeIn' (SizeMaxConst _) = mempty  instance Pretty KernelUse where
src/Futhark/CodeGen/ImpCode/OpenCL.hs view
@@ -18,6 +18,7 @@     FailureMsg (..),     GroupDim,     KernelConst (..),+    KernelConstExp,     module Futhark.CodeGen.ImpCode,     module Futhark.IR.GPU.Sizes,   )@@ -26,7 +27,7 @@ import Data.Map qualified as M import Data.Text qualified as T import Futhark.CodeGen.ImpCode-import Futhark.CodeGen.ImpCode.GPU (GroupDim, KernelConst (..))+import Futhark.CodeGen.ImpCode.GPU (GroupDim, KernelConst (..), KernelConstExp) import Futhark.IR.GPU.Sizes import Futhark.Util.Pretty @@ -35,6 +36,9 @@   { openClProgram :: T.Text,     -- | Must be prepended to the program.     openClPrelude :: T.Text,+    -- | Definitions to be passed as macro definitions to the kernel+    -- compiler.+    openClMacroDefs :: [(Name, KernelConstExp)],     openClKernelNames :: M.Map KernelName KernelSafety,     -- | So we can detect whether the device is capable.     openClUsedTypes :: [PrimType],
src/Futhark/CodeGen/ImpGen.hs view
@@ -38,7 +38,6 @@     hasFunction,     collect,     collect',-    comment,     VarEntry (..),     ArrayEntry (..), @@ -76,6 +75,7 @@     caseMatch,      -- * Constructing code.+    newVName,     dLParams,     dFParams,     addLoopVar,@@ -391,13 +391,6 @@   modify $ \s -> s {stateCode = prev_code}   pure (x, new_code) --- | Execute a code generation action, wrapping the generated code--- within a 'Imp.Comment' with the given description.-comment :: T.Text -> ImpM rep r op () -> ImpM rep r op ()-comment desc m = do-  code <- collect m-  emit $ Imp.Comment desc code- -- | Emit some generated imperative code. emit :: Imp.Code op -> ImpM rep r op () emit code = modify $ \s -> s {stateCode = stateCode s <> code}@@ -870,7 +863,7 @@   copyDWIM (patElemName pe) [] se []   case op of     OpaqueNil -> pure ()-    OpaqueTrace s -> comment ("Trace: " <> s) $ do+    OpaqueTrace s -> sComment ("Trace: " <> s) $ do       se_t <- subExpType se       case se_t of         Prim t -> tracePrim s t se@@ -937,7 +930,7 @@       dPrimV "x" . TPrimExp $         BinOpExp (Add it OverflowUndef) e' $           BinOpExp (Mul it OverflowUndef) i' s'-    copyDWIM (patElemName pe) [DimFix i] (Var (tvVar x)) []+    copyDWIMFix (patElemName pe) [i] (Var (tvVar x)) [] defCompileBasicOp (Pat [pe]) (Manifest _ src) =   copyDWIM (patElemName pe) [] (Var src) [] defCompileBasicOp (Pat [pe]) (Concat i (x :| ys) _) = do@@ -967,7 +960,7 @@       copy t dest_mem static_src   | otherwise =       forM_ (zip [0 ..] es) $ \(i, e) ->-        copyDWIM (patElemName pe) [DimFix $ fromInteger i] e []+        copyDWIMFix (patElemName pe) [fromInteger i] e []   where     isLiteral (Constant v) = Just v     isLiteral _ = Nothing@@ -1665,6 +1658,8 @@   body' <- collect body   emit $ Imp.While cond body' +-- | Execute a code generation action, wrapping the generated code+-- within a 'Imp.Comment' with the given description. sComment :: T.Text -> ImpM rep r op () -> ImpM rep r op () sComment s code = do   code' <- collect code
src/Futhark/CodeGen/ImpGen/GPU.hs view
@@ -27,7 +27,7 @@ import Futhark.Error import Futhark.IR.GPUMem import Futhark.MonadFreshNames-import Futhark.Util.IntegralExp (divUp, rem)+import Futhark.Util.IntegralExp (divUp, nextMul) import Prelude hiding (quot, rem)  callKernelOperations :: Operations GPUMem HostEnv Imp.HostOp@@ -134,7 +134,7 @@   sKernelThread "gpuseq" tid (defKernelAttrs one one) $     compileStms (freeIn res) stms $       forM_ (zip pes res) $ \(pe, SubExpRes _ se) ->-        copyDWIM (patElemName pe) [DimFix 0] se []+        copyDWIMFix (patElemName pe) [0] se [] opCompiler pat e =   compilerBugS $     "opCompiler: Invalid pattern\n  "@@ -170,14 +170,13 @@ -- 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.HostCode -> CallKernelGen (Maybe (Imp.TExp Bool))-checkLocalMemoryReqs code = do-  scope <- askScope+checkLocalMemoryReqs :: (VName -> Bool) -> Imp.HostCode -> CallKernelGen (Maybe (Imp.TExp Bool))+checkLocalMemoryReqs in_scope code = do   let alloc_sizes = map (sum . map alignedSize . localAllocSizes . Imp.kernelBody) $ getGPU code    -- If any of the sizes involve a variable that is not known at this   -- point, then we cannot check the requirements.-  if any (`M.notMember` scope) (namesToList $ freeIn alloc_sizes)+  if not $ all in_scope $ namesToList $ freeIn alloc_sizes     then pure Nothing     else do       local_memory_capacity :: TV Int32 <- dPrim "local_memory_capacity" int32@@ -200,7 +199,7 @@     -- These allocations will actually be padded to an 8-byte aligned     -- size, so we should take that into account when checking whether     -- they fit.-    alignedSize x = x + ((8 - (x `rem` 8)) `rem` 8)+    alignedSize x = nextMul x 8  withAcc ::   Pat LetDecMem ->@@ -250,9 +249,10 @@ -- always be safe (and what would we do if none of the branches would -- work?). expCompiler dest (Match cond (first_case : cases) defbranch sort@(MatchDec _ MatchEquiv)) = do+  scope <- askScope   tcode <- collect $ compileBody dest $ caseBody first_case   fcode <- collect $ expCompiler dest $ Match cond cases defbranch sort-  check <- checkLocalMemoryReqs tcode+  check <- checkLocalMemoryReqs (`M.member` scope) tcode   let matches = caseMatch cond (casePat first_case)   emit $ case check of     Nothing -> fcode
src/Futhark/CodeGen/ImpGen/GPU/Base.hs view
@@ -29,6 +29,9 @@     fenceForArrays,     updateAcc,     genZeroes,+    isPrimParam,+    kernelConstToExp,+    getChunkSize,      -- * Host-level bulk operations     sReplicate,@@ -52,7 +55,6 @@ import Futhark.Error import Futhark.IR.GPUMem import Futhark.IR.Mem.LMAD qualified as LMAD-import Futhark.MonadFreshNames import Futhark.Transform.Rename import Futhark.Util (dropLast, nubOrd, splitFromEnd) import Futhark.Util.IntegralExp (divUp, quot, rem)@@ -257,6 +259,44 @@         . entryArrayLoc         =<< lookupArray arr +isPrimParam :: (Typed p) => Param p -> Bool+isPrimParam = primType . paramType++kernelConstToExp :: Imp.KernelConstExp -> CallKernelGen Imp.Exp+kernelConstToExp = traverse f+  where+    f (Imp.SizeMaxConst c) = do+      v <- dPrim (prettyString c) int64+      sOp $ Imp.GetSizeMax (tvVar v) c+      pure $ tvVar v+    f (Imp.SizeConst k c) = do+      v <- dPrim (nameToString k) int64+      sOp $ Imp.GetSize (tvVar v) k c+      pure $ tvVar v++-- | Given available register and a list of parameter types, compute+-- the largest available chunk size given the parameters for which we+-- want chunking and the available resources. Used in+-- 'SegScan.SinglePass.compileSegScan', and 'SegRed.compileSegRed'+-- (with primitive non-commutative operators only).+getChunkSize :: [Type] -> Imp.KernelConstExp+getChunkSize types = do+  let max_group_size = Imp.SizeMaxConst SizeGroup+      max_group_mem = Imp.SizeMaxConst SizeLocalMemory+      max_group_reg = Imp.SizeMaxConst SizeRegisters+      k_mem = le64 max_group_mem `quot` le64 max_group_size+      k_reg = le64 max_group_reg `quot` le64 max_group_size+      types' = map elemType $ filter primType types+      sizes = map primByteSize types'++      sum_sizes = sum sizes+      sum_sizes' = sum (map (sMax64 4 . primByteSize) types') `quot` 4+      max_size = maximum sizes++      mem_constraint = max k_mem sum_sizes `quot` max_size+      reg_constraint = (k_reg - 1 - sum_sizes') `quot` (2 * sum_sizes')+  untyped $ sMax64 1 $ sMin64 mem_constraint reg_constraint+ inBlockScan ::   KernelConstants ->   Maybe (Imp.TExp Int32 -> Imp.TExp Int32 -> Imp.TExp Bool) ->@@ -275,7 +315,7 @@         splitAt (length actual_params `div` 2) actual_params       y_to_x =         forM_ (zip x_params y_params) $ \(x, y) ->-          when (primType (paramType x)) $+          when (isPrimParam x) $             copyDWIM (paramName x) [] (Var (paramName y)) []    -- Set initial y values@@ -342,23 +382,21 @@     array_scan = not $ all primType $ lambdaReturnType scan_lam      readInitial p arr-      | primType $ paramType p =-          copyDWIM (paramName p) [] (Var arr) [DimFix ltid]+      | isPrimParam p =+          copyDWIMFix (paramName p) [] (Var arr) [ltid]       | otherwise =-          copyDWIM (paramName p) [] (Var arr) [DimFix gtid]+          copyDWIMFix (paramName p) [] (Var arr) [gtid]      readParam behind p arr-      | primType $ paramType p =-          copyDWIM (paramName p) [] (Var arr) [DimFix $ ltid - behind]+      | isPrimParam p =+          copyDWIMFix (paramName p) [] (Var arr) [ltid - behind]       | otherwise =-          copyDWIM (paramName p) [] (Var arr) [DimFix $ gtid - behind + arrs_full_size]+          copyDWIMFix (paramName p) [] (Var arr) [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) []+    writeResult x y arr = do+      when (isPrimParam x) $+        copyDWIMFix arr [ltid] (Var $ paramName x) []+      copyDWIM (paramName y) [] (Var $ paramName x) []  groupScan ::   Maybe (Imp.TExp Int32 -> Imp.TExp Int32 -> Imp.TExp Bool) ->@@ -421,16 +459,16 @@       group_offset = sExt64 (kernelGroupId constants) * kernelGroupSize constants        writeBlockResult p arr-        | primType $ paramType p =-            copyDWIM arr [DimFix $ sExt64 block_id] (Var $ paramName p) []+        | isPrimParam p =+            copyDWIMFix arr [sExt64 block_id] (Var $ paramName p) []         | otherwise =-            copyDWIM arr [DimFix $ group_offset + sExt64 block_id] (Var $ paramName p) []+            copyDWIMFix arr [group_offset + sExt64 block_id] (Var $ paramName p) []        readPrevBlockResult p arr-        | primType $ paramType p =-            copyDWIM (paramName p) [] (Var arr) [DimFix $ sExt64 block_id - 1]+        | isPrimParam p =+            copyDWIMFix (paramName p) [] (Var arr) [sExt64 block_id - 1]         | otherwise =-            copyDWIM (paramName p) [] (Var arr) [DimFix $ group_offset + sExt64 block_id - 1]+            copyDWIMFix (paramName p) [] (Var arr) [group_offset + sExt64 block_id - 1]    doInBlockScan seg_flag ltid_in_bounds lam   barrier@@ -440,8 +478,8 @@     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) []+          unless (isPrimParam x) $+            copyDWIMFix arr [arrs_full_size + group_offset + sExt64 block_size + ltid] (Var $ paramName x) []      barrier @@ -457,7 +495,7 @@         flag_true <- seg_flag         Just $ \from to ->           flag_true (from * block_size + block_size - 1) (to * block_size + block_size - 1)-  comment+  sComment     "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 @@ -467,12 +505,12 @@     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+          unless (isPrimParam x) $+            copyDWIMFix               arr-              [DimFix $ arrs_full_size + group_offset + ltid]+              [arrs_full_size + group_offset + ltid]               (Var arr)-              [DimFix $ arrs_full_size + group_offset + sExt64 block_size + ltid]+              [arrs_full_size + group_offset + sExt64 block_size + ltid]      barrier @@ -498,8 +536,8 @@        write_final_result =         forM_ (zip x_params arrs) $ \(p, arr) ->-          when (primType $ paramType p) $-            copyDWIM arr [DimFix ltid] (Var $ paramName p) []+          when (isPrimParam p) $+            copyDWIMFix arr [ltid] (Var $ paramName p) []    sComment "carry-in for every block except the first" $     localOps threadOperations $ do@@ -512,9 +550,9 @@   sComment "restore correct values for first block" $     sWhen (is_first_block .&&. ltid_in_bounds) $       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]+        if isPrimParam y+          then copyDWIMFix arr [ltid] (Var $ paramName y) []+          else copyDWIMFix (paramName x) [] (Var arr) [arrs_full_size + group_offset + ltid]    barrier @@ -550,17 +588,13 @@         let i = local_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 =-            pure ()+      writeReduceOpResult param arr =+        when (isPrimParam param) $+          copyDWIMFix arr [sExt64 local_tid] (Var $ paramName param) [] -      writeArrayOpResult param arr-        | Prim _ <- paramType param =-            pure ()-        | otherwise =-            copyDWIMFix arr [0] (Var $ paramName param) []+      writeArrayOpResult param arr =+        unless (isPrimParam param) $+          copyDWIMFix arr [sExt64 local_tid] (Var $ paramName param) []    let (reduce_acc_params, reduce_arr_params) =         splitAt (length arrs) $ lambdaParams lam@@ -570,17 +604,17 @@    offset <-- (0 :: Imp.TExp Int32) -  comment "participating threads read initial accumulator" $+  sComment "participating threads read initial accumulator" $     localOps threadOperations . sWhen (local_tid .<. w) $       zipWithM_ readReduceArgument reduce_acc_params arrs    let do_reduce = localOps threadOperations $ do-        comment "read array element" $+        sComment "read array element" $           zipWithM_ readReduceArgument reduce_arr_params arrs-        comment "apply reduction operation" $+        sComment "apply reduction operation" $           compileBody' reduce_acc_params $             lambdaBody lam-        comment "write result of operation" $+        sComment "write result of operation" $           zipWithM_ writeReduceOpResult reduce_acc_params arrs       in_wave_reduce = everythingVolatile do_reduce @@ -622,10 +656,11 @@   cross_wave_reductions   errorsync -  sComment "Copy array-typed operands to result array" $ do-    sWhen (local_tid .==. 0) $-      localOps threadOperations $-        zipWithM_ writeArrayOpResult reduce_acc_params arrs+  unless (all isPrimParam reduce_acc_params) $+    sComment "Copy array-typed operands to result array" $+      sWhen (local_tid .==. 0) $+        localOps threadOperations $+          zipWithM_ writeArrayOpResult reduce_acc_params arrs  compileThreadOp :: OpCompiler GPUMem KernelEnv Imp.KernelOp compileThreadOp pat (Alloc size space) =@@ -900,10 +935,10 @@   let onLeaf name _ = lookupConstExp name       lookupConstExp name =         constExp =<< hasExp =<< M.lookup name vtable-      constExp (Op (Inner (SizeOp (GetSize key _)))) =-        Just $ LeafExp (Imp.SizeConst $ keyWithEntryPoint fname key) int32-      constExp (Op (Inner (SizeOp (GetSizeMax size_class)))) =-        Just $ LeafExp (Imp.SizeMaxConst size_class) int32+      constExp (Op (Inner (SizeOp (GetSize key c)))) =+        Just $ LeafExp (Imp.SizeConst (keyWithEntryPoint fname key) c) int32+      constExp (Op (Inner (SizeOp (GetSizeMax c)))) =+        Just $ LeafExp (Imp.SizeMaxConst c) int32       constExp e = primExpFromExp lookupConstExp e   pure $ replaceInPrimExpM onLeaf size   where@@ -1092,7 +1127,12 @@     -- | Number of groups.     kAttrNumGroups :: Count NumGroups SubExp,     -- | Group size.-    kAttrGroupSize :: Count GroupSize SubExp+    kAttrGroupSize :: Count GroupSize SubExp,+    -- | Variables that are specially in scope inside the kernel.+    -- Operationally, these will be available at kernel compile time+    -- (which happens at run-time, with access to machine-specific+    -- information).+    kAttrConstExps :: M.Map VName Imp.KernelConstExp   }  -- | The default kernel attributes.@@ -1105,7 +1145,8 @@     { kAttrFailureTolerant = False,       kAttrCheckLocalMemory = True,       kAttrNumGroups = num_groups,-      kAttrGroupSize = group_size+      kAttrGroupSize = group_size,+      kAttrConstExps = mempty     }  getSize :: String -> SizeClass -> CallKernelGen (TV Int64)@@ -1170,12 +1211,12 @@ sKernelOp attrs constants ops name m = do   HostEnv atomics _ locks <- askEnv   body <- makeAllMemoryGlobal $ subImpM_ (KernelEnv atomics constants locks) ops m-  uses <- computeKernelUses body mempty+  uses <- computeKernelUses body $ M.keys $ kAttrConstExps attrs   group_size <- onGroupSize $ kernelGroupSize constants   emit . Imp.Op . Imp.CallKernel $     Imp.Kernel       { Imp.kernelBody = body,-        Imp.kernelUses = uses,+        Imp.kernelUses = uses <> map constToUse (M.toList (kAttrConstExps attrs)),         Imp.kernelNumGroups = [untyped $ kernelNumGroups constants],         Imp.kernelGroupSize = [group_size],         Imp.kernelName = name,@@ -1192,6 +1233,8 @@         case x of           Just (LeafExp kc _) -> Right kc           _ -> Left $ untyped e++    constToUse (v, e) = Imp.ConstUse v e  sKernelFailureTolerant ::   Bool ->
src/Futhark/CodeGen/ImpGen/GPU/Group.hs view
@@ -27,7 +27,6 @@ import Futhark.Error import Futhark.IR.GPUMem import Futhark.IR.Mem.LMAD qualified as LMAD-import Futhark.MonadFreshNames import Futhark.Transform.Rename import Futhark.Util (chunks, mapAccumLM, takeLast) import Futhark.Util.IntegralExp (divUp, rem)
src/Futhark/CodeGen/ImpGen/GPU/SegHist.hs view
@@ -48,8 +48,8 @@ import Futhark.Construct (fullSliceNum) import Futhark.IR.GPUMem import Futhark.IR.Mem.LMAD qualified as LMAD-import Futhark.MonadFreshNames import Futhark.Pass.ExplicitAllocations ()+import Futhark.Transform.Substitute import Futhark.Util (chunks, mapAccumLM, maxinum, splitFromEnd, takeLast) import Futhark.Util.IntegralExp (divUp, quot, rem) import Prelude hiding (quot, rem)@@ -242,17 +242,9 @@           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 (prettyString (tvVar hist_L2)))-    $ Imp.SizeBespoke (nameFromString "L2_for_histogram") hist_L2_def+  sOp $ Imp.GetSizeMax (tvVar hist_L2) Imp.SizeCache    let hist_L2_ln_sz = 16 * 4 -- L2 cache line size approximation   hist_RACE_exp <-@@ -1073,7 +1065,8 @@   KernelBody GPUMem ->   CallKernelGen () compileSegHist (Pat pes) lvl space ops kbody = do-  KernelAttrs _ _ num_groups group_size <- lvlKernelAttrs lvl+  KernelAttrs {kAttrNumGroups = num_groups, kAttrGroupSize = group_size} <-+    lvlKernelAttrs lvl   -- 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@@ -1180,8 +1173,11 @@                   ++ zip bucket_ids (shapeDims (histShape op))                   ++ zip vector_ids (shapeDims $ histOpShape op)                   ++ [(subhistogram_id, Var $ tvVar num_histos)]+            -- The operator may have references to the old flat thread+            -- ID, which we must update to point at the new one.+            subst = M.singleton (segFlat space) flat_gtid -        let segred_op = SegBinOp Commutative (histOp op) (histNeutral op) mempty+        let segred_op = SegBinOp Commutative (substituteNames subst $ histOp op) (histNeutral op) mempty         compileSegRed' (Pat red_pes) grid segred_space [segred_op] $ \red_cont ->           red_cont . flip map subhistos $ \subhisto ->             ( Var subhisto,
src/Futhark/CodeGen/ImpGen/GPU/SegRed.hs view
@@ -41,845 +41,1035 @@ --   cases, we would allocate a /whole/ group per segment with the --   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.GPU.SegRed-  ( compileSegRed,-    compileSegRed',-    DoSegBody,-  )-where--import Control.Monad-import Data.List (genericLength, zip7)-import Data.Maybe-import Futhark.CodeGen.ImpCode.GPU qualified as Imp-import Futhark.CodeGen.ImpGen-import Futhark.CodeGen.ImpGen.GPU.Base-import Futhark.Error-import Futhark.IR.GPUMem-import Futhark.IR.Mem.LMAD qualified as LMAD-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.-maxNumOps :: Int32-maxNumOps = 10---- | Code generation for the body of the SegRed, taking a continuation--- 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 t'SubExp' for reading the result.-type DoSegBody = ([(SubExp, [Imp.TExp Int64])] -> InKernelGen ()) -> InKernelGen ()---- | Compile 'SegRed' instance to host-level code with calls to--- various kernels.-compileSegRed ::-  Pat LetDecMem ->-  SegLevel ->-  SegSpace ->-  [SegBinOp GPUMem] ->-  KernelBody GPUMem ->-  CallKernelGen ()-compileSegRed pat lvl space reds body = do-  emit $ Imp.DebugPrint "\n# SegRed" Nothing-  KernelAttrs _ _ num_groups group_size <- lvlKernelAttrs lvl-  let grid = KernelGrid num_groups group_size-  compileSegRed' pat grid space reds $ \red_cont ->-    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) $ patElems pat-        zipWithM_ (compileThreadResult space) map_arrs map_res--      red_cont $ map ((,[]) . kernelResultSubExp) red_res-  emit $ Imp.DebugPrint "" Nothing---- | Like 'compileSegRed', but where the body is a monadic action.-compileSegRed' ::-  Pat LetDecMem ->-  KernelGrid ->-  SegSpace ->-  [SegBinOp GPUMem] ->-  DoSegBody ->-  CallKernelGen ()-compileSegRed' pat grid space reds body-  | genericLength reds > maxNumOps =-      compilerLimitationS $-        "compileSegRed': at most " ++ show maxNumOps ++ " reduction operators are supported."-  | [(_, Constant (IntValue (Int64Value 1))), _] <- unSegSpace space =-      nonsegmentedReduction pat num_groups group_size space reds body-  | otherwise = do-      let group_size' = pe64 $ unCount group_size-          segment_size = pe64 $ 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 = gridNumGroups grid-    group_size = gridGroupSize grid---- | Prepare intermediate arrays for the reduction.  Prim-typed--- arguments go in local memory (so we need to do the allocation of--- those arrays inside the kernel), while array-typed arguments go in--- 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 GPUMem ->-  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-  forM red_acc_params $ \p ->-    case paramDec p of-      MemArray pt shape _ (ArrayIn mem _) -> do-        let shape' = Shape [num_threads] <> shape-        sArray "red_arr" pt shape' mem $-          LMAD.iota 0 (map pe64 $ shapeDims shape')-      _ -> do-        let pt = elemType $ paramType p-            shape = Shape [group_size]-        sAllocArray "red_arr" pt shape $ Space "local"---- | Arrays for storing group results.------ The group-result arrays have an extra dimension because they are--- also used for keeping vectorised accumulators for first-stage--- reduction, if necessary.  If necessary, this dimension has size--- group_size, and otherwise 1.  When actually storing group results,--- the first index is set to 0.-groupResultArrays ::-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->-  [SegBinOp GPUMem] ->-  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-          extra_dim-            | primType t, shapeRank shape == 0 = intConst Int64 1-            | otherwise = group_size-          full_shape = Shape [extra_dim, virt_num_groups] <> shape <> arrayShape t-          -- Move the groupsize dimension last to ensure coalesced-          -- memory access.-          perm = [1 .. shapeRank full_shape - 1] ++ [0]-      sAllocArrayPerm "segred_tmp" pt full_shape (Space "device") perm--nonsegmentedReduction ::-  Pat LetDecMem ->-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->-  SegSpace ->-  [SegBinOp GPUMem] ->-  DoSegBody ->-  CallKernelGen ()-nonsegmentedReduction segred_pat num_groups group_size space reds body = do-  let (gtids, dims) = unzip $ unSegSpace space-      dims' = map pe64 dims-      num_groups' = fmap pe64 num_groups-      group_size' = fmap pe64 group_size-      global_tid = Imp.le64 $ segFlat space-      w = last dims'--  counter <- genZeroes "counters" $ fromIntegral maxNumOps--  reds_group_res_arrs <- groupResultArrays num_groups group_size reds--  num_threads <--    dPrimV "num_threads" $-      unCount num_groups' * unCount group_size'--  sKernelThread "segred_nonseg" (segFlat space) (defKernelAttrs num_groups group_size) $ do-    constants <- kernelConstants <$> askEnv-    sync_arr <- sAllocArray "sync_arr" Bool (Shape [intConst Int32 1]) $ Space "local"-    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 :: Imp.TExp Int64)--    let num_elements = Imp.elements w-        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-    reds_op_renamed <--      reductionStageOne-        constants-        (zip gtids dims')-        num_elements-        global_tid-        elems_per_thread-        (tvExp num_threads)-        slugs-        body--    let segred_pes =-          chunks (map (length . segBinOpNeutral) reds) $-            patElems 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 ::-  Pat LetDecMem ->-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->-  SegSpace ->-  [SegBinOp GPUMem] ->-  DoSegBody ->-  CallKernelGen ()-smallSegmentsReduction (Pat segred_pes) num_groups group_size space reds body = do-  let (gtids, dims) = unzip $ unSegSpace space-      dims' = map pe64 dims-      segment_size = last dims'--  -- Careful to avoid division by zero now.-  segment_size_nonzero <--    dPrimVE "segment_size_nonzero" $ sMax64 1 segment_size--  let num_groups' = fmap pe64 num_groups-      group_size' = fmap pe64 group_size-  num_threads <- dPrimV "num_threads" $ unCount num_groups' * unCount group_size'-  let num_segments = product $ init dims'-      segments_per_group = unCount group_size' `quot` segment_size_nonzero-      required_groups = sExt32 $ num_segments `divUp` segments_per_group--  emit $ Imp.DebugPrint "# SegRed-small" Nothing-  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" (segFlat space) (defKernelAttrs num_groups group_size) $ do-    constants <- kernelConstants <$> askEnv-    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' -> 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 = sExt64 $ kernelLocalThreadId constants-          segment_index =-            (ltid `quot` segment_size_nonzero)-              + (sExt64 group_id' * sExt64 segments_per_group)-          index_within_segment = ltid `rem` segment_size--      dIndexSpace (zip (init gtids) (init dims')) segment_index-      dPrimV_ (last gtids) index_within_segment--      let out_of_bounds =-            forM_ (zip reds reds_arrs) $ \(SegBinOp _ _ nes _, red_arrs) ->-              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 = map (,[ltid]) (concat reds_arrs)-                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--      sOp $ Imp.ErrorSync Imp.FenceLocal -- Also implicitly barrier.-      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)-              (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-          ( 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 ::-  Pat LetDecMem ->-  Count NumGroups SubExp ->-  Count GroupSize SubExp ->-  SegSpace ->-  [SegBinOp GPUMem] ->-  DoSegBody ->-  CallKernelGen ()-largeSegmentsReduction segred_pat num_groups group_size space reds body = do-  let (gtids, dims) = unzip $ unSegSpace space-      dims' = map pe64 dims-      num_segments = product $ init dims'-      segment_size = last dims'-      num_groups' = fmap pe64 num_groups-      group_size' = fmap pe64 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--  num_threads <--    dPrimV "num_threads" $-      unCount num_groups' * unCount group_size'--  threads_per_segment <--    dPrimV "threads_per_segment" $-      groups_per_segment * unCount group_size'--  emit $ Imp.DebugPrint "# SegRed-large" Nothing-  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 (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-  -- allocate and zero out an array here, which is expensive.-  -- However, we exploit the fact that the number of segments being-  -- reduced at any point in time is limited by the number of-  -- workgroups. If we bound the number of workgroups, we can get away-  -- with using that many counters.  FIXME: Is this limit checked-  -- anywhere?  There are other places in the compiler that will fail-  -- if the group count exceeds the maximum group size, which is at-  -- most 1024 anyway.-  let num_counters = fromIntegral maxNumOps * 1024-  counter <- genZeroes "counters" num_counters--  sKernelThread "segred_large" (segFlat space) (defKernelAttrs num_groups group_size) $ do-    constants <- kernelConstants <$> askEnv-    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 (sExt32 (tvExp virt_num_groups)) $ \group_id -> do-      let segment_gtids = init gtids-          w = last dims-          local_tid = kernelLocalThreadId constants--      flat_segment_id <--        dPrimVE "flat_segment_id" $-          group_id `quot` sExt32 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 = sExt64 flat_segment_id * groups_per_segment-      dIndexSpace (zip segment_gtids (init dims')) $ sExt64 flat_segment_id-      dPrim_ (last gtids) int64-      let num_elements = Imp.elements $ pe64 w--      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-          (tvExp threads_per_segment)-          slugs-          body--      let segred_pes =-            chunks (map (length . segBinOpNeutral) reds) $-              patElems 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.le64 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.le64 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.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` sMax64 1 num_segments-  elements_per_thread <--    dPrimVE "elements_per_thread" $-      segment_size `divUp` (unCount group_size * groups_per_segment)-  pure (groups_per_segment, Imp.elements elements_per_thread)---- | A SegBinOp with auxiliary information.-data SegBinOpSlug = SegBinOpSlug-  { slugOp :: SegBinOp GPUMem,-    -- | The arrays used for computing the intra-group reduction-    -- (either local or global memory).-    slugArrs :: [VName],-    -- | Places to store accumulator in stage 1 reduction.-    slugAccs :: [(VName, [Imp.TExp Int64])]-  }--slugBody :: SegBinOpSlug -> Body GPUMem-slugBody = lambdaBody . segBinOpLambda . slugOp--slugParams :: SegBinOpSlug -> [LParam GPUMem]-slugParams = lambdaParams . segBinOpLambda . slugOp--slugNeutral :: SegBinOpSlug -> [SubExp]-slugNeutral = segBinOpNeutral . slugOp--slugShape :: SegBinOpSlug -> Shape-slugShape = segBinOpShape . slugOp--slugsComm :: [SegBinOpSlug] -> Commutativity-slugsComm = mconcat . map (segBinOpComm . slugOp)--accParams, nextParams :: SegBinOpSlug -> [LParam GPUMem]-accParams slug = take (length (slugNeutral slug)) $ slugParams slug-nextParams slug = drop (length (slugNeutral slug)) $ slugParams slug--segBinOpSlug :: Imp.TExp Int32 -> Imp.TExp Int32 -> (SegBinOp GPUMem, [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-          pure (tvVar acc, [])-      | otherwise =-          pure (param_arr, [sExt64 local_tid, sExt64 group_id])--computeThreadChunkSize ::-  Commutativity ->-  Imp.TExp Int64 ->-  Imp.TExp Int64 ->-  Imp.Count Imp.Elements (Imp.TExp Int64) ->-  Imp.Count Imp.Elements (Imp.TExp Int64) ->-  TV Int64 ->-  ImpM rep r op ()-computeThreadChunkSize Commutative threads_per_segment thread_index elements_per_thread num_elements chunk_var =-  chunk_var-    <-- sMin64-      (Imp.unCount elements_per_thread)-      ((Imp.unCount num_elements - thread_index) `divUp` threads_per_segment)-computeThreadChunkSize Noncommutative _ 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--reductionStageZero ::-  KernelConstants ->-  [(VName, Imp.TExp Int64)] ->-  Imp.Count Imp.Elements (Imp.TExp Int64) ->-  Imp.TExp Int64 ->-  Imp.Count Imp.Elements (Imp.TExp Int64) ->-  Imp.TExp Int64 ->-  [SegBinOpSlug] ->-  DoSegBody ->-  InKernelGen ([Lambda GPUMem], InKernelGen ())-reductionStageZero constants ispace num_elements global_tid elems_per_thread threads_per_segment slugs body = do-  let (gtids, _dims) = unzip ispace-      gtid = mkTV (last gtids) int64-      local_tid = sExt64 $ kernelLocalThreadId constants--  -- Figure out how many elements this thread should process.-  chunk_size <- dPrim "chunk_size" int64-  computeThreadChunkSize-    (slugsComm slugs)-    threads_per_segment-    (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 []--  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) []--            sOp $ Imp.ErrorSync Imp.FenceLocal -- Also implicitly barrier.-            groupReduce (sExt32 (kernelGroupSize constants)) slug_op_renamed (slugArrs slug)--            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) []--  -- If this is a non-commutative reduction, each thread must run the-  -- loop the same number of iterations, because we will be performing-  -- a group-wide reduction in there.-  let comm = slugsComm slugs-      (bound, check_bounds) =-        case comm of-          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-        Commutative ->-          global_tid + threads_per_segment * i-        Noncommutative ->-          let index_in_segment = global_tid `quot` kernelGroupSize constants-           in sExt64 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 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)-                      (map resSubExp $ bodyResult $ slugBody slug)-                  )-                $ \((acc, acc_is), se) ->-                  copyDWIMFix acc (acc_is ++ vec_is) se []--    case comm of-      Noncommutative -> do-        doTheReduction-        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 []-          sUnless (local_tid .==. 0) reset_to_neutral-      _ -> pure ()-  sOp $ Imp.ErrorSync Imp.FenceLocal-  pure (slugs_op_renamed, doTheReduction)--reductionStageOne ::-  KernelConstants ->-  [(VName, Imp.TExp Int64)] ->-  Imp.Count Imp.Elements (Imp.TExp Int64) ->-  Imp.TExp Int64 ->-  Imp.Count Imp.Elements (Imp.TExp Int64) ->-  Imp.TExp Int64 ->-  [SegBinOpSlug] ->-  DoSegBody ->-  InKernelGen [Lambda GPUMem]-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--  case slugsComm slugs of-    Noncommutative -> pure ()-    Commutative -> doTheReduction--  pure slugs_op_renamed--reductionStageTwo ::-  KernelConstants ->-  [PatElem LetDecMem] ->-  Imp.TExp Int32 ->-  Imp.TExp Int32 ->-  [Imp.TExp Int64] ->-  Imp.TExp Int64 ->-  Imp.TExp Int64 ->-  SegBinOpSlug ->-  [LParam GPUMem] ->-  [LParam GPUMem] ->-  Lambda GPUMem ->-  [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--    sOp $ Imp.Barrier Imp.FenceGlobal--    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--      sLoopNest (slugShape slug) $ \vec_is -> do-        unless (null $ slugShape slug) $-          sOp (Imp.Barrier Imp.FenceLocal)--        -- 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--          forM_ (zip red_x_params nes) $ \(p, ne) ->-            copyDWIMFix (paramName p) [] ne []--          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)--              compileStms mempty (bodyStms $ slugBody slug) $-                forM_ (zip red_x_params $ map resSubExp $ bodyResult $ slugBody slug) $ \(p, se) ->-                  copyDWIMFix (paramName p) [] se []--        forM_ (zip red_x_params red_arrs) $ \(p, arr) ->-          when (primType $ paramType p) $-            copyDWIMFix arr [sExt64 local_tid] (Var $ paramName p) []--        sOp $ Imp.ErrorSync Imp.FenceLocal--        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)-                  []+--+-- An optimization specfically targeted at non-segmented and large-segments+-- segmented reductions with non-commutative is made: The stage one main loop is+-- essentially stripmined by a factor `chunk`, inserting collective copies via+-- local memory of each reduction parameter going into the intra-group (partial)+-- reductions. This saves a factor `chunk` number of intra-group reductions at+-- the cost of some overhead in collective copies.+module Futhark.CodeGen.ImpGen.GPU.SegRed+  ( compileSegRed,+    compileSegRed',+    DoSegBody,+  )+where++import Control.Monad+import Data.List (genericLength, zip4)+import Data.Map qualified as M+import Data.Maybe+import Futhark.CodeGen.ImpCode.GPU qualified as Imp+import Futhark.CodeGen.ImpGen+import Futhark.CodeGen.ImpGen.GPU.Base+import Futhark.Error+import Futhark.IR.GPUMem+import Futhark.IR.Mem.LMAD qualified as LMAD+import Futhark.Transform.Rename+import Futhark.Util (chunks, mapAccumLM)+import Futhark.Util.IntegralExp (divUp, nextMul, quot, rem)+import Prelude hiding (quot, rem)++forM2_ :: (Monad m) => [a] -> [b] -> (a -> b -> m c) -> m ()+forM2_ xs ys f = forM_ (zip xs ys) (uncurry f)++-- | The maximum number of operators we support in a single SegRed.+-- This limit arises out of the static allocation of counters.+maxNumOps :: Int+maxNumOps = 10++-- | Code generation for the body of the SegRed, taking a continuation+-- 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 t'SubExp' for reading the result.+type DoSegBody = ([(SubExp, [Imp.TExp Int64])] -> InKernelGen ()) -> InKernelGen ()++-- | Datatype used to distinguish between and work with the different sets of+-- intermediate memory we need for the different ReduceKinds.+data SegRedIntermediateArrays+  = GeneralSegRedInterms+      { groupRedArrs :: [VName]+      }+  | NoncommPrimSegRedInterms+      { collCopyArrs :: [VName],+        groupRedArrs :: [VName],+        privateChunks :: [VName]+      }++-- | Compile 'SegRed' instance to host-level code with calls to+-- various kernels.+compileSegRed ::+  Pat LetDecMem ->+  SegLevel ->+  SegSpace ->+  [SegBinOp GPUMem] ->+  KernelBody GPUMem ->+  CallKernelGen ()+compileSegRed pat lvl space segbinops map_kbody = do+  emit $ Imp.DebugPrint "\n# SegRed" Nothing+  KernelAttrs {kAttrNumGroups = num_groups, kAttrGroupSize = group_size} <-+    lvlKernelAttrs lvl+  let grid = KernelGrid num_groups group_size++  compileSegRed' pat grid space segbinops $ \red_cont ->+    sComment "apply map function" $+      compileStms mempty (kernelBodyStms map_kbody) $ do+        let (red_res, map_res) = splitAt (segBinOpResults segbinops) $ kernelBodyResult map_kbody++        let mapout_arrs = drop (segBinOpResults segbinops) $ patElems pat+        unless (null mapout_arrs) $+          sComment "write map-out result(s)" $ do+            zipWithM_ (compileThreadResult space) mapout_arrs map_res++        red_cont $ map ((,[]) . kernelResultSubExp) red_res+  emit $ Imp.DebugPrint "" Nothing++paramOf :: SegBinOp GPUMem -> [Param LParamMem]+paramOf (SegBinOp _ op ne _) = take (length ne) $ lambdaParams op++isPrimSegBinOp :: SegBinOp GPUMem -> Bool+isPrimSegBinOp segbinop =+  all primType (lambdaReturnType $ segBinOpLambda segbinop)+    && shapeRank (segBinOpShape segbinop) == 0++-- | Like 'compileSegRed', but where the body is a monadic action.+compileSegRed' ::+  Pat LetDecMem ->+  KernelGrid ->+  SegSpace ->+  [SegBinOp GPUMem] ->+  DoSegBody ->+  CallKernelGen ()+compileSegRed' pat grid space segbinops map_body_cont+  | genericLength segbinops > maxNumOps =+      compilerLimitationS $+        "compileSegRed': at most " ++ show maxNumOps ++ " reduction operators are supported."+  | otherwise = do+      chunk_v <- dPrimV "chunk_size" . isInt64 =<< kernelConstToExp chunk_const+      case unSegSpace space of+        [(_, Constant (IntValue (Int64Value 1))), _] ->+          compileReduction (chunk_v, chunk_const) nonsegmentedReduction+        _ -> do+          let segment_size = pe64 $ last $ segSpaceDims space+              use_small_segments = segment_size * 2 .<. pe64 (unCount group_size) * tvExp chunk_v+          sIf+            use_small_segments+            (compileReduction (chunk_v, chunk_const) smallSegmentsReduction)+            (compileReduction (chunk_v, chunk_const) largeSegmentsReduction)+  where+    compileReduction chunk f =+      f pat num_groups group_size chunk space segbinops map_body_cont++    param_types = map paramType $ concatMap paramOf segbinops++    num_groups = gridNumGroups grid+    group_size = gridGroupSize grid++    chunk_const =+      if Noncommutative `elem` map segBinOpComm segbinops+        && all isPrimSegBinOp segbinops+        then getChunkSize param_types+        else Imp.ValueExp $ IntValue $ intValue Int64 (1 :: Int64)++-- | Prepare intermediate arrays for the reduction.  Prim-typed+-- arguments go in local memory (so we need to do the allocation of+-- those arrays inside the kernel), while array-typed arguments go in+-- global memory.  Allocations for the latter have already been+-- performed.  This policy is baked into how the allocations are done+-- in ExplicitAllocations.+--+-- For more info about the intermediate arrays used for the different reduction+-- kernels, see note [IntermArrays].+makeIntermArrays ::+  Imp.TExp Int64 ->+  SubExp ->+  SubExp ->+  [SegBinOp GPUMem] ->+  InKernelGen [SegRedIntermediateArrays]+makeIntermArrays group_id group_size chunk segbinops+  | Noncommutative <- mconcat (map segBinOpComm segbinops),+    all isPrimSegBinOp segbinops =+      noncommPrimSegRedInterms+  | otherwise =+      generalSegRedInterms group_id group_size segbinops+  where+    params = map paramOf segbinops++    noncommPrimSegRedInterms = do+      group_worksize <- tvSize <$> dPrimV "group_worksize" group_worksize_E++      -- compute total amount of lmem.+      let sum_ x y = nextMul x y + group_size_E * y+          group_reds_lmem_requirement = foldl sum_ 0 $ concat elem_sizes+          collcopy_lmem_requirement = group_worksize_E * max_elem_size+          lmem_total_size =+            Imp.bytes $+              collcopy_lmem_requirement `sMax64` group_reds_lmem_requirement++      -- offsets into the total pool of lmem for each group reduction array.+      (_, offsets) <-+        forAccumLM2D 0 elem_sizes $ \byte_offs elem_size ->+          (,byte_offs `quot` elem_size)+            <$> dPrimVE "offset" (sum_ byte_offs elem_size)++      -- total pool of local mem.+      lmem <- sAlloc "local_mem" lmem_total_size (Space "local")+      let arrInLMem ptype name len_se offset =+            sArray+              (name ++ "_" ++ prettyString ptype)+              ptype+              (Shape [len_se])+              lmem+              $ LMAD.iota offset [pe64 len_se]++      forM (zipWith zip params offsets) $ \ps_and_offsets -> do+        (coll_copy_arrs, group_red_arrs, priv_chunks) <-+          fmap unzip3 $ forM ps_and_offsets $ \(p, offset) -> do+            let ptype = elemType $ paramType p+            (,,)+              <$> arrInLMem ptype "coll_copy_arr" group_worksize 0+              <*> arrInLMem ptype "group_red_arr" group_size offset+              <*> sAllocArray+                ("chunk_" ++ prettyString ptype)+                ptype+                (Shape [chunk])+                (ScalarSpace [chunk] ptype)+        pure $ NoncommPrimSegRedInterms coll_copy_arrs group_red_arrs priv_chunks+    group_size_E = pe64 group_size+    group_worksize_E = group_size_E * pe64 chunk++    paramSize = primByteSize . elemType . paramType+    elem_sizes = map (map paramSize) params+    max_elem_size = maximum $ concat elem_sizes++    forAccumLM2D acc ls f = mapAccumLM (mapAccumLM f) acc ls++generalSegRedInterms ::+  Imp.TExp Int64 ->+  SubExp ->+  [SegBinOp GPUMem] ->+  InKernelGen [SegRedIntermediateArrays]+generalSegRedInterms group_id group_size segbinops =+  fmap (map GeneralSegRedInterms) $+    forM (map paramOf segbinops) $+      mapM $ \p ->+        case paramDec p of+          MemArray pt shape _ (ArrayIn mem _) -> do+            let shape' = Shape [group_size] <> shape+            let shape_E = map pe64 $ shapeDims shape'+            sArray ("red_arr_" ++ prettyString pt) pt shape' mem $+              LMAD.iota (group_id * product shape_E) shape_E+          _ -> do+            let pt = elemType $ paramType p+                shape = Shape [group_size]+            sAllocArray ("red_arr_" ++ prettyString pt) pt shape $ Space "local"++-- | Arrays for storing group results.+--+-- The group-result arrays have an extra dimension because they are+-- also used for keeping vectorised accumulators for first-stage+-- reduction, if necessary.  If necessary, this dimension has size+-- group_size, and otherwise 1.  When actually storing group results,+-- the first index is set to 0.+groupResultArrays ::+  SubExp ->+  SubExp ->+  [SegBinOp GPUMem] ->+  CallKernelGen [[VName]]+groupResultArrays num_virtgroups group_size segbinops =+  forM segbinops $ \(SegBinOp _ lam _ shape) ->+    forM (lambdaReturnType lam) $ \t -> do+      let pt = elemType t+          extra_dim+            | primType t, shapeRank shape == 0 = intConst Int64 1+            | otherwise = group_size+          full_shape = Shape [extra_dim, num_virtgroups] <> shape <> arrayShape t+          -- Move the groupsize dimension last to ensure coalesced+          -- memory access.+          perm = [1 .. shapeRank full_shape - 1] ++ [0]+      sAllocArrayPerm "segred_tmp" pt full_shape (Space "device") perm++type DoCompileSegRed =+  Pat LetDecMem ->+  Count NumGroups SubExp ->+  Count GroupSize SubExp ->+  (TV Int64, Imp.KernelConstExp) ->+  SegSpace ->+  [SegBinOp GPUMem] ->+  DoSegBody ->+  CallKernelGen ()++nonsegmentedReduction :: DoCompileSegRed+nonsegmentedReduction (Pat segred_pes) num_groups group_size (chunk_v, chunk_const) space segbinops map_body_cont = do+  let (gtids, dims) = unzip $ unSegSpace space+      chunk = tvExp chunk_v+      num_groups_se = unCount num_groups+      group_size_se = unCount group_size+      group_size' = pe64 group_size_se+      global_tid = Imp.le64 $ segFlat space+      n = pe64 $ last dims++  counters <- genZeroes "counters" maxNumOps++  reds_group_res_arrs <- groupResultArrays num_groups_se group_size_se segbinops++  num_threads <-+    fmap tvSize $ dPrimV "num_threads" $ pe64 num_groups_se * group_size'++  let attrs =+        (defKernelAttrs num_groups group_size)+          { kAttrConstExps = M.singleton (tvVar chunk_v) chunk_const+          }++  sKernelThread "segred_nonseg" (segFlat space) attrs $ do+    constants <- kernelConstants <$> askEnv+    let ltid = kernelLocalThreadId constants+    let group_id = kernelGroupId constants++    interms <- makeIntermArrays (sExt64 group_id) group_size_se (tvSize chunk_v) segbinops+    sync_arr <- sAllocArray "sync_arr" Bool (Shape [intConst Int32 1]) $ Space "local"++    -- Since this is the nonsegmented case, all outer segment IDs must+    -- necessarily be 0.+    forM_ gtids $ \v -> dPrimV_ v (0 :: Imp.TExp Int64)++    q <- dPrimVE "q" $ n `divUp` (sExt64 (kernelNumThreads constants) * chunk)++    slugs <-+      mapM (segBinOpSlug ltid group_id) $+        zip3 segbinops interms reds_group_res_arrs+    new_lambdas <-+      reductionStageOne+        gtids+        n+        global_tid+        q+        chunk+        (pe64 num_threads)+        slugs+        map_body_cont++    let segred_pess =+          chunks+            (map (length . segBinOpNeutral) segbinops)+            segred_pes++    forM_ (zip4 segred_pess slugs new_lambdas [0 ..]) $+      \(pes, slug, new_lambda, i) ->+        reductionStageTwo+          pes+          group_id+          [0]+          0+          (sExt64 $ kernelNumGroups constants)+          slug+          new_lambda+          counters+          sync_arr+          (fromInteger i)++smallSegmentsReduction :: DoCompileSegRed+smallSegmentsReduction (Pat segred_pes) num_groups group_size _ space segbinops map_body_cont = do+  let (gtids, dims) = unzip $ unSegSpace space+      dims' = map pe64 dims+      segment_size = last dims'++  -- Careful to avoid division by zero now.+  segment_size_nonzero <-+    dPrimVE "segment_size_nonzero" $ sMax64 1 segment_size++  let group_size_se = unCount group_size+      num_groups_se = unCount group_size+      num_groups' = pe64 num_groups_se+      group_size' = pe64 group_size_se+  num_threads <- fmap tvSize $ dPrimV "num_threads" $ num_groups' * group_size'+  let num_segments = product $ init dims'+      segments_per_group = group_size' `quot` segment_size_nonzero+      required_groups = sExt32 $ num_segments `divUp` segments_per_group++  emit $ Imp.DebugPrint "# SegRed-small" Nothing+  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" (segFlat space) (defKernelAttrs num_groups group_size) $ do+    constants <- kernelConstants <$> askEnv+    let group_id = kernelGroupSize constants+        ltid = sExt64 $ kernelLocalThreadId constants++    interms <- generalSegRedInterms group_id group_size_se segbinops+    let reds_arrs = map groupRedArrs interms++    -- 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 $ \virtgroup_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 segment_index =+            (ltid `quot` segment_size_nonzero)+              + (sExt64 virtgroup_id * sExt64 segments_per_group)+          index_within_segment = ltid `rem` segment_size++      dIndexSpace (zip (init gtids) (init dims')) segment_index+      dPrimV_ (last gtids) index_within_segment++      let in_bounds =+            map_body_cont $ \red_res ->+              sComment "save results to be reduced" $ do+                let red_dests = map (,[ltid]) (concat reds_arrs)+                forM2_ red_dests red_res $ \(d, d_is) (res, res_is) ->+                  copyDWIMFix d d_is res res_is+          out_of_bounds =+            forM2_ segbinops reds_arrs $ \(SegBinOp _ _ nes _) red_arrs ->+              forM2_ red_arrs nes $ \arr ne ->+                copyDWIMFix arr [ltid] ne []++      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++      sOp $ Imp.ErrorSync Imp.FenceLocal -- Also implicitly barrier.+      let crossesSegment from to =+            (sExt64 to - sExt64 from) .>. (sExt64 to `rem` segment_size)+      sWhen (segment_size .>. 0) $+        sComment "perform segmented scan to imitate reduction" $+          forM2_ segbinops reds_arrs $ \(SegBinOp _ red_op _ _) red_arrs ->+            groupScan+              (Just crossesSegment)+              (sExt64 $ pe64 num_threads)+              (segment_size * segments_per_group)+              red_op+              red_arrs++      sOp $ Imp.Barrier Imp.FenceLocal++      sComment "save final values of segments"+        $ sWhen+          ( sExt64 virtgroup_id+              * segments_per_group+              + sExt64 ltid+                .<. num_segments+                .&&. ltid+                .<. segments_per_group+          )+        $ forM2_ segred_pes (concat reds_arrs)+        $ \pe arr -> do+          -- Figure out which segment result this thread should write...+          let flat_segment_index =+                sExt64 virtgroup_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 :: DoCompileSegRed+largeSegmentsReduction (Pat segred_pes) num_groups group_size (chunk_v, chunk_const) space segbinops map_body_cont = do+  let (gtids, dims) = unzip $ unSegSpace space+      dims' = map pe64 dims+      num_segments = product $ init dims'+      segment_size = last dims'+      num_groups' = pe64 $ unCount num_groups+      group_size_se = unCount group_size+      group_size' = pe64 group_size_se+      chunk = tvExp chunk_v++  groups_per_segment <-+    dPrimVE "groups_per_segment" $+      num_groups' `divUp` sMax64 1 num_segments++  q <-+    dPrimVE "q" $+      segment_size `divUp` (group_size' * groups_per_segment * chunk)++  num_virtgroups <-+    dPrimV "num_virtgroups" $+      groups_per_segment * num_segments+  threads_per_segment <-+    dPrimVE "threads_per_segment" $+      groups_per_segment * group_size'++  emit $ Imp.DebugPrint "# SegRed-large" Nothing+  emit $ Imp.DebugPrint "num_segments" $ Just $ untyped num_segments+  emit $ Imp.DebugPrint "segment_size" $ Just $ untyped segment_size+  emit $ Imp.DebugPrint "num_virtgroups" $ Just $ untyped $ tvExp num_virtgroups+  emit $ Imp.DebugPrint "num_groups" $ Just $ untyped num_groups'+  emit $ Imp.DebugPrint "group_size" $ Just $ untyped group_size'+  emit $ Imp.DebugPrint "q" $ Just $ untyped q+  emit $ Imp.DebugPrint "groups_per_segment" $ Just $ untyped groups_per_segment++  reds_group_res_arrs <- groupResultArrays (tvSize num_virtgroups) group_size_se segbinops++  -- In principle we should have a counter for every segment.  Since+  -- the number of segments is a dynamic quantity, we would have to+  -- allocate and zero out an array here, which is expensive.+  -- However, we exploit the fact that the number of segments being+  -- reduced at any point in time is limited by the number of+  -- workgroups. If we bound the number of workgroups, we can get away+  -- with using that many counters.  FIXME: Is this limit checked+  -- anywhere?  There are other places in the compiler that will fail+  -- if the group count exceeds the maximum group size, which is at+  -- most 1024 anyway.+  let num_counters = maxNumOps * 1024+  counters <- genZeroes "counters" $ fromIntegral num_counters++  let attrs =+        (defKernelAttrs num_groups group_size)+          { kAttrConstExps = M.singleton (tvVar chunk_v) chunk_const+          }++  sKernelThread "segred_large" (segFlat space) attrs $ do+    constants <- kernelConstants <$> askEnv+    let group_id = sExt64 $ kernelGroupId constants+        ltid = kernelLocalThreadId constants++    interms <- makeIntermArrays group_id group_size_se (tvSize chunk_v) segbinops+    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 (sExt32 (tvExp num_virtgroups)) $ \virtgroup_id -> do+      let segment_gtids = init gtids++      flat_segment_id <-+        dPrimVE "flat_segment_id" $+          sExt64 virtgroup_id `quot` groups_per_segment++      global_tid <-+        dPrimVE "global_tid" $+          (sExt64 virtgroup_id * sExt64 group_size' + sExt64 ltid)+            `rem` threads_per_segment++      let first_group_for_segment = flat_segment_id * groups_per_segment+      dIndexSpace (zip segment_gtids (init dims')) flat_segment_id+      dPrim_ (last gtids) int64+      let n = pe64 $ last dims++      slugs <-+        mapM (segBinOpSlug ltid virtgroup_id) $+          zip3 segbinops interms reds_group_res_arrs+      new_lambdas <-+        reductionStageOne+          gtids+          n+          global_tid+          q+          chunk+          threads_per_segment+          slugs+          map_body_cont++      let segred_pess =+            chunks+              (map (length . segBinOpNeutral) segbinops)+              segred_pes++          multiple_groups_per_segment =+            forM_ (zip4 segred_pess slugs new_lambdas [0 ..]) $+              \(pes, slug, new_lambda, i) -> do+                let counter_idx =+                      fromIntegral (i * num_counters)+                        + flat_segment_id+                          `rem` fromIntegral num_counters+                reductionStageTwo+                  pes+                  virtgroup_id+                  (map Imp.le64 segment_gtids)+                  first_group_for_segment+                  groups_per_segment+                  slug+                  new_lambda+                  counters+                  sync_arr+                  counter_idx++          one_group_per_segment =+            sComment "first thread in group saves final result to memory" $+              forM2_ slugs segred_pess $ \slug pes ->+                sWhen (ltid .==. 0) $+                  forM2_ pes (slugAccs slug) $ \v (acc, acc_is) ->+                    copyDWIMFix (patElemName v) (map Imp.le64 segment_gtids) (Var acc) acc_is++      sIf (groups_per_segment .==. 1) one_group_per_segment multiple_groups_per_segment++-- | Auxiliary information for a single reduction. A slug holds the `SegBinOp`+-- operator for a single reduction, the different arrays required throughout+-- stages one and two, and a global mem destination for the final result of the+-- particular reduction.+data SegBinOpSlug = SegBinOpSlug+  { slugOp :: SegBinOp GPUMem,+    -- | Intermediate arrays needed for the given reduction.+    slugInterms :: SegRedIntermediateArrays,+    -- | Place(s) to store group accumulator(s) in stage 1 reduction.+    slugAccs :: [(VName, [Imp.TExp Int64])],+    -- | Global memory destination(s) for the final result(s) for this+    -- particular reduction.+    groupResArrs :: [VName]+  }++segBinOpSlug ::+  Imp.TExp Int32 ->+  Imp.TExp Int32 ->+  (SegBinOp GPUMem, SegRedIntermediateArrays, [VName]) ->+  InKernelGen SegBinOpSlug+segBinOpSlug ltid group_id (op, interms, group_res_arrs) = do+  accs <- zipWithM mkAcc (lambdaParams (segBinOpLambda op)) group_res_arrs+  pure $ SegBinOpSlug op interms accs group_res_arrs+  where+    mkAcc p group_res_arr+      | Prim t <- paramType p,+        shapeRank (segBinOpShape op) == 0 = do+          group_res_acc <- dPrim (baseString (paramName p) <> "_group_res_acc") t+          pure (tvVar group_res_acc, [])+      -- if this is a non-primitive reduction, the global mem result array will+      -- double as accumulator.+      | otherwise =+          pure (group_res_arr, [sExt64 ltid, sExt64 group_id])++slugLambda :: SegBinOpSlug -> Lambda GPUMem+slugLambda = segBinOpLambda . slugOp++slugBody :: SegBinOpSlug -> Body GPUMem+slugBody = lambdaBody . slugLambda++slugParams :: SegBinOpSlug -> [LParam GPUMem]+slugParams = lambdaParams . slugLambda++slugNeutral :: SegBinOpSlug -> [SubExp]+slugNeutral = segBinOpNeutral . slugOp++slugShape :: SegBinOpSlug -> Shape+slugShape = segBinOpShape . slugOp++slugsComm :: [SegBinOpSlug] -> Commutativity+slugsComm = mconcat . map (segBinOpComm . slugOp)++slugSplitParams :: SegBinOpSlug -> ([LParam GPUMem], [LParam GPUMem])+slugSplitParams slug = splitAt (length (slugNeutral slug)) $ slugParams slug++slugGroupRedArrs :: SegBinOpSlug -> [VName]+slugGroupRedArrs = groupRedArrs . slugInterms++slugPrivChunks :: SegBinOpSlug -> [VName]+slugPrivChunks = privateChunks . slugInterms++slugCollCopyArrs :: SegBinOpSlug -> [VName]+slugCollCopyArrs = collCopyArrs . slugInterms++reductionStageOne ::+  [VName] ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  [SegBinOpSlug] ->+  DoSegBody ->+  InKernelGen [Lambda GPUMem]+reductionStageOne gtids n global_tid q chunk threads_per_segment slugs body_cont = do+  constants <- kernelConstants <$> askEnv+  let glb_ind_var = mkTV (last gtids) int64+      ltid = sExt64 $ kernelLocalThreadId constants++  dScope Nothing $ scopeOfLParams $ concatMap slugParams slugs++  sComment "ne-initialise the outer (per-group) accumulator(s)" $ do+    forM_ slugs $ \slug ->+      forM2_ (slugAccs slug) (slugNeutral slug) $ \(acc, acc_is) ne ->+        sLoopNest (slugShape slug) $ \vec_is ->+          copyDWIMFix acc (acc_is ++ vec_is) ne []++  new_lambdas <- mapM (renameLambda . slugLambda) slugs+  let group_size = sExt32 $ kernelGroupSize constants+  let doGroupReduce =+        forM2_ slugs new_lambdas $ \slug new_lambda -> do+          let accs = slugAccs slug+          let params = slugParams slug+          sLoopNest (slugShape slug) $ \vec_is -> do+            let group_red_arrs = slugGroupRedArrs slug+            sComment "store accs. prims go in lmem; non-prims in params (in global mem)" $+              forM_ (zip3 group_red_arrs accs params) $+                \(arr, (acc, acc_is), p) ->+                  if isPrimParam p+                    then copyDWIMFix arr [ltid] (Var acc) (acc_is ++ vec_is)+                    else copyDWIMFix (paramName p) [] (Var acc) (acc_is ++ vec_is)++            sOp $ Imp.ErrorSync Imp.FenceLocal -- Also implicitly barrier.+            groupReduce group_size new_lambda group_red_arrs+            sOp $ Imp.Barrier Imp.FenceLocal++            sComment "thread 0 updates per-group acc(s); rest reset to ne" $ do+              sIf+                (ltid .==. 0)+                ( forM2_ accs (lambdaParams new_lambda) $+                    \(acc, acc_is) p ->+                      copyDWIMFix acc (acc_is ++ vec_is) (Var $ paramName p) []+                )+                ( forM2_ accs (slugNeutral slug) $+                    \(acc, acc_is) ne ->+                      copyDWIMFix acc (acc_is ++ vec_is) ne []+                )++  case (slugsComm slugs, all (isPrimSegBinOp . slugOp) slugs) of+    (Noncommutative, True) ->+      noncommPrimParamsStageOneBody+        slugs+        body_cont+        glb_ind_var+        global_tid+        q+        n+        chunk+        doGroupReduce+    _ ->+      generalStageOneBody+        slugs+        body_cont+        glb_ind_var+        global_tid+        q+        n+        threads_per_segment+        doGroupReduce++  pure new_lambdas++generalStageOneBody ::+  [SegBinOpSlug] ->+  DoSegBody ->+  TV Int64 ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  InKernelGen () ->+  InKernelGen ()+generalStageOneBody slugs body_cont glb_ind_var global_tid q n threads_per_segment doGroupReduce = do+  let is_comm = slugsComm slugs == Commutative++  constants <- kernelConstants <$> askEnv+  let group_size = kernelGroupSize constants+      ltid = sExt64 $ kernelLocalThreadId constants++  -- this group's id within its designated segment, and this group's initial+  -- global offset.+  group_id_in_segment <- dPrimVE "group_id_in_segment" $ global_tid `quot` group_size+  group_base_offset <- dPrimVE "group_base_offset" $ group_id_in_segment * q * group_size++  sFor "i" q $ \i -> do+    group_offset <- dPrimVE "group_offset" $ group_base_offset + i * group_size+    glb_ind_var+      <-- if is_comm+        then global_tid + threads_per_segment * i+        else group_offset + ltid++    sWhen (tvExp glb_ind_var .<. n) $+      sComment "apply map function(s)" $+        body_cont $ \all_red_res -> do+          let maps_res = chunks (map (length . slugNeutral) slugs) all_red_res++          forM2_ slugs maps_res $ \slug map_res ->+            sLoopNest (slugShape slug) $ \vec_is -> do+              let (acc_params, next_params) = slugSplitParams slug+              sComment "load accumulator(s)" $+                forM2_ acc_params (slugAccs slug) $ \p (acc, acc_is) ->+                  copyDWIMFix (paramName p) [] (Var acc) (acc_is ++ vec_is)+              sComment "load next value(s)" $+                forM2_ next_params map_res $ \p (res, res_is) ->+                  copyDWIMFix (paramName p) [] res (res_is ++ vec_is)+              sComment "apply reduction operator(s)"+                $ compileStms mempty (bodyStms $ slugBody slug)+                $ sComment "store in accumulator(s)"+                $ forM2_+                  (slugAccs slug)+                  (map resSubExp $ bodyResult $ slugBody slug)+                $ \(acc, acc_is) se ->+                  copyDWIMFix acc (acc_is ++ vec_is) se []++    unless is_comm doGroupReduce+  sOp $ Imp.ErrorSync Imp.FenceLocal+  when is_comm doGroupReduce++noncommPrimParamsStageOneBody ::+  [SegBinOpSlug] ->+  DoSegBody ->+  TV Int64 ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  InKernelGen () ->+  InKernelGen ()+noncommPrimParamsStageOneBody slugs body_cont glb_ind_var global_tid q n chunk doLMemGroupReduce = do+  constants <- kernelConstants <$> askEnv+  let group_size = kernelGroupSize constants+      ltid = sExt64 $ kernelLocalThreadId constants++  -- this group's id within its designated segment; the stride made per group in+  -- the outer `i < q` loop; and this group's initial global offset.+  group_id_in_segment <- dPrimVE "group_offset_in_segment" $ global_tid `quot` group_size+  group_stride <- dPrimVE "group_stride" $ group_size * chunk+  group_base_offset <- dPrimVE "group_base_offset" $ group_id_in_segment * q * group_stride++  let chunkLoop = sFor "k" chunk++  sFor "i" q $ \i -> do+    -- group offset in this iteration.+    group_offset <- dPrimVE "group_offset" $ group_base_offset + i * group_stride+    chunkLoop $ \k -> do+      loc_ind <- dPrimVE "loc_ind" $ k * group_size + ltid+      glb_ind_var <-- group_offset + loc_ind++      sIf+        (tvExp glb_ind_var .<. n)+        ( body_cont $ \all_red_res -> do+            let slugs_res = chunks (map (length . slugNeutral) slugs) all_red_res+            forM2_ slugs slugs_res $ \slug slug_res -> do+              let priv_chunks = slugPrivChunks slug+              sComment "write map result(s) to private chunk(s)" $+                forM2_ priv_chunks slug_res $ \priv_chunk (res, res_is) ->+                  copyDWIMFix priv_chunk [k] res res_is+        )+        -- if out of bounds, fill chunk(s) with neutral element(s)+        ( forM_ slugs $ \slug ->+            forM2_ (slugPrivChunks slug) (slugNeutral slug) $+              \priv_chunk ne ->+                copyDWIMFix priv_chunk [k] ne []+        )++    sOp $ Imp.ErrorSync Imp.FenceLocal++    sComment "effectualize collective copies in local memory" $ do+      forM_ slugs $ \slug -> do+        let coll_copy_arrs = slugCollCopyArrs slug+        let priv_chunks = slugPrivChunks slug++        forM2_ coll_copy_arrs priv_chunks $ \lmem_arr priv_chunk -> do+          chunkLoop $ \k -> do+            lmem_idx <- dPrimVE "lmem_idx" $ ltid + k * group_size+            copyDWIMFix lmem_arr [lmem_idx] (Var priv_chunk) [k]++          sOp $ Imp.Barrier Imp.FenceLocal++          chunkLoop $ \k -> do+            lmem_idx <- dPrimVE "lmem_idx" $ ltid * chunk + k+            copyDWIMFix priv_chunk [k] (Var lmem_arr) [lmem_idx]++          sOp $ Imp.Barrier Imp.FenceLocal++    sComment "per-thread sequential reduction of private chunk(s)" $ do+      chunkLoop $ \k ->+        forM_ slugs $ \slug -> do+          let accs = map fst $ slugAccs slug+          let (acc_ps, next_ps) = slugSplitParams slug+          let ps_accs_chunks = zip4 acc_ps next_ps accs (slugPrivChunks slug)++          sComment "load params for all reductions" $ do+            forM_ ps_accs_chunks $ \(acc_p, next_p, acc, priv_chunk) -> do+              copyDWIM (paramName acc_p) [] (Var acc) []+              copyDWIMFix (paramName next_p) [] (Var priv_chunk) [k]++          sComment "apply reduction operator(s)" $ do+            let binop_ress = map resSubExp $ bodyResult $ slugBody slug+            compileStms mempty (bodyStms $ slugBody slug) $+              forM2_ accs binop_ress $ \acc binop_res ->+                copyDWIM acc [] binop_res []+    doLMemGroupReduce+  sOp $ Imp.ErrorSync Imp.FenceLocal++reductionStageTwo ::+  [PatElem LetDecMem] ->+  Imp.TExp Int32 ->+  [Imp.TExp Int64] ->+  Imp.TExp Int64 ->+  Imp.TExp Int64 ->+  SegBinOpSlug ->+  Lambda GPUMem ->+  VName ->+  VName ->+  Imp.TExp Int64 ->+  InKernelGen ()+reductionStageTwo segred_pes group_id segment_gtids first_group_for_segment groups_per_segment slug new_lambda counters sync_arr counter_idx = do+  constants <- kernelConstants <$> askEnv++  let ltid32 = kernelLocalThreadId constants+      ltid = sExt64 ltid32+      group_size = kernelGroupSize constants++  let (acc_params, next_params) = slugSplitParams slug+  let nes = slugNeutral slug+  let red_arrs = slugGroupRedArrs slug+  let group_res_arrs = groupResArrs slug++  old_counter <- dPrim "old_counter" int32+  (counter_mem, _, counter_offset) <-+    fullyIndexArray+      counters+      [counter_idx]+  sComment "first thread in group saves group result to global memory" $+    sWhen (ltid32 .==. 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++  sOp $ Imp.Barrier Imp.FenceGlobal++  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 (ltid32 .==. 0) $+      sOp $+        Imp.Atomic DefaultSpace $+          Imp.AtomicAdd Int32 (tvVar old_counter) counter_mem counter_offset $+            untyped $+              negate groups_per_segment++    sLoopNest (slugShape slug) $ \vec_is -> do+      unless (null $ slugShape slug) $+        sOp (Imp.Barrier Imp.FenceLocal)++      -- 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.+      --+      -- TODO: here we *could* insert a collective copy of the num_groups+      -- per-group results. However, it may not be beneficial, since num_groups+      -- is not necessarily larger than group_size, meaning the number of+      -- uncoalesced reads here may be insignificant. In fact, if we happen to+      -- have a num_groups < group_size, then the collective copy would add+      -- unnecessary overhead. Also, this code is only executed by a single+      -- group.+      sComment "read in the per-group-results" $ do+        read_per_thread <-+          dPrimVE "read_per_thread" $+            groups_per_segment `divUp` sExt64 group_size++        forM2_ acc_params nes $ \p ne ->+          copyDWIM (paramName p) [] ne []++        sFor "i" read_per_thread $ \i -> do+          group_res_id <-+            dPrimVE "group_res_id" $+              ltid * 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+            forM2_ next_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) $+              forM2_ acc_params (map resSubExp $ bodyResult $ slugBody slug) $ \p se ->+                copyDWIMFix (paramName p) [] se []++      forM2_ acc_params red_arrs $ \p arr ->+        when (isPrimParam p) $+          copyDWIMFix arr [ltid] (Var $ paramName p) []++      sOp $ Imp.ErrorSync Imp.FenceLocal++      sComment "reduce the per-group results" $ do+        groupReduce (sExt32 group_size) new_lambda red_arrs++        sComment "and back to memory with the final result" $+          sWhen (ltid32 .==. 0) $+            forM2_ segred_pes (lambdaParams new_lambda) $ \pe p ->+              copyDWIMFix+                (patElemName pe)+                (segment_gtids ++ vec_is)+                (Var $ paramName p)+                []++-- Note [IntermArrays]+--+-- Intermediate memory for the nonsegmented and large segments non-commutative+-- reductions with all primitive parameters:+--+--   These kernels need local memory for 1) the initial collective copy, 2) the+--   (virtualized) group reductions, and (TODO: this one not implemented yet!)+--   3) the final single-group collective copy. There are no dependencies+--   between these three stages, so we can reuse the same pool of local mem for+--   all three. These intermediates all go into local mem because of the+--   assumption of primitive parameter types.+--+--   Let `elem_sizes` be a list of element type sizes for the reduction+--   operators in a given redomap fusion. Then the amount of local mem needed+--   across the three steps are:+--+--   1) The initial collective copy from global to thread-private memory+--   requires `group_size * CHUNK * max elem_sizes`, since the collective copies+--   are performed in sequence (ie. inputs to different reduction operators need+--   not be held in local mem simultaneously).+--   2) The intra-group reductions of local memory held per-thread results+--   require `group_size * sum elem_sizes` bytes, since per-thread results for+--   all fused reductions are group-reduced simultaneously.+--   3) If group_size < num_groups, then after the final single-group collective+--   copy, a thread-sequential reduction reduces the number of per-group partial+--   results from num_groups down to group_size for each reduction array, such+--   that they will each fit in the final intra-group reduction. This requires+--   `num_groups * max elem_sizes`.+--+--   In summary, the total amount of local mem needed is the maximum between:+--   1) initial collective copy: group_size * CHUNK * max elem_sizes+--   2) intra-group reductions:  group_size * sum elem_sizes+--   3) final collective copy:   num_groups * max elem_sizes+--+--   The amount of local mem will most likely be decided by 1) in most cases,+--   unless the number of fused operators is very high *or* if we have a+--   `num_groups > group_size * CHUNK`, but this is unlikely, in which case 2)+--   and 3), respectively, will dominate.+--+--   Aside from local memory, these kernels also require a CHUNK-sized array of+--   thread-private register memory per reduction operator.+--+-- For all other reductions, ie. commutative reductions, reductions with at+-- least one non-primitive operator, and small segments reductions:+--+--   These kernels use local memory only for the intra-group reductions, and+--   since they do not use chunking or CHUNK, they all require onlly `group_size+--   * max elem_sizes` bytes of local memory and no thread-private register mem.
src/Futhark/CodeGen/ImpGen/GPU/SegScan/SinglePass.hs view
@@ -7,6 +7,7 @@  import Control.Monad import Data.List (zip4, zip7)+import Data.Map qualified as M import Data.Maybe import Futhark.CodeGen.ImpCode.GPU qualified as Imp import Futhark.CodeGen.ImpGen@@ -15,7 +16,7 @@ import Futhark.IR.Mem.LMAD qualified as LMAD import Futhark.Transform.Rename import Futhark.Util (mapAccumLM, takeLast)-import Futhark.Util.IntegralExp (IntegralExp (mod, rem), divUp, quot)+import Futhark.Util.IntegralExp (IntegralExp (mod, rem), divUp, nextMul, quot) import Prelude hiding (mod, quot, rem)  xParams, yParams :: SegBinOp GPUMem -> [LParam GPUMem]@@ -24,9 +25,6 @@ yParams scan =   drop (length (segBinOpNeutral scan)) (lambdaParams (segBinOpLambda scan)) -alignTo :: (IntegralExp a) => a -> a -> a-alignTo x a = (x `divUp` a) * a- createLocalArrays ::   Count GroupSize SubExp ->   SubExp ->@@ -36,7 +34,7 @@   let groupSizeE = pe64 groupSize       workSize = pe64 chunk * groupSizeE       prefixArraysSize =-        foldl (\acc tySize -> alignTo acc tySize + tySize * groupSizeE) 0 $+        foldl (\acc tySize -> nextMul acc tySize + tySize * groupSizeE) 0 $           map primByteSize types       maxTransposedArraySize =         foldl1 sMax64 $ map (\ty -> workSize * primByteSize ty) types@@ -44,7 +42,7 @@       warpSize :: (Num a) => a       warpSize = 32       maxWarpExchangeSize =-        foldl (\acc tySize -> alignTo acc tySize + tySize * fromInteger warpSize) 0 $+        foldl (\acc tySize -> nextMul acc tySize + tySize * fromInteger warpSize) 0 $           map primByteSize types       maxLookbackSize = maxWarpExchangeSize + warpSize       size = Imp.bytes $ maxLookbackSize `sMax64` prefixArraysSize `sMax64` maxTransposedArraySize@@ -52,7 +50,7 @@   (_, byteOffsets) <-     mapAccumLM       ( \off tySize -> do-          off' <- dPrimVE "byte_offsets" $ alignTo off tySize + pe64 groupSize * tySize+          off' <- dPrimVE "byte_offsets" $ nextMul off tySize + pe64 groupSize * tySize           pure (off', off)       )       0@@ -61,7 +59,7 @@   (_, warpByteOffsets) <-     mapAccumLM       ( \off tySize -> do-          off' <- dPrimVE "warp_byte_offset" $ alignTo off tySize + warpSize * tySize+          off' <- dPrimVE "warp_byte_offset" $ nextMul off tySize + warpSize * tySize           pure (off', off)       )       warpSize@@ -208,12 +206,10 @@       | otherwise =           copyDWIMFix (paramName p) [] (Var arr) [gtid - behind + arrs_full_size] -    writeResult x y arr-      | primType $ paramType x = do-          copyDWIMFix arr [ltid] (Var $ paramName x) []-          copyDWIM (paramName y) [] (Var $ paramName x) []-      | otherwise =-          copyDWIM (paramName y) [] (Var $ paramName x) []+    writeResult x y arr = do+      when (isPrimParam x) $+        copyDWIMFix arr [ltid] (Var $ paramName x) []+      copyDWIM (paramName y) [] (Var $ paramName x) []  -- | Compile 'SegScan' instance to host-level code with calls to a -- single-pass kernel.@@ -232,27 +228,17 @@        n = product $ map pe64 $ segSpaceDims space -      tys = map (\(Prim pt) -> pt) $ lambdaReturnType $ segBinOpLambda scan_op-      tys_sizes = map primByteSize tys--      sumT, maxT :: Integer-      sumT = sum tys_sizes-      sumT' = sum (map (max 4 . primByteSize) tys) `div` 4-      maxT = maximum tys_sizes--      -- TODO: Make these constants dynamic by querying device-      k_reg = 64-      k_mem = 95--      mem_constraint = max k_mem sumT `div` maxT-      reg_constraint = (k_reg - 1 - sumT') `div` (2 * sumT')+      tys' = lambdaReturnType $ segBinOpLambda scan_op -      chunk :: (Num a) => a-      chunk = fromIntegral $ max 1 $ min mem_constraint reg_constraint+      tys = map elemType tys'        group_size_e = pe64 $ unCount $ kAttrGroupSize attrs       num_physgroups_e = pe64 $ unCount $ kAttrNumGroups attrs +  let chunk_const = getChunkSize tys'+  chunk_v <- dPrimV "chunk_size" . isInt64 =<< kernelConstToExp chunk_const+  let chunk = tvExp chunk_v+   num_virtgroups <-     tvSize <$> dPrimV "num_virtgroups" (n `divUp` (group_size_e * chunk))   let num_virtgroups_e = pe64 num_virtgroups@@ -266,11 +252,7 @@       not_segmented_e = fromBool $ not segmented       segment_size = last dims' -  let debug_ s v = emit $ Imp.DebugPrint s $ Just $ untyped (v :: Imp.TExp Int32)-  debug_ "Sequential elements per thread (chunk) " chunk-  debug_ "Memory constraint" $ fromIntegral mem_constraint-  debug_ "Register constraint" $ fromIntegral reg_constraint-  debug_ "sumT'" $ fromIntegral sumT'+  emit $ Imp.DebugPrint "Sequential elements per thread (chunk)" $ Just $ untyped chunk    statusFlags <- sAllocArray "status_flags" int8 (Shape [num_virtgroups]) (Space "device")   sReplicate statusFlags $ intConst Int8 statusX@@ -284,11 +266,18 @@    global_id <- genZeroes "global_dynid" 1 -  sKernelThread "segscan" (segFlat space) attrs $ do+  let attrs' = attrs {kAttrConstExps = M.singleton (tvVar chunk_v) chunk_const}++  sKernelThread "segscan" (segFlat space) attrs' $ do+    chunk32 <- dPrimVE "chunk_size_32b" $ sExt32 $ tvExp chunk_v+     constants <- kernelConstants <$> askEnv +    let ltid32 = kernelLocalThreadId constants+        ltid = sExt64 ltid32+     (sharedId, transposedArrays, prefixArrays, warpscan, exchanges) <--      createLocalArrays (kAttrGroupSize attrs) (intConst Int64 chunk) tys+      createLocalArrays (kAttrGroupSize attrs) (tvSize chunk_v) tys      -- We wrap the entire kernel body in a virtualisation loop to handle the     -- case where we do not have enough workgroups to cover the iteration space.@@ -308,7 +297,7 @@     sFor "virtloop_i" iters $ const $ do       dyn_id <- dPrim "dynamic_id" int32       sComment "First thread in block fetches this block's dynamic_id" $ do-        sWhen (kernelLocalThreadId constants .==. 0) $ do+        sWhen (ltid32 .==. 0) $ do           (globalIdMem, _, globalIdOff) <- fullyIndexArray global_id [0]           sOp $             Imp.Atomic DefaultSpace $@@ -333,34 +322,32 @@       copyDWIMFix (tvVar dyn_id) [] (Var sharedId) [0]       sOp local_barrier -      blockOff <--        dPrimV "blockOff" $-          sExt64 (tvExp dyn_id) * chunk * group_size_e -- kernelGroupSize constants-      sgmIdx <- dPrimVE "sgm_idx" $ tvExp blockOff `mod` segment_size+      block_offset <-+        dPrimVE "block_offset" $+          sExt64 (tvExp dyn_id) * chunk * group_size_e+      sgm_idx <- dPrimVE "sgm_idx" $ block_offset `mod` segment_size       boundary <-         dPrimVE "boundary" $           sExt32 $-            sMin64 (chunk * group_size_e) (segment_size - sgmIdx)+            sMin64 (chunk * group_size_e) (segment_size - sgm_idx)       segsize_compact <-         dPrimVE "segsize_compact" $           sExt32 $             sMin64 (chunk * group_size_e) segment_size-      privateArrays <-+      private_chunks <-         forM tys $ \ty ->           sAllocArray             "private"             ty-            (Shape [intConst Int64 chunk])-            (ScalarSpace [intConst Int64 chunk] ty)+            (Shape [tvSize chunk_v])+            (ScalarSpace [tvSize chunk_v] ty) +      thd_offset <- dPrimVE "thd_offset" $ block_offset + ltid+       sComment "Load and map" $         sFor "i" chunk $ \i -> do           -- The map's input index-          virt_tid <--            dPrimVE "virt_tid" $-              tvExp blockOff-                + sExt64 (kernelLocalThreadId constants)-                + i * kernelGroupSize constants+          virt_tid <- dPrimVE "virt_tid" $ thd_offset + i * group_size_e           dIndexSpace (zip gtids dims') virt_tid           -- Perform the map           let in_bounds =@@ -373,66 +360,62 @@                     copyDWIMFix (patElemName dest) (map Imp.le64 gtids) (kernelResultSubExp src) []                    -- Write to-scan results to private memory.-                  forM_ (zip privateArrays $ map kernelResultSubExp all_scan_res) $ \(dest, src) ->+                  forM_ (zip private_chunks $ map kernelResultSubExp all_scan_res) $ \(dest, src) ->                     copyDWIMFix dest [i] src []                out_of_bounds =-                forM_ (zip privateArrays scanop_nes) $ \(dest, ne) ->+                forM_ (zip private_chunks scanop_nes) $ \(dest, ne) ->                   copyDWIMFix dest [i] ne []            sIf (virt_tid .<. n) in_bounds out_of_bounds        sOp $ Imp.ErrorSync Imp.FenceLocal       sComment "Transpose scan inputs" $ do-        forM_ (zip transposedArrays privateArrays) $ \(trans, priv) -> do+        forM_ (zip transposedArrays private_chunks) $ \(trans, priv) -> do           sFor "i" chunk $ \i -> do-            sharedIdx <--              dPrimVE "sharedIdx" $-                sExt64 (kernelLocalThreadId constants)-                  + i * kernelGroupSize constants+            sharedIdx <- dPrimVE "sharedIdx" $ ltid + i * group_size_e             copyDWIMFix trans [sharedIdx] (Var priv) [i]           sOp local_barrier           sFor "i" chunk $ \i -> do-            sharedIdx <- dPrimV "sharedIdx" $ kernelLocalThreadId constants * chunk + i+            sharedIdx <- dPrimV "sharedIdx" $ ltid * chunk + i             copyDWIMFix priv [sExt64 i] (Var trans) [sExt64 $ tvExp sharedIdx]           sOp local_barrier        sComment "Per thread scan" $ do         -- We don't need to touch the first element, so only m-1         -- iterations here.-        globalIdx <--          dPrimVE "gidx" $-            (kernelLocalThreadId constants * chunk) + 1         sFor "i" (chunk - 1) $ \i -> do           let xs = map paramName $ xParams scan_op               ys = map paramName $ yParams scan_op           -- determine if start of segment           new_sgm <-             if segmented-              then dPrimVE "new_sgm" $ (globalIdx + sExt32 i - boundary) `mod` segsize_compact .==. 0+              then do+                gidx <- dPrimVE "gidx" $ (ltid32 * chunk32) + 1+                dPrimVE "new_sgm" $ (gidx + sExt32 i - boundary) `mod` segsize_compact .==. 0               else pure false           -- skip scan of first element in segment           sUnless new_sgm $ do-            forM_ (zip4 privateArrays xs ys tys) $ \(src, x, y, ty) -> do+            forM_ (zip4 private_chunks xs ys tys) $ \(src, x, y, ty) -> do               dPrim_ x ty               dPrim_ y ty               copyDWIMFix x [] (Var src) [i]               copyDWIMFix y [] (Var src) [i + 1]              compileStms mempty (bodyStms $ lambdaBody $ segBinOpLambda scan_op) $-              forM_ (zip privateArrays $ map resSubExp $ bodyResult $ lambdaBody $ segBinOpLambda scan_op) $ \(dest, res) ->+              forM_ (zip private_chunks $ map resSubExp $ bodyResult $ lambdaBody $ segBinOpLambda scan_op) $ \(dest, res) ->                 copyDWIMFix dest [i + 1] res []        sComment "Publish results in shared memory" $ do-        forM_ (zip prefixArrays privateArrays) $ \(dest, src) ->-          copyDWIMFix dest [sExt64 $ kernelLocalThreadId constants] (Var src) [chunk - 1]+        forM_ (zip prefixArrays private_chunks) $ \(dest, src) ->+          copyDWIMFix dest [ltid] (Var src) [chunk - 1]         sOp local_barrier        let crossesSegment = do             guard segmented             Just $ \from to ->-              let from' = (from + 1) * chunk - 1-                  to' = (to + 1) * chunk - 1+              let from' = (from + 1) * chunk32 - 1+                  to' = (to + 1) * chunk32 - 1                in (to' - from') .>. (to' + segsize_compact - boundary) `mod` segsize_compact        scan_op1 <- renameLambda $ segBinOpLambda scan_op@@ -441,19 +424,19 @@       sComment "Scan results (with warp scan)" $ do         groupScan           crossesSegment+          group_size_e           num_virt_threads-          (kernelGroupSize constants)           scan_op1           prefixArrays          sOp $ Imp.ErrorSync Imp.FenceLocal          let firstThread acc prefixes =-              copyDWIMFix (tvVar acc) [] (Var prefixes) [sExt64 (kernelGroupSize constants) - 1]+              copyDWIMFix (tvVar acc) [] (Var prefixes) [sExt64 group_size_e - 1]             notFirstThread acc prefixes =-              copyDWIMFix (tvVar acc) [] (Var prefixes) [sExt64 (kernelLocalThreadId constants) - 1]+              copyDWIMFix (tvVar acc) [] (Var prefixes) [ltid - 1]         sIf-          (kernelLocalThreadId constants .==. 0)+          (ltid32 .==. 0)           (zipWithM_ firstThread accs prefixArrays)           (zipWithM_ notFirstThread accs prefixArrays) @@ -461,9 +444,9 @@        prefixes <- forM (zip scanop_nes tys) $ \(ne, ty) ->         dPrimV "prefix" $ TPrimExp $ toExp' ty ne-      blockNewSgm <- dPrimVE "block_new_sgm" $ sgmIdx .==. 0+      blockNewSgm <- dPrimVE "block_new_sgm" $ sgm_idx .==. 0       sComment "Perform lookback" $ do-        sWhen (blockNewSgm .&&. kernelLocalThreadId constants .==. 0) $ do+        sWhen (blockNewSgm .&&. ltid32 .==. 0) $ do           everythingVolatile $             forM_ (zip accs incprefixArrays) $ \(acc, incprefixArray) ->               copyDWIMFix incprefixArray [tvExp dyn_id] (tvSize acc) []@@ -475,8 +458,8 @@         -- end sWhen          let warpSize = kernelWaveSize constants-        sWhen (bNot blockNewSgm .&&. kernelLocalThreadId constants .<. warpSize) $ do-          sWhen (kernelLocalThreadId constants .==. 0) $ do+        sWhen (bNot blockNewSgm .&&. ltid32 .<. warpSize) $ do+          sWhen (ltid32 .==. 0) $ do             sIf               (not_segmented_e .||. boundary .==. sExt32 (group_size_e * chunk))               ( do@@ -505,7 +488,7 @@            sIf             (tvExp status .==. statusP)-            ( sWhen (kernelLocalThreadId constants .==. 0) $+            ( sWhen (ltid32 .==. 0) $                 everythingVolatile $                   forM_ (zip prefixes incprefixArrays) $ \(prefix, incprefixArray) ->                     copyDWIMFix (tvVar prefix) [] (Var incprefixArray) [tvExp dyn_id - 1]@@ -518,11 +501,11 @@                 let loopStop = warpSize * (-1)                     sameSegment readIdx                       | segmented =-                          let startIdx = sExt64 (tvExp readIdx + 1) * kernelGroupSize constants * chunk - 1-                           in tvExp blockOff - startIdx .<=. sgmIdx+                          let startIdx = sExt64 (tvExp readIdx + 1) * group_size_e * chunk - 1+                           in block_offset - startIdx .<=. sgm_idx                       | otherwise = true                 sWhile (tvExp readOffset .>. loopStop) $ do-                  readI <- dPrimV "read_i" $ tvExp readOffset + kernelLocalThreadId constants+                  readI <- dPrimV "read_i" $ tvExp readOffset + ltid32                   aggrs <- forM (zip scanop_nes tys) $ \(ne, ty) ->                     dPrimV "aggr" $ TPrimExp $ toExp' ty ne                   flag <- dPrimV "flag" (statusX :: Imp.TExp Int8)@@ -546,12 +529,12 @@                   -- end sWhen                    forM_ (zip exchanges aggrs) $ \(exchange, aggr) ->-                    copyDWIMFix exchange [sExt64 $ kernelLocalThreadId constants] (tvSize aggr) []-                  copyDWIMFix warpscan [sExt64 $ kernelLocalThreadId constants] (tvSize flag) []+                    copyDWIMFix exchange [ltid] (tvSize aggr) []+                  copyDWIMFix warpscan [ltid] (tvSize flag) []                    -- execute warp-parallel reduction but only if the last read flag in not STATUS_P                   copyDWIMFix (tvVar flag) [] (Var warpscan) [sExt64 warpSize - 1]-                  sWhen (tvExp flag .<. (2 :: Imp.TExp Int8)) $ do+                  sWhen (tvExp flag .<. statusP) $ do                     lam' <- renameLambda scan_op1                     inBlockScanLookback                       constants@@ -573,7 +556,7 @@                     )                    -- update prefix if flag different than STATUS_X:-                  sWhen (tvExp flag .>. (statusX :: Imp.TExp Int8)) $ do+                  sWhen (tvExp flag .>. statusX) $ do                     lam <- renameLambda scan_op1                     let (xs, ys) = splitAt (length tys) $ map paramName $ lambdaParams lam                     forM_ (zip xs aggrs) $ \(x, aggr) -> dPrimV_ x (tvExp aggr)@@ -586,7 +569,7 @@            -- end sWhile           -- end sIf-          sWhen (kernelLocalThreadId constants .==. 0) $ do+          sWhen (ltid32 .==. 0) $ do             scan_op2 <- renameLambda scan_op1             let xs = map paramName $ take (length tys) $ lambdaParams scan_op2                 ys = map paramName $ drop (length tys) $ lambdaParams scan_op2@@ -629,7 +612,7 @@             dPrimV_ y' $ tvExp acc          sIf-          (kernelLocalThreadId constants * chunk .<. boundary .&&. bNot blockNewSgm)+          (ltid32 * chunk32 .<. boundary .&&. bNot blockNewSgm)           ( compileStms mempty (bodyStms $ lambdaBody scan_op4) $               forM_ (zip3 xs tys $ map resSubExp $ bodyResult $ lambdaBody scan_op4) $                 \(x, ty, res) -> x <~~ toExp' ty res@@ -639,38 +622,34 @@         -- elements left before new segment.         stop <-           dPrimVE "stopping_point" $-            segsize_compact - (kernelLocalThreadId constants * chunk - 1 + segsize_compact - boundary) `rem` segsize_compact+            segsize_compact - (ltid32 * chunk32 - 1 + segsize_compact - boundary) `rem` segsize_compact         sFor "i" chunk $ \i -> do           sWhen (sExt32 i .<. stop - 1) $ do-            forM_ (zip privateArrays ys) $ \(src, y) ->+            forM_ (zip private_chunks ys) $ \(src, y) ->               -- only include prefix for the first segment part per thread               copyDWIMFix y [] (Var src) [i]             compileStms mempty (bodyStms $ lambdaBody scan_op3) $-              forM_ (zip privateArrays $ map resSubExp $ bodyResult $ lambdaBody scan_op3) $+              forM_ (zip private_chunks $ map resSubExp $ bodyResult $ lambdaBody scan_op3) $                 \(dest, res) ->                   copyDWIMFix dest [i] res []        sComment "Transpose scan output and Write it to global memory in coalesced fashion" $ do-        forM_ (zip3 transposedArrays privateArrays $ map patElemName all_pes) $ \(locmem, priv, dest) -> do+        forM_ (zip3 transposedArrays private_chunks $ map patElemName all_pes) $ \(locmem, priv, dest) -> do           -- sOp local_barrier           sFor "i" chunk $ \i -> do             sharedIdx <-               dPrimV "sharedIdx" $-                sExt64 (kernelLocalThreadId constants * chunk) + i+                sExt64 (ltid * chunk) + i             copyDWIMFix locmem [tvExp sharedIdx] (Var priv) [i]           sOp local_barrier           sFor "i" chunk $ \i -> do-            flat_idx <--              dPrimVE "flat_idx" $-                tvExp blockOff-                  + kernelGroupSize constants * i-                  + sExt64 (kernelLocalThreadId constants)+            flat_idx <- dPrimVE "flat_idx" $ thd_offset + i * group_size_e             dIndexSpace (zip gtids dims') flat_idx             sWhen (flat_idx .<. n) $ do               copyDWIMFix                 dest                 (map Imp.le64 gtids)                 (Var locmem)-                [sExt64 $ flat_idx - tvExp blockOff]+                [sExt64 $ flat_idx - block_offset]           sOp local_barrier {-# NOINLINE compileSegScan #-}
src/Futhark/CodeGen/ImpGen/GPU/ToOpenCL.hs view
@@ -10,7 +10,6 @@ where  import Control.Monad-import Control.Monad.Identity import Control.Monad.Reader import Control.Monad.State import Data.Bifunctor (second)@@ -58,7 +57,7 @@ translateGPU target prog =   let env = envFromProg prog       ( prog',-        ToOpenCL kernels device_funs used_types sizes failures+        ToOpenCL kernels device_funs used_types sizes failures constants         ) =           (`runState` initialOpenCL) . (`runReaderT` env) $ do             let ImpGPU.Definitions@@ -86,13 +85,15 @@             T.unlines device_defs           ]    in ImpOpenCL.Program-        opencl_code-        opencl_prelude-        kernels'-        (S.toList used_types)-        (findParamUsers env prog' (cleanSizes sizes))-        failures-        prog'+        { openClProgram = opencl_code,+          openClPrelude = opencl_prelude,+          openClMacroDefs = constants,+          openClKernelNames = kernels',+          openClUsedTypes = S.toList used_types,+          openClParams = findParamUsers env prog' (cleanSizes sizes),+          openClFailures = failures,+          hostDefinitions = prog'+        }   where     genPrelude TargetOpenCL = genOpenClPrelude     genPrelude TargetCUDA = const genCUDAPrelude@@ -172,11 +173,12 @@     clDevFuns :: M.Map Name (C.Definition, T.Text),     clUsedTypes :: S.Set PrimType,     clSizes :: M.Map Name SizeClass,-    clFailures :: [FailureMsg]+    clFailures :: [FailureMsg],+    clConstants :: [(Name, KernelConstExp)]   }  initialOpenCL :: ToOpenCL-initialOpenCL = ToOpenCL mempty mempty mempty mempty mempty+initialOpenCL = ToOpenCL mempty mempty mempty mempty mempty mempty  data Env = Env   { envFuns :: ImpGPU.Functions ImpGPU.HostOp,@@ -331,7 +333,7 @@ isConst :: GroupDim -> Maybe T.Text isConst (Left (ValueExp (IntValue x))) =   Just $ prettyText $ intToInt64 x-isConst (Right (SizeConst v)) =+isConst (Right (SizeConst v _)) =   Just $ zEncodeText $ nameToText v isConst (Right (SizeMaxConst size_class)) =   Just $ "max_" <> prettyText size_class@@ -354,7 +356,8 @@           mapM_ GC.item body       kstate = GC.compUserState cstate -      (const_defs, const_undefs) = unzip $ mapMaybe constDef $ kernelUses kernel+      (kernel_consts, (const_defs, const_undefs)) =+        second unzip $ unzip $ mapMaybe (constDef (kernelName kernel)) $ kernelUses kernel    let (local_memory_bytes, (local_memory_params, local_memory_args, local_memory_init)) =         second unzip3 $@@ -458,7 +461,8 @@     s       { clGPU = M.insert name (safety, kernel_fun) $ clGPU s,         clUsedTypes = typesInKernel kernel <> clUsedTypes s,-        clFailures = kernelFailures kstate+        clFailures = kernelFailures kstate,+        clConstants = kernel_consts <> clConstants s       }    -- The error handling stuff is automatically added later.@@ -506,17 +510,19 @@ -- 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) =+constDef :: Name -> KernelUse -> Maybe ((Name, KernelConstExp), (C.BlockItem, C.BlockItem))+constDef kernel_name (ConstUse v e) =   Just-    ( [C.citem|$escstm:(T.unpack def)|],-      [C.citem|$escstm:(T.unpack undef)|]+    ( (nameFromText v', e),+      ( [C.citem|$escstm:(T.unpack def)|],+        [C.citem|$escstm:(T.unpack undef)|]+      )     )   where-    e' = compilePrimExp e-    def = "#define " <> idText (C.toIdent v mempty) <> " (" <> expText e' <> ")"+    v' = zEncodeText $ nameToText kernel_name <> "." <> prettyText v+    def = "#define " <> idText (C.toIdent v mempty) <> " (" <> v' <> ")"     undef = "#undef " <> idText (C.toIdent v mempty)-constDef _ = Nothing+constDef _ _ = Nothing  commonPrelude :: T.Text commonPrelude =@@ -549,14 +555,6 @@   "#define FUTHARK_HIP\n"     <> preludeCU     <> commonPrelude--compilePrimExp :: PrimExp KernelConst -> C.Exp-compilePrimExp e = runIdentity $ GC.compilePrimExp compileKernelConst e-  where-    compileKernelConst (SizeConst key) =-      pure [C.cexp|$id:(zEncodeText (prettyText key))|]-    compileKernelConst (SizeMaxConst size_class) =-      pure [C.cexp|$id:("max_" <> prettyString size_class)|]  kernelArgs :: Kernel -> [KernelArg] kernelArgs = mapMaybe useToArg . kernelUses
src/Futhark/CodeGen/ImpGen/Multicore/Base.hs view
@@ -37,7 +37,6 @@ import Futhark.CodeGen.ImpGen import Futhark.Error import Futhark.IR.MCMem-import Futhark.MonadFreshNames import Futhark.Transform.Rename import Prelude hiding (quot, rem) 
src/Futhark/CodeGen/ImpGen/Multicore/SegHist.hs view
@@ -11,7 +11,6 @@ import Futhark.CodeGen.ImpGen.Multicore.Base import Futhark.CodeGen.ImpGen.Multicore.SegRed (compileSegRed') import Futhark.IR.MCMem-import Futhark.MonadFreshNames import Futhark.Transform.Rename (renameLambda) import Futhark.Util (chunks, splitFromEnd, takeLast) import Futhark.Util.IntegralExp (rem)
src/Futhark/Compiler/Program.hs view
@@ -155,7 +155,6 @@   -- 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.-  let filepath = includeToFilePath include   r <- contentsAndModTime filepath vfs   case (r, lookup prelude_str prelude) of     (Just (Right (s, mod_time)), _) ->@@ -167,6 +166,8 @@     (Nothing, Nothing) ->       pure $ Left $ ProgError loc $ pretty not_found   where+    filepath = includeToFilePath include+     prelude_str = "/" Posix.</> includeToString include Posix.<.> "fut"      loaded path s mod_time =@@ -178,7 +179,7 @@         }      not_found =-      "Could not find import " <> E.quote (includeToText include) <> "."+      "Could not read file " <> E.quote (T.pack filepath) <> "."  handleFile :: ReaderState -> VFS -> LoadedFile T.Text -> IO UncheckedImport handleFile state_mvar vfs (LoadedFile file_name import_name file_contents mod_time) = do
src/Futhark/Doc/Generator.hs view
@@ -124,7 +124,7 @@          forEnv env =           mconcat (map vname' $ M.toList $ envNameMap env)-            <> mconcat (map forMty $ M.elems $ envSigTable env)+            <> mconcat (map forMty $ M.elems $ envModTypeTable env)         forMod (ModEnv env) = forEnv env         forMod ModFun {} = mempty         forMty = forMod . mtyMod@@ -373,7 +373,7 @@  synopsisDec :: S.Set VName -> FileModule -> Dec -> Maybe (DocM Html) synopsisDec visible fm dec = case dec of-  SigDec s -> synopsisModType mempty s+  ModTypeDec s -> synopsisModType mempty s   ModDec m -> synopsisMod fm m   ValDec v -> synopsisValBind v   TypeDec t -> synopsisType t@@ -386,8 +386,8 @@           pure $             fullRow $               keyword "open" <> fromString (" <" <> prettyString x <> ">")-  LocalDec (SigDec s) _-    | sigName s `S.member` visible ->+  LocalDec (ModTypeDec s) _+    | modTypeName s `S.member` visible ->         synopsisModType (keyword "local" <> " ") s   LocalDec {} -> Nothing   ImportDec {} -> Nothing@@ -404,7 +404,7 @@     keyword "import "       <> (H.a ! A.href dest) (fromString (show (includeToString file))) synopsisOpened (ModAscript _ se _ _) = Just $ do-  se' <- synopsisSigExp se+  se' <- synopsisModTypeExp se   pure $ "... : " <> se' synopsisOpened _ = Nothing @@ -427,18 +427,18 @@       mconcat (intersperse " -> " $ params' ++ [rettype'])     ) -synopsisModType :: Html -> SigBind -> Maybe (DocM Html)+synopsisModType :: Html -> ModTypeBind -> Maybe (DocM Html) synopsisModType prefix sb = Just $ do-  let name' = vnameSynopsisDef $ sigName sb+  let name' = vnameSynopsisDef $ modTypeName sb   fullRow <$> do-    se' <- synopsisSigExp $ sigExp sb+    se' <- synopsisModTypeExp $ modTypeExp sb     pure $ prefix <> keyword "module type " <> name' <> " = " <> se'  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+    Nothing -> (proceed <=< envModType) <$> M.lookup name modtable+    Just (s, _) -> Just $ proceed =<< synopsisModTypeExp s   where     proceed sig' = do       let name' = vnameSynopsisDef name@@ -446,8 +446,8 @@       pure $ specRow (keyword "module " <> name') ": " (ps' <> sig')      FileModule _abs Env {envModTable = modtable} _ _ = fm-    envSig (ModEnv e) = renderEnv e-    envSig (ModFun (FunSig _ _ (MTy _ m))) = envSig m+    envModType (ModEnv e) = renderEnv e+    envModType (ModFun (FunModType _ _ (MTy _ m))) = envModType m  synopsisType :: TypeBind -> Maybe (DocM Html) synopsisType tb = Just $ do@@ -548,7 +548,7 @@ modParamHtml :: [ModParamBase Info VName] -> DocM Html modParamHtml [] = pure mempty modParamHtml (ModParam pname psig _ _ : mps) =-  liftM2 f (synopsisSigExp psig) (modParamHtml mps)+  liftM2 f (synopsisModTypeExp psig) (modParamHtml mps)   where     f se params =       "("@@ -558,26 +558,26 @@         <> ") -> "         <> params -synopsisSigExp :: SigExpBase Info VName -> DocM Html-synopsisSigExp e = case e of-  SigVar v _ _ -> qualNameHtml v-  SigParens e' _ -> parens <$> synopsisSigExp e'-  SigSpecs ss _ -> braces . (H.table ! A.class_ "specs") . mconcat <$> mapM synopsisSpec ss-  SigWith s (TypeRef v ps t _) _ -> do-    s' <- synopsisSigExp s+synopsisModTypeExp :: ModTypeExpBase Info VName -> DocM Html+synopsisModTypeExp e = case e of+  ModTypeVar v _ _ -> qualNameHtml v+  ModTypeParens e' _ -> parens <$> synopsisModTypeExp e'+  ModTypeSpecs ss _ -> braces . (H.table ! A.class_ "specs") . mconcat <$> mapM synopsisSpec ss+  ModTypeWith s (TypeRef v ps t _) _ -> do+    s' <- synopsisModTypeExp s     t' <- typeExpHtml t     v' <- qualNameHtml v     let ps' = mconcat $ map ((" " <>) . typeParamHtml) ps     pure $ s' <> keyword " with " <> v' <> ps' <> " = " <> t'-  SigArrow Nothing e1 e2 _ ->-    liftM2 f (synopsisSigExp e1) (synopsisSigExp e2)+  ModTypeArrow Nothing e1 e2 _ ->+    liftM2 f (synopsisModTypeExp e1) (synopsisModTypeExp e2)     where       f e1' e2' = e1' <> " -> " <> e2'-  SigArrow (Just v) e1 e2 _ ->+  ModTypeArrow (Just v) e1 e2 _ ->     do       let name = vnameHtml v-      e1' <- synopsisSigExp e1-      e2' <- noLink [v] $ synopsisSigExp e2+      e1' <- synopsisModTypeExp e1+      e2' <- noLink [v] $ synopsisModTypeExp e2       pure $ "(" <> name <> ": " <> e1' <> ") -> " <> e2'  keyword :: String -> Html@@ -625,8 +625,8 @@         (mconcat (map (" " <>) tparams') <> ": ")         rettype'   ModSpec name sig _ _ ->-    specRow (keyword "module " <> vnameSynopsisDef name) ": " <$> synopsisSigExp sig-  IncludeSpec e _ -> fullRow . (keyword "include " <>) <$> synopsisSigExp e+    specRow (keyword "module " <> vnameSynopsisDef name) ": " <$> synopsisModTypeExp sig+  IncludeSpec e _ -> fullRow . (keyword "include " <>) <$> synopsisModTypeExp e  typeExpHtml :: TypeExp Info VName -> DocM Html typeExpHtml e = case e of@@ -802,7 +802,7 @@ describeGenericMod ::   VName ->   IndexWhat ->-  SigExp ->+  ModTypeExp ->   Maybe DocComment ->   (Html -> DocM Html) ->   DocM Html@@ -812,7 +812,7 @@   decl_type <- f name'    doc' <- case se of-    SigSpecs specs _ -> (<>) <$> docHtml doc <*> describeSpecs specs+    ModTypeSpecs specs _ -> (<>) <$> docHtml doc <*> describeSpecs specs     _ -> docHtml doc    let decl_doc = H.dd ! A.class_ "desc_doc" $ doc'@@ -837,14 +837,14 @@ describeDec _ (TypeDec vb) =   Just $     describeGeneric (typeAlias vb) IndexType (typeDoc vb) (`typeBindHtml` vb)-describeDec _ (SigDec (SigBind name se doc _)) = Just $+describeDec _ (ModTypeDec (ModTypeBind name se doc _)) = Just $   describeGenericMod name IndexModuleType se doc $ \name' ->     pure $ keyword "module type " <> name' describeDec _ (ModDec mb) = Just $   describeGeneric (modName mb) IndexModule (modDoc mb) $ \name' ->     pure $ keyword "module " <> name' describeDec _ OpenDec {} = Nothing-describeDec visible (LocalDec (SigDec (SigBind name se doc _)) _)+describeDec visible (LocalDec (ModTypeDec (ModTypeBind name se doc _)) _)   | name `S.member` visible = Just $       describeGenericMod name IndexModuleType se doc $ \name' ->         pure $ keyword "local module type " <> name'@@ -883,12 +883,12 @@ describeSpec (ModSpec name se doc _) =   describeGenericMod name IndexModule se doc $ \name' ->     case se of-      SigSpecs {} -> pure $ keyword "module " <> name'+      ModTypeSpecs {} -> pure $ keyword "module " <> name'       _ -> do-        se' <- synopsisSigExp se+        se' <- synopsisModTypeExp se         pure $ keyword "module " <> name' <> ": " <> se' describeSpec (IncludeSpec sig _) = do-  sig' <- synopsisSigExp sig+  sig' <- synopsisModTypeExp sig   doc' <- docHtml Nothing   let decl_header =         (H.dt ! A.class_ "desc_header") $
src/Futhark/IR/GPU/Sizes.hs view
@@ -38,6 +38,12 @@     SizeLocalMemory   | -- | A bespoke size with a default.     SizeBespoke Name Int64+  | -- | Amount of registers available per workgroup. Mostly+    -- meaningful for querying the maximum.+    SizeRegisters+  | -- | Amount of L2 cache memory, in bytes. Mostly meaningful for+    -- querying the maximum.+    SizeCache   deriving (Eq, Ord, Show)  instance Pretty SizeClass where@@ -54,6 +60,8 @@   pretty SizeLocalMemory = "local_memory"   pretty (SizeBespoke k def) =     "bespoke" <> parens (pretty k <> comma <+> pretty def)+  pretty SizeRegisters = "registers"+  pretty SizeCache = "cache"  -- | The default value for the size.  If 'Nothing', that means the backend gets to decide. sizeDefault :: SizeClass -> Maybe Int64
src/Futhark/IR/SegOp.hs view
@@ -1157,8 +1157,8 @@ simplifySegOp (SegHist lvl space ops ts kbody) = do   (lvl', space', ts') <- Engine.simplify (lvl, space, ts) -  (ops', ops_hoisted) <- fmap unzip $-    forM ops $+  Engine.localVtable (flip (foldr ST.consume) $ concatMap histDest ops) $ do+    (ops', ops_hoisted) <- fmap unzip . forM ops $       \(HistOp w rf arrs nes dims lam) -> do         w' <- Engine.simplify w         rf' <- Engine.simplify rf@@ -1174,12 +1174,12 @@             op_hoisted           ) -  (kbody', body_hoisted) <- simplifyKernelBody space kbody+    (kbody', body_hoisted) <- simplifyKernelBody space kbody -  pure-    ( SegHist lvl' space' ops' ts' kbody',-      mconcat ops_hoisted <> body_hoisted-    )+    pure+      ( SegHist lvl' space' ops' ts' kbody',+        mconcat ops_hoisted <> body_hoisted+      )   where     scope = scopeOfSegSpace space     scope_vtable = ST.fromScope scope
src/Futhark/Internalise/Defunctorise.hs view
@@ -148,7 +148,7 @@  maybeAscript ::   SrcLoc ->-  Maybe (SigExp, Info (M.Map VName VName)) ->+  Maybe (ModTypeExp, Info (M.Map VName VName)) ->   ModExp ->   ModExp maybeAscript loc (Just (mtye, substs)) me = ModAscript me mtye substs loc@@ -311,7 +311,7 @@     evalModExp       $ foldr         addParam-        (maybeAscript (srclocOf mb) (modSignature mb) $ modExp mb)+        (maybeAscript (srclocOf mb) (modType mb) $ modExp mb)       $ modParams mb   mname <- transformName $ modName mb   pure $ Scope (scopeSubsts $ modScope mod) $ M.singleton mname mod@@ -331,7 +331,7 @@       bindingNames [typeAlias tb] $ do         transformTypeBind tb         transformDecs ds'-    SigDec {} : ds' ->+    ModTypeDec {} : ds' ->       transformDecs ds'     ModDec mb : ds' ->       bindingNames [modName mb] $ do
src/Futhark/LSP/Handlers.hs view
@@ -66,18 +66,13 @@         file_path = uriToFilePath $ doc ^. uri     tryReCompile state_mvar file_path -onDocumentOpenHandler :: IORef State -> Handlers (LspM ())-onDocumentOpenHandler state_mvar =-  notificationHandler SMethod_TextDocumentDidOpen $ \msg -> do-    let TNotificationMessage _ _ (DidOpenTextDocumentParams doc) = msg-        file_path = uriToFilePath $ doc ^. uri-    logStringStderr <& ("Opened document: " ++ show (doc ^. uri))-    tryReCompile state_mvar file_path+-- Some clients (Eglot) sends open/close events whether we want them+-- or not, so we better be prepared to ignore them.+onDocumentOpenHandler :: Handlers (LspM ())+onDocumentOpenHandler = notificationHandler SMethod_TextDocumentDidOpen $ \_ -> pure ()  onDocumentCloseHandler :: Handlers (LspM ())-onDocumentCloseHandler =-  notificationHandler SMethod_TextDocumentDidClose $ \_msg ->-    logStringStderr <& "Closed document"+onDocumentCloseHandler = notificationHandler SMethod_TextDocumentDidClose $ \_msg -> pure ()  -- Sent by Eglot when first connecting - not sure when else it might -- be sent.@@ -93,7 +88,7 @@ handlers state_mvar _ =   mconcat     [ onInitializeHandler,-      onDocumentOpenHandler state_mvar,+      onDocumentOpenHandler,       onDocumentCloseHandler,       onDocumentSaveHandler state_mvar,       onDocumentChangeHandler state_mvar,
− src/Futhark/Optimise/InPlaceLowering.hs
@@ -1,416 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# 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:------ @---   let r =---     loop (r1 = r0) = for i < n do---       let a = r1[i]---       let r1[i] = a * i---       in r1---   ...---   let x = y with [k] <- r in---   ...--- @------ We want to turn this into the following:------ @---   let x0 = y with [k] <- r0---   loop (x = x0) = for i < n do---     let a = a[k,i]---     let x[k,i] = a * i---     in x---   let r = x[k] in---   ...--- @------ The intent is that we are also going to optimise the new data--- movement (in the @x0@-binding), possibly by changing how @r0@ is--- defined.  For the above transformation to be valid, a number of--- conditions must be fulfilled:------    (1) @r@ must not be consumed after the original in-place update.------    (2) @k@ and @y@ must be available at the beginning of the loop.------    (3) @x@ must be visible whenever @r@ is visible.  (This means---    that both @x@ and @r@ must be bound in the same t'Body'.)------    (4) If @x@ is consumed at a point after the loop, @r@ must not---    be used after that point.------    (5) The size of @r1@ is invariant inside the loop.------    (6) The value @r@ must come from something that we can actually---    optimise (e.g. not a function parameter).------    (7) @y@ (or its aliases) may not be used inside the body of the---    loop.------    (8) The result of the loop may not alias the merge parameter---    @r1@.------    (9) @y@ or its aliases may not be used after the loop.------ FIXME: the implementation is not finished yet.  Specifically, not--- all of the above conditions are checked.-module Futhark.Optimise.InPlaceLowering-  ( inPlaceLoweringGPU,-    inPlaceLoweringSeq,-    inPlaceLoweringMC,-  )-where--import Control.Monad-import Control.Monad.RWS-import Data.Map.Strict qualified as M-import Data.Ord (comparing)-import Futhark.Analysis.Alias-import Futhark.Builder-import Futhark.IR.Aliases-import Futhark.IR.GPU-import Futhark.IR.MC-import Futhark.IR.Seq (Seq)-import Futhark.Optimise.InPlaceLowering.LowerIntoStm-import Futhark.Pass-import Futhark.Util (nubByOrd)---- | Apply the in-place lowering optimisation to the given program.-inPlaceLoweringGPU :: Pass GPU GPU-inPlaceLoweringGPU = inPlaceLowering onKernelOp lowerUpdateGPU---- | Apply the in-place lowering optimisation to the given program.-inPlaceLoweringSeq :: Pass Seq Seq-inPlaceLoweringSeq = inPlaceLowering pure lowerUpdate---- | Apply the in-place lowering optimisation to the given program.-inPlaceLoweringMC :: Pass MC MC-inPlaceLoweringMC = inPlaceLowering onMCOp lowerUpdate---- | Apply the in-place lowering optimisation to the given program.-inPlaceLowering ::-  (Constraints rep) =>-  OnOp rep ->-  LowerUpdate rep (ForwardingM rep) ->-  Pass rep rep-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 $-          stmsFromList <$> optimiseStms (stmsToList stms) (pure ())--    optimiseFunDef consts fundec =-      modifyNameSource $-        runForwardingM lower onOp $-          descend (stmsToList consts) $-            bindingFParams (funDefParams fundec) $ do-              body <- optimiseBody $ funDefBody fundec-              pure $ fundec {funDefBody = body}--    descend [] m = m-    descend (stm : stms) m = bindingStm stm $ descend stms m--type Constraints rep = (Buildable rep, AliasableRep rep)--optimiseBody ::-  (Constraints rep) =>-  Body (Aliases rep) ->-  ForwardingM rep (Body (Aliases rep))-optimiseBody (Body als stms res) = do-  stms' <- deepen $ optimiseStms (stmsToList stms) $ mapM_ (seen . resSubExp) res-  pure $ Body als (stmsFromList stms') res-  where-    seen Constant {} = pure ()-    seen (Var v) = seenVar v--optimiseStms ::-  (Constraints rep) =>-  [Stm (Aliases rep)] ->-  ForwardingM rep () ->-  ForwardingM rep [Stm (Aliases rep)]-optimiseStms [] m = m >> pure []-optimiseStms (stm : stms) m = do-  (stms', bup) <- tapBottomUp $ bindingStm stm $ optimiseStms stms m-  stm' <- optimiseInStm stm-  -- XXX: unfortunate that we cannot handle duplicate update values.-  -- Would be good to improve this.  See inplacelowering6.fut.-  case nubByOrd (comparing updateValue)-    . filter ((`notNameIn` bottomUpSeen bup) . updateSource) -- (9)-    . filter ((`elem` boundHere) . updateValue)-    $ forwardThese bup of-    [] -> do-      checkIfForwardableUpdate stm'-      pure $ stm' : stms'-    updates -> do-      lower <- asks topLowerUpdate-      scope <- askScope--      -- If we forward any updates, we need to remove them from stms'.-      let updated_names =-            map updateName updates-          notUpdated =-            not . any (`elem` updated_names) . patNames . stmPat--      -- Condition (5) and (7) are assumed to be checked by-      -- lowerUpdate.-      case lower scope stm' updates of-        Just lowering -> do-          new_stms <- lowering-          new_stms' <- optimiseStms new_stms $ tell bup {forwardThese = []}-          pure $ new_stms' ++ filter notUpdated stms'-        Nothing -> do-          checkIfForwardableUpdate stm'-          pure $ stm' : stms'-  where-    boundHere = patNames $ stmPat stm--    checkIfForwardableUpdate (Let pat (StmAux cs _ _) e)-      | Pat [PatElem v dec] <- pat,-        BasicOp (Update Unsafe src slice (Var ve)) <- e =-          maybeForward ve v dec cs src slice-    checkIfForwardableUpdate stm' =-      mapM_ seenVar $ namesToList $ freeIn $ stmExp stm'--optimiseInStm :: (Constraints rep) => Stm (Aliases rep) -> ForwardingM rep (Stm (Aliases rep))-optimiseInStm (Let pat dec e) =-  Let pat dec <$> optimiseExp e--optimiseExp :: (Constraints rep) => Exp (Aliases rep) -> ForwardingM rep (Exp (Aliases rep))-optimiseExp (Loop merge form body) =-  bindingScope (scopeOfLoopForm form) . bindingFParams (map fst merge) $-    Loop merge form <$> optimiseBody body-optimiseExp (Op op) = do-  f <- asks topOnOp-  Op <$> f op-optimiseExp e = mapExpM optimise e-  where-    optimise =-      identityMapper-        { mapOnBody = const optimiseBody-        }--onSegOp ::-  (Constraints rep) =>-  SegOp lvl (Aliases rep) ->-  ForwardingM rep (SegOp lvl (Aliases rep))-onSegOp op =-  bindingScope (scopeOfSegSpace (segSpace op)) $ do-    let mapper = identitySegOpMapper {mapOnSegOpBody = onKernelBody}-        onKernelBody kbody = do-          stms <--            deepen $-              optimiseStms (stmsToList (kernelBodyStms kbody)) $-                mapM_ seenVar $-                  namesToList $-                    freeIn $-                      kernelBodyResult kbody-          pure kbody {kernelBodyStms = stmsFromList stms}-    mapSegOpM mapper op--onMCOp :: OnOp MC-onMCOp (ParOp par_op op) = ParOp <$> traverse onSegOp par_op <*> onSegOp op-onMCOp op = pure op--onKernelOp :: OnOp GPU-onKernelOp (SegOp op) = SegOp <$> onSegOp op-onKernelOp op = pure op--data Entry rep = Entry-  { entryNumber :: Int,-    entryAliases :: Names,-    entryDepth :: Int,-    entryOptimisable :: Bool,-    entryType :: NameInfo (Aliases rep)-  }--type VTable rep = M.Map VName (Entry rep)--type OnOp rep = Op (Aliases rep) -> ForwardingM rep (Op (Aliases rep))--data TopDown rep = TopDown-  { topDownCounter :: Int,-    topDownTable :: VTable rep,-    topDownDepth :: Int,-    topLowerUpdate :: LowerUpdate rep (ForwardingM rep),-    topOnOp :: OnOp rep-  }--data BottomUp rep = BottomUp-  { bottomUpSeen :: Names,-    forwardThese :: [DesiredUpdate (LetDec (Aliases rep))]-  }--instance Semigroup (BottomUp rep) where-  BottomUp seen1 forward1 <> BottomUp seen2 forward2 =-    BottomUp (seen1 <> seen2) (forward1 <> forward2)--instance Monoid (BottomUp rep) where-  mempty = BottomUp mempty mempty--newtype ForwardingM rep a = ForwardingM (RWS (TopDown rep) (BottomUp rep) VNameSource a)-  deriving-    ( Monad,-      Applicative,-      Functor,-      MonadReader (TopDown rep),-      MonadWriter (BottomUp rep),-      MonadState VNameSource-    )--instance MonadFreshNames (ForwardingM rep) where-  getNameSource = get-  putNameSource = put--instance (Constraints rep) => HasScope (Aliases rep) (ForwardingM rep) where-  askScope = M.map entryType <$> asks topDownTable--runForwardingM ::-  LowerUpdate rep (ForwardingM rep) ->-  OnOp rep ->-  ForwardingM rep 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 rep)) ->-  [Param dec] ->-  ForwardingM rep a ->-  ForwardingM rep a-bindingParams f params = local $ \(TopDown n vtable d x y) ->-  let entry 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--bindingFParams ::-  [FParam (Aliases rep)] ->-  ForwardingM rep a ->-  ForwardingM rep a-bindingFParams = bindingParams FParamName--bindingScope ::-  Scope (Aliases rep) ->-  ForwardingM rep a ->-  ForwardingM rep 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--bindingStm ::-  Stm (Aliases rep) ->-  ForwardingM rep a ->-  ForwardingM rep a-bindingStm (Let pat _ _) = local $ \(TopDown n vtable d x y) ->-  let entries = M.fromList $ map entry $ patElems 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--bindingNumber :: VName -> ForwardingM rep Int-bindingNumber name = do-  res <- asks $ fmap entryNumber . M.lookup name . topDownTable-  case res of-    Just n -> pure n-    Nothing ->-      error $-        "bindingNumber: variable "-          ++ prettyString name-          ++ " not found."--deepen :: ForwardingM rep a -> ForwardingM rep a-deepen = local $ \env -> env {topDownDepth = topDownDepth env + 1}--areAvailableBefore :: Names -> VName -> ForwardingM rep Bool-areAvailableBefore names point = do-  pointN <- bindingNumber point-  nameNs <- mapM bindingNumber $ namesToList names-  pure $ all (< pointN) nameNs--isInCurrentBody :: VName -> ForwardingM rep Bool-isInCurrentBody name = do-  current <- asks topDownDepth-  res <- asks $ fmap entryDepth . M.lookup name . topDownTable-  case res of-    Just d -> pure $ d == current-    Nothing ->-      error $-        "isInCurrentBody: variable "-          ++ prettyString name-          ++ " not found."--isOptimisable :: VName -> ForwardingM rep Bool-isOptimisable name = do-  res <- asks $ fmap entryOptimisable . M.lookup name . topDownTable-  case res of-    Just b -> pure b-    Nothing ->-      error $-        "isOptimisable: variable "-          ++ prettyString name-          ++ " not found."--seenVar :: forall rep. VName -> ForwardingM rep ()-seenVar name = do-  aliases <--    asks $-      maybe mempty entryAliases-        . M.lookup name-        . topDownTable-  tell $ (mempty :: BottomUp rep) {bottomUpSeen = oneName name <> aliases}--tapBottomUp :: ForwardingM rep a -> ForwardingM rep (a, BottomUp rep)-tapBottomUp m = do-  (x, bup) <- listen m-  pure (x, bup)--maybeForward ::-  (Constraints rep) =>-  VName ->-  VName ->-  LetDec (Aliases rep) ->-  Certs ->-  VName ->-  Slice SubExp ->-  ForwardingM rep ()-maybeForward v dest_nm dest_dec cs src slice = do-  -- Checks condition (2)-  available <--    (freeIn src <> freeIn slice <> freeIn cs)-      `areAvailableBefore` v-  -- Check condition (3)-  samebody <- isInCurrentBody v-  -- Check condition (6)-  optimisable <- isOptimisable v-  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]}
− src/Futhark/Optimise/InPlaceLowering/LowerIntoStm.hs
@@ -1,376 +0,0 @@-{-# LANGUAGE TypeFamilies #-}--module Futhark.Optimise.InPlaceLowering.LowerIntoStm-  ( lowerUpdateGPU,-    lowerUpdate,-    LowerUpdate,-    DesiredUpdate (..),-  )-where--import Control.Monad-import Control.Monad.Writer-import Data.Either-import Data.List (find, unzip5)-import Data.Maybe (isNothing, mapMaybe)-import Futhark.Analysis.PrimExp.Convert-import Futhark.Construct-import Futhark.IR.Aliases-import Futhark.IR.GPU-import Futhark.Optimise.InPlaceLowering.SubstituteIndices--data DesiredUpdate dec = DesiredUpdate-  { -- | Name of result.-    updateName :: VName,-    -- | Type of result.-    updateType :: dec,-    updateCerts :: Certs,-    updateSource :: VName,-    updateIndices :: Slice SubExp,-    updateValue :: VName-  }-  deriving (Show)--instance Functor DesiredUpdate where-  f `fmap` u = u {updateType = f $ updateType u}--updateHasValue :: VName -> DesiredUpdate dec -> Bool-updateHasValue name = (name ==) . updateValue--type LowerUpdate rep m =-  Scope (Aliases rep) ->-  Stm (Aliases rep) ->-  [DesiredUpdate (LetDec (Aliases rep))] ->-  Maybe (m [Stm (Aliases rep)])--lowerUpdate ::-  ( MonadFreshNames m,-    Buildable rep,-    LetDec rep ~ Type,-    AliasableRep rep-  ) =>-  LowerUpdate rep m-lowerUpdate scope (Let pat aux (Loop merge form body)) updates = do-  canDo <- lowerUpdateIntoLoop scope updates pat merge form body-  Just $ do-    (prestms, poststms, pat', merge', body') <- canDo-    pure $-      prestms-        ++ [ certify (stmAuxCerts aux) $-               mkLet pat' $-                 Loop merge' form body'-           ]-        ++ poststms-lowerUpdate-  _-  (Let pat aux (BasicOp (SubExp (Var v))))-  [DesiredUpdate bindee_nm bindee_dec cs src (Slice is) val]-    | patNames pat == [src] =-        let is' = fullSlice (typeOf bindee_dec) is-         in Just . pure $-              [ certify (stmAuxCerts aux <> cs) $-                  mkLet [Ident bindee_nm $ typeOf bindee_dec] $-                    BasicOp $-                      Update Unsafe v is' $-                        Var val-              ]-lowerUpdate _ _ _ =-  Nothing--lowerUpdateGPU :: (MonadFreshNames m) => LowerUpdate GPU m-lowerUpdateGPU-  scope-  (Let pat aux (Op (SegOp (SegMap lvl space ts kbody))))-  updates-    | all ((`elem` patNames pat) . updateValue) updates,-      not source_used_in_kbody = do-        mk <- lowerUpdatesIntoSegMap scope pat ts updates space kbody-        Just $ do-          (pat', ts', kbody', poststms) <- mk-          let cs = stmAuxCerts aux <> foldMap updateCerts updates-          pure $-            certify cs (Let pat' aux $ Op $ SegOp $ SegMap lvl space ts' kbody')-              : stmsToList poststms-    where-      -- This check is a bit more conservative than ideal.  In a perfect-      -- world, we would allow indexing a[i,j] if the update is also-      -- to exactly a[i,j], as that will not create cross-iteration-      -- dependencies.  (Although the type checker wouldn't be able to-      -- permit this anyway.)-      source_used_in_kbody =-        mconcat (map (`lookupAliases` scope) (namesToList (freeIn kbody)))-          `namesIntersect` mconcat (map ((`lookupAliases` scope) . updateSource) updates)-lowerUpdateGPU scope stm updates = lowerUpdate scope stm updates--lowerUpdatesIntoSegMap ::-  (MonadFreshNames m) =>-  Scope (Aliases GPU) ->-  Pat (LetDec (Aliases GPU)) ->-  [Type] ->-  [DesiredUpdate (LetDec (Aliases GPU))] ->-  SegSpace ->-  KernelBody (Aliases GPU) ->-  Maybe-    ( m-        ( Pat (LetDec (Aliases GPU)),-          [Type],-          KernelBody (Aliases GPU),-          Stms (Aliases GPU)-        )-    )-lowerUpdatesIntoSegMap scope pat ret_ts updates kspace kbody = do-  -- The updates are all-or-nothing.  Being more liberal would require-  -- changes to the in-place-lowering pass itself.-  mk <- mapM onRet (zip3 (patElems pat) ret_ts (kernelBodyResult kbody))-  pure $ do-    (pes, ret_ts', bodystms, krets, poststms) <- unzip5 <$> sequence mk-    pure-      ( Pat pes,-        ret_ts',-        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 _ cs se <- Just ret--          -- The slice we're writing per thread must fully cover the-          -- underlying dimensions.-          guard $-            let (dims', slice') =-                  unzip . drop (length gtids) . filter (isNothing . dimFix . snd) $-                    zip (arrayDims (typeOf bindee_dec)) (unSlice slice)-             in isFullSlice (Shape dims') (Slice slice')--          Just $ do-            (slice', bodystms) <--              flip runBuilderT scope $-                traverse (toSubExp "index") $-                  fixSlice (fmap pe64 slice) $-                    map (pe64 . Var) gtids--            let ret' = WriteReturns cs src [(fullSlice (typeOf bindee_dec) (map DimFix slice'), se)]--            v_aliased <- newName v--            pure-              ( PatElem bindee_nm bindee_dec,-                typeOf bindee_dec,-                bodystms,-                ret',-                stmsFromList-                  [ mkLet [Ident v_aliased $ typeOf v_dec] $ BasicOp $ Index bindee_nm slice,-                    mkLet [Ident v $ typeOf v_dec] $ BasicOp $ Replicate mempty $ Var v_aliased-                  ]-              )-    onRet (pe, ret_t, ret) =-      Just $ pure (pe, ret_t, mempty, ret, mempty)--lowerUpdateIntoLoop ::-  ( Buildable rep,-    BuilderOps rep,-    Aliased rep,-    LetDec rep ~ (als, Type),-    MonadFreshNames m-  ) =>-  Scope rep ->-  [DesiredUpdate (LetDec rep)] ->-  Pat (LetDec rep) ->-  [(FParam rep, SubExp)] ->-  LoopForm ->-  Body rep ->-  Maybe-    ( m-        ( [Stm rep],-          [Stm rep],-          [Ident],-          [(FParam rep, SubExp)],-          Body rep-        )-    )-lowerUpdateIntoLoop scope updates pat val form body = do-  -- Algorithm:-  ---  --   0) Map each result of the loop body to a corresponding in-place-  --      update, if one exists.-  ---  --   1) Create new merge variables corresponding to the arrays being-  --      updated; extend the pattern and the @res@ list with these,-  --      and remove the parts of the result list that have a-  --      corresponding in-place update.-  ---  --      (The creation of the new merge variable identifiers is-  --      actually done at the same time as step (0)).-  ---  --   2) Create in-place updates at the end of the loop body.-  ---  --   3) Create index expressions that read back the values written-  --      in (2).  If the merge parameter corresponding to this value-  --      is unique, also @copy@ this value.-  ---  --   4) Update the result of the loop body to properly pass the new-  --      arrays and indexed elements to the next iteration of the-  --      loop.-  ---  -- We also check that the merge parameters we work with have-  -- loop-invariant shapes.--  -- Safety condition (8).-  forM_ (zip val $ bodyAliases body) $ \((p, _), als) ->-    guard $ paramName p `notNameIn` als--  mk_in_place_map <- summariseLoop scope updates usedInBody resmap val--  Just $ do-    in_place_map <- mk_in_place_map-    (val', prestms, poststms) <- mkMerges in_place_map-    let valpat = mkResAndPat in_place_map-        idxsubsts = indexSubstitutions in_place_map-    (idxsubsts', newstms) <- substituteIndices idxsubsts $ bodyStms body-    (body_res, res_stms) <- manipulateResult in_place_map idxsubsts'-    let body' = mkBody (newstms <> res_stms) body_res-    pure (prestms, poststms, valpat, val', body')-  where-    usedInBody =-      mconcat $ map (`lookupAliases` scope) $ namesToList $ freeIn body <> freeIn form-    resmap = zip (bodyResult body) $ patIdents pat--    mkMerges ::-      (MonadFreshNames m, Buildable rep) =>-      [LoopResultSummary (als, Type)] ->-      m ([(Param DeclType, SubExp)], [Stm rep], [Stm rep])-    mkMerges summaries = do-      ((origmerge, extramerge), (prestms, poststms)) <--        runWriterT $ partitionEithers <$> mapM mkMerge summaries-      pure (origmerge ++ extramerge, prestms, poststms)--    mkMerge summary-      | Just (update, mergename, mergedec) <- relatedUpdate summary = do-          source <- newVName "modified_source"-          precopy <- newVName $ baseString (updateValue update) <> "_precopy"-          let source_t = snd $ updateType update-              elm_t = source_t `setArrayDims` sliceDims (updateIndices update)-          tell-            ( [ mkLet [Ident source source_t] . BasicOp-                  $ Update-                    Unsafe-                    (updateSource update)-                    (fullSlice source_t $ unSlice $ updateIndices update)-                  $ snd-                  $ mergeParam summary-              ],-              [ mkLet [Ident precopy elm_t] . BasicOp $-                  Index-                    (updateName update)-                    (fullSlice source_t $ unSlice $ updateIndices update),-                mkLet [Ident (updateValue update) elm_t] $ BasicOp $ Replicate mempty $ Var precopy-              ]-            )-          pure $-            Right-              ( Param mempty mergename (toDecl (typeOf mergedec) Unique),-                Var source-              )-      | otherwise = pure $ Left $ mergeParam summary--    mkResAndPat summaries =-      let (origpat, extrapat) = partitionEithers $ map mkResAndPat' summaries-       in origpat ++ extrapat--    mkResAndPat' summary-      | Just (update, _, _) <- relatedUpdate summary =-          Right (Ident (updateName update) (snd $ updateType update))-      | otherwise =-          Left (inPatAs summary)--summariseLoop ::-  ( Aliased rep,-    MonadFreshNames m-  ) =>-  Scope rep ->-  [DesiredUpdate (als, Type)] ->-  Names ->-  [(SubExpRes, Ident)] ->-  [(Param DeclType, SubExp)] ->-  Maybe (m [LoopResultSummary (als, Type)])-summariseLoop scope updates usedInBody resmap merge =-  sequence <$> zipWithM summariseLoopResult resmap merge-  where-    summariseLoopResult (se, v) (fparam, mergeinit)-      | Just update <- find (updateHasValue $ identName v) updates =-          -- Safety condition (7)-          if usedInBody `namesIntersect` lookupAliases (updateSource update) scope-            then Nothing-            else-              if hasLoopInvariantShape fparam-                then Just $ do-                  lowered_array <- newVName "lowered_array"-                  pure-                    LoopResultSummary-                      { resultSubExp = se,-                        inPatAs = 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--    loopInvariant (Var v) = v `notElem` merge_param_names-    loopInvariant Constant {} = True--data LoopResultSummary dec = LoopResultSummary-  { resultSubExp :: SubExpRes,-    inPatAs :: Ident,-    mergeParam :: (Param DeclType, SubExp),-    relatedUpdate :: Maybe (DesiredUpdate dec, VName, dec)-  }-  deriving (Show)--indexSubstitutions :: (Typed dec) => [LoopResultSummary dec] -> IndexSubstitutions-indexSubstitutions = mapMaybe getSubstitution-  where-    getSubstitution res = do-      (DesiredUpdate _ _ cs _ is _, nm, dec) <- relatedUpdate res-      let name = paramName $ fst $ mergeParam res-      pure (name, (cs, nm, typeOf dec, is))--manipulateResult ::-  (Buildable rep, MonadFreshNames m) =>-  [LoopResultSummary (LetDec rep)] ->-  IndexSubstitutions ->-  m (Result, Stms rep)-manipulateResult summaries substs = do-  let (orig_ses, updated_ses) = partitionEithers $ map unchangedRes summaries-  (subst_ses, res_stms) <- runWriterT $ zipWithM substRes updated_ses substs-  pure (orig_ses ++ subst_ses, stmsFromList res_stms)-  where-    unchangedRes summary =-      case relatedUpdate summary of-        Nothing -> Left $ resultSubExp summary-        Just _ -> Right $ resultSubExp summary-    substRes (SubExpRes res_cs (Var res_v)) (subst_v, (_, nm, _, _))-      | res_v == subst_v =-          pure $ SubExpRes res_cs $ Var nm-    substRes (SubExpRes res_cs res_se) (_, (cs, nm, dec, Slice is)) = do-      v' <- newIdent' (++ "_updated") $ Ident nm $ typeOf dec-      tell-        [ certify (res_cs <> cs) . mkLet [v'] . BasicOp $-            Update Unsafe nm (fullSlice (typeOf dec) is) res_se-        ]-      pure $ varRes $ identName v'
− src/Futhark/Optimise/InPlaceLowering/SubstituteIndices.hs
@@ -1,191 +0,0 @@-{-# 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--import Control.Monad-import Data.Map.Strict qualified as M-import Futhark.Construct-import Futhark.IR-import Futhark.IR.Prop.Aliases-import Futhark.Transform.Substitute---- | Essentially the components of an 'Index' expression.-type IndexSubstitution = (Certs, VName, Type, Slice SubExp)---- | A mapping from variable names to the indexing operation they--- should be replaced with.-type IndexSubstitutions = [(VName, IndexSubstitution)]--typeEnvFromSubstitutions :: (LParamInfo rep ~ Type) => IndexSubstitutions -> Scope rep-typeEnvFromSubstitutions = M.fromList . map (fromSubstitution . snd)-  where-    fromSubstitution (_, name, t, _) =-      (name, LParamName t)---- | Perform the substitution.-substituteIndices ::-  ( MonadFreshNames m,-    BuilderOps rep,-    Buildable rep,-    Aliased rep-  ) =>-  IndexSubstitutions ->-  Stms rep ->-  m (IndexSubstitutions, Stms rep)-substituteIndices substs stms =-  runBuilderT (substituteIndicesInStms substs stms) types-  where-    types = typeEnvFromSubstitutions substs--substituteIndicesInStms ::-  (MonadBuilder m, Buildable (Rep m), Aliased (Rep m)) =>-  IndexSubstitutions ->-  Stms (Rep m) ->-  m IndexSubstitutions-substituteIndicesInStms = foldM substituteIndicesInStm--substituteIndicesInStm ::-  (MonadBuilder m, Buildable (Rep m), Aliased (Rep m)) =>-  IndexSubstitutions ->-  Stm (Rep m) ->-  m IndexSubstitutions-substituteIndicesInStm substs (Let pat _ (BasicOp (Rearrange perm v)))-  | Just (cs, src, src_t, is) <- lookup v substs,-    [v'] <- patNames pat = do-      let extra_dims = arrayRank src_t - length perm-          perm' = [0 .. extra_dims - 1] ++ map (+ extra_dims) perm-      src' <--        letExp (baseString v' <> "_subst") $ BasicOp $ Rearrange perm' src-      src_t' <- lookupType src'-      pure $ (v', (cs, src', src_t', is)) : substs-substituteIndicesInStm substs (Let pat rep e) = do-  e' <- substituteIndicesInExp substs e-  addStm $ Let pat rep e'-  pure substs--substituteIndicesInExp ::-  (MonadBuilder m, Buildable (Rep m), Aliased (Rep m)) =>-  IndexSubstitutions ->-  Exp (Rep m) ->-  m (Exp (Rep m))-substituteIndicesInExp substs (Op op) = do-  let used_in_op = filter ((`nameIn` freeIn op) . fst) substs-  var_substs <- fmap mconcat $-    forM used_in_op $ \(v, (cs, src, src_dec, Slice is)) -> do-      v' <--        certifying cs $-          letExp (baseString src <> "_op_idx") $-            BasicOp $-              Index src $-                fullSlice (typeOf src_dec) is-      pure $ M.singleton v v'-  pure $ Op $ substituteNames var_substs op-substituteIndicesInExp substs e = do-  substs' <- copyAnyConsumed e-  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 $-                    letSubExp (baseString v ++ "_row") $-                      BasicOp $-                        Index src2 $-                          fullSlice (typeOf src2dec) (unSlice is2)-                row_copy <--                  letExp (baseString v ++ "_row_copy") . BasicOp $-                    Replicate mempty row-                pure $-                  update-                    v-                    v-                    ( mempty,-                      row_copy,-                      src2dec-                        `setType` ( typeOf src2dec-                                      `setArrayDims` sliceDims is2-                                  ),-                      Slice []-                    )-                    substs'-          consumingSubst substs' _ =-            pure substs'-       in foldM consumingSubst substs . namesToList . consumedInExp--substituteIndicesInSubExp ::-  (MonadBuilder m) =>-  IndexSubstitutions ->-  SubExp ->-  m SubExp-substituteIndicesInSubExp substs (Var v) =-  Var <$> substituteIndicesInVar substs v-substituteIndicesInSubExp _ se =-  pure se--substituteIndicesInVar ::-  (MonadBuilder m) =>-  IndexSubstitutions ->-  VName ->-  m VName-substituteIndicesInVar substs v-  | Just (cs2, src2, _, Slice []) <- lookup v substs =-      certifying cs2 $-        letExp (baseString src2) $-          BasicOp $-            SubExp $-              Var src2-  | Just (cs2, src2, src2_dec, Slice is2) <- lookup v substs =-      certifying cs2 $-        letExp (baseString src2 <> "_v_idx") $-          BasicOp $-            Index src2 $-              fullSlice (typeOf src2_dec) is2-  | otherwise =-      pure v--substituteIndicesInBody ::-  (MonadBuilder m, Buildable (Rep m), Aliased (Rep m)) =>-  IndexSubstitutions ->-  Body (Rep m) ->-  m (Body (Rep m))-substituteIndicesInBody substs (Body _ stms res) = do-  (substs', stms') <--    inScopeOf stms $-      collectStms $-        substituteIndicesInStms substs stms-  (res', res_stms) <--    inScopeOf stms' $-      collectStms $-        mapM (onSubExpRes substs') res-  mkBodyM (stms' <> res_stms) res'-  where-    onSubExpRes substs' (SubExpRes cs se) =-      SubExpRes cs <$> substituteIndicesInSubExp substs' se--update ::-  VName ->-  VName ->-  IndexSubstitution ->-  IndexSubstitutions ->-  IndexSubstitutions-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 " ++ prettyString needle
src/Futhark/Passes.hs view
@@ -25,7 +25,6 @@ import Futhark.Optimise.Fusion import Futhark.Optimise.GenRedOpt import Futhark.Optimise.HistAccs-import Futhark.Optimise.InPlaceLowering import Futhark.Optimise.InliningDeadFun import Futhark.Optimise.MemoryBlockMerging qualified as MemoryBlockMerging import Futhark.Optimise.MergeGPUBodies@@ -103,7 +102,6 @@         mergeGPUBodies,         simplifyGPU, -- Cleanup merged GPUBody kernels.         sinkGPU, -- Sink reads within GPUBody kernels.-        inPlaceLoweringGPU,         babysitKernels,         -- Important to simplify after babysitting in order to fix up         -- redundant manifests.@@ -118,8 +116,7 @@   standardPipeline     >>> onePass firstOrderTransform     >>> passes-      [ simplifySeq,-        inPlaceLoweringSeq+      [ simplifySeq       ]  -- | Run 'seqPipeline', then add memory information (and@@ -183,8 +180,7 @@         unstreamMC,         performCSE True,         simplifyMC,-        sinkMC,-        inPlaceLoweringMC+        sinkMC       ]  -- | Run 'mcPipeline' and then add memory information.
src/Futhark/Util/IntegralExp.hs view
@@ -39,6 +39,9 @@   divUp x y =     (x + y - 1) `Futhark.Util.IntegralExp.div` y +  nextMul :: e -> e -> e+  nextMul x y = x `divUp` y * y+ -- | 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}
src/Language/Futhark/Interpreter.hs view
@@ -90,14 +90,14 @@  type Stack = [StackFrame] -type Sizes = M.Map VName Int64+type Exts = M.Map VName Value  -- | The monad in which evaluation takes place. newtype EvalM a   = EvalM       ( ReaderT           (Stack, M.Map ImportName Env)-          (StateT Sizes (F ExtOp))+          (StateT Exts (F ExtOp))           a       )   deriving@@ -106,7 +106,7 @@       Functor,       MonadFree ExtOp,       MonadReader (Stack, M.Map ImportName Env),-      MonadState Sizes+      MonadState Exts     )  runEvalM :: M.Map ImportName Env -> EvalM a -> F ExtOp a@@ -129,11 +129,11 @@ lookupImport :: ImportName -> EvalM (Maybe Env) lookupImport f = asks $ M.lookup f . snd -putExtSize :: VName -> Int64 -> EvalM ()+putExtSize :: VName -> Value -> EvalM () putExtSize v x = modify $ M.insert v x -getSizes :: EvalM Sizes-getSizes = get+getExts :: EvalM Exts+getExts = get  -- | Disregard any existential sizes computed during this action. -- This is used so that existentials computed during one iteration of@@ -145,8 +145,13 @@   put s   pure x -extSizeEnv :: EvalM Env-extSizeEnv = i64Env <$> getSizes+extEnv :: EvalM Env+extEnv = valEnv . M.map f <$> getExts+  where+    f v =+      ( Nothing,+        v+      )  valueStructType :: ValueType -> StructType valueStructType = first $ flip sizeFromInteger mempty . toInteger@@ -296,7 +301,8 @@ -- | An expression evaluator that embeds an environment. type Eval = Exp -> EvalM Value --- | A TermValue with a 'Nothing' type annotation is an intrinsic.+-- | A TermValue with a 'Nothing' type annotation is an intrinsic or+-- an existential. data TermBinding   = TermValue (Maybe T.BoundV) Value   | -- | A polymorphic value that must be instantiated.  The@@ -636,7 +642,7 @@  evalWithExts :: Env -> EvalM Eval evalWithExts env = do-  size_env <- extSizeEnv+  size_env <- extEnv   pure $ eval $ size_env <> env  -- | Evaluate all possible sizes, except those that contain free@@ -750,16 +756,17 @@ evalArg env e ext = do   v <- eval env e   case ext of-    Just ext' -> putExtSize ext' $ asInt64 v-    Nothing -> pure ()+    Just ext' -> putExtSize ext' v+    _ -> pure ()   pure v  returned :: Env -> TypeBase Size als -> [VName] -> Value -> EvalM Value returned _ _ [] v = pure v returned env ret retext v = do-  mapM_ (uncurry putExtSize) . M.toList $-    resolveExistentials retext (expandType env $ toStruct ret) $-      valueShape v+  mapM_ (uncurry putExtSize . second (ValuePrim . SignedValue . Int64Value))+    . M.toList+    $ resolveExistentials retext (expandType env $ toStruct ret)+    $ valueShape v   pure v  evalAppExp :: Env -> AppExp -> EvalM Value@@ -1193,7 +1200,7 @@ evalDec :: Env -> Dec -> EvalM Env evalDec env (ValDec (ValBind _ v _ (Info ret) tparams ps fbody _ _ _)) = localExts $ do   binding <- evalFunctionBinding env tparams ps ret fbody-  sizes <- extSizeEnv+  sizes <- extEnv   pure $     env {envTerm = M.insert v binding $ envTerm env} <> sizes evalDec env (OpenDec me _) = do@@ -1204,7 +1211,7 @@ 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 {} = pure env+evalDec env ModTypeDec {} = pure env evalDec env (TypeDec (TypeBind v l ps _ (Info (RetType dims t)) _ _)) = do   let abbr = T.TypeAbbr l ps . RetType dims $ expandType env t   pure env {envType = M.insert v abbr $ envType env}@@ -1941,7 +1948,7 @@     -- We need to extract any new existential sizes and add them as     -- ordinary bindings to the context, or we will not be able to     -- look up their values later.-    sizes <- extSizeEnv+    sizes <- extEnv     pure $ env <> sizes  interpretDec :: Ctx -> Dec -> F ExtOp Ctx
src/Language/Futhark/Parser/Monad.hs view
@@ -57,7 +57,7 @@ addDoc :: DocComment -> UncheckedDec -> UncheckedDec addDoc doc (ValDec val) = ValDec (val {valBindDoc = Just doc}) addDoc doc (TypeDec tp) = TypeDec (tp {typeDoc = Just doc})-addDoc doc (SigDec sig) = SigDec (sig {sigDoc = Just doc})+addDoc doc (ModTypeDec sig) = ModTypeDec (sig {modTypeDoc = Just doc}) addDoc doc (ModDec mod) = ModDec (mod {modDoc = Just doc}) addDoc _ dec = dec 
src/Language/Futhark/Parser/Parser.y view
@@ -217,7 +217,7 @@ Dec_ :: { UncheckedDec }     : Val               { ValDec $1 }     | TypeAbbr          { TypeDec $1 }-    | SigBind           { SigDec $1 }+    | ModTypeBind       { ModTypeDec $1 }     | ModBind           { ModDec $1 }     | open ModExp       { OpenDec $2 (srclocOf $1) }     | import stringlit@@ -228,29 +228,29 @@  ; -SigExp :: { UncheckedSigExp }-        : QualName            { let (v, loc) = $1 in SigVar v NoInfo (srclocOf loc) }-        | '{' Specs '}'  { SigSpecs $2 (srcspan $1 $>) }-        | SigExp with TypeRef { SigWith $1 $3 (srcspan $1 $>) }-        | '(' SigExp ')'      { SigParens $2 (srcspan $1 $>) }-        | '(' id ':' SigExp ')' '->' SigExp-                              { let L _ (ID name) = $2-                                in SigArrow (Just name) $4 $7 (srcspan $1 $>) }-        | SigExp '->' SigExp  { SigArrow Nothing $1 $3 (srcspan $1 $>) }+ModTypeExp :: { UncheckedModTypeExp }+        : QualName                { let (v, loc) = $1 in ModTypeVar v NoInfo (srclocOf loc) }+        | '{' Specs '}'           { ModTypeSpecs $2 (srcspan $1 $>) }+        | ModTypeExp with TypeRef { ModTypeWith $1 $3 (srcspan $1 $>) }+        | '(' ModTypeExp ')'      { ModTypeParens $2 (srcspan $1 $>) }+        | '(' id ':' ModTypeExp ')' '->' ModTypeExp+                                  { let L _ (ID name) = $2+                                    in ModTypeArrow (Just name) $4 $7 (srcspan $1 $>) }+        | ModTypeExp '->' ModTypeExp  { ModTypeArrow Nothing $1 $3 (srcspan $1 $>) }  TypeRef :: { TypeRefBase NoInfo Name }          : QualName TypeParams '=' TypeExpTerm            { TypeRef (fst $1) $2 $4 (srcspan (snd $1) $>) } -SigBind :: { SigBindBase NoInfo Name }-         : module type id '=' SigExp+ModTypeBind :: { ModTypeBindBase NoInfo Name }+         : module type id '=' ModTypeExp           { let L _ (ID name) = $3-            in SigBind name $5 Nothing (srcspan $1 $>) }+            in ModTypeBind name $5 Nothing (srcspan $1 $>) }  ModExp :: { UncheckedModExp }-        : ModExp ':' SigExp+        : ModExp ':' ModTypeExp           { ModAscript $1 $3 NoInfo (srcspan $1 $>) }-        | '\\' ModParam maybeAscription(SimpleSigExp) '->' ModExp+        | '\\' ModParam maybeAscription(SimpleModTypeExp) '->' ModExp           { ModLambda $2 (fmap (,NoInfo) $3) $5 (srcspan $1 $>) }         | import stringlit           { let L _ (STRINGLIT s) = $2 in ModImport (T.unpack s) NoInfo (srcspan $1 $>) }@@ -273,18 +273,18 @@               { let (v, loc) = $1 in ModVar v (srclocOf loc) }             | '{' Decs '}' { ModDecs $2 (srcspan $1 $>) } -SimpleSigExp :: { UncheckedSigExp }-             : QualName            { let (v, loc) = $1 in SigVar v NoInfo (srclocOf loc) }-             | '(' SigExp ')'      { $2 }+SimpleModTypeExp :: { UncheckedModTypeExp }+             : QualName            { let (v, loc) = $1 in ModTypeVar v NoInfo (srclocOf loc) }+             | '(' ModTypeExp ')'      { $2 }  ModBind :: { ModBindBase NoInfo Name }-         : module id ModParams maybeAscription(SigExp) '=' ModExp+         : module id ModParams maybeAscription(ModTypeExp) '=' ModExp            { let L floc (ID fname) = $2;              in ModBind fname $3 (fmap (,NoInfo) $4) $6 Nothing (srcspan $1 $>)            }  ModParam :: { ModParamBase NoInfo Name }-          : '(' id ':' SigExp ')' { let L _ (ID name) = $2 in ModParam name $4 NoInfo (srcspan $1 $>) }+          : '(' id ':' ModTypeExp ')' { let L _ (ID name) = $2 in ModParam name $4 NoInfo (srcspan $1 $>) }  ModParams :: { [ModParamBase NoInfo Name] }            : ModParam ModParams { $1 : $2 }@@ -308,10 +308,10 @@         { let L _ (ID name) = $3           in TypeSpec $2 name $4 Nothing (srcspan $1 $>) } -      | module id ':' SigExp+      | module id ':' ModTypeExp         { let L _ (ID name) = $2           in ModSpec name $4 Nothing (srcspan $1 $>) }-      | include SigExp+      | include ModTypeExp         { IncludeSpec $2 (srcspan $1 $>) }       | Doc Spec         { addDocSpec $1 $2 }
src/Language/Futhark/Pretty.hs view
@@ -460,7 +460,7 @@ instance (Eq vn, IsName vn, Annot f) => Pretty (DecBase f vn) where   pretty (ValDec dec) = pretty dec   pretty (TypeDec dec) = pretty dec-  pretty (SigDec sig) = pretty sig+  pretty (ModTypeDec sig) = pretty sig   pretty (ModDec sd) = pretty sd   pretty (OpenDec x _) = "open" <+> pretty x   pretty (LocalDec dec _) = "local" <+> pretty dec@@ -544,19 +544,19 @@   pretty (IncludeSpec e _) =     "include" <+> pretty e -instance (Eq vn, IsName vn, Annot f) => Pretty (SigExpBase f vn) where-  pretty (SigVar v _ _) = pretty v-  pretty (SigParens e _) = parens $ pretty e-  pretty (SigSpecs ss _) = nestedBlock "{" "}" (stack $ punctuate line $ map pretty ss)-  pretty (SigWith s (TypeRef v ps td _) _) =+instance (Eq vn, IsName vn, Annot f) => Pretty (ModTypeExpBase f vn) where+  pretty (ModTypeVar v _ _) = pretty v+  pretty (ModTypeParens e _) = parens $ pretty e+  pretty (ModTypeSpecs ss _) = nestedBlock "{" "}" (stack $ punctuate line $ map pretty ss)+  pretty (ModTypeWith s (TypeRef v ps td _) _) =     pretty s <+> "with" <+> pretty v <+> hsep (map pretty ps) <> " =" <+> pretty td-  pretty (SigArrow (Just v) e1 e2 _) =+  pretty (ModTypeArrow (Just v) e1 e2 _) =     parens (prettyName v <> colon <+> pretty e1) <+> "->" <+> pretty e2-  pretty (SigArrow Nothing e1 e2 _) =+  pretty (ModTypeArrow Nothing e1 e2 _) =     pretty e1 <+> "->" <+> pretty e2 -instance (Eq vn, IsName vn, Annot f) => Pretty (SigBindBase f vn) where-  pretty (SigBind name e _ _) =+instance (Eq vn, IsName vn, Annot f) => Pretty (ModTypeBindBase f vn) where+  pretty (ModTypeBind name e _ _) =     "module type" <+> prettyName name <+> equals <+> pretty e  instance (Eq vn, IsName vn, Annot f) => Pretty (ModParamBase f vn) where
src/Language/Futhark/Primitive.hs view
@@ -1285,7 +1285,7 @@           FloatType Float32,           \case             [FloatValue (Float32Value x), IntValue (Int32Value y)] ->-              Just $ FloatValue $ Float32Value $ x * (2 ** fromIntegral y)+              Just $ FloatValue $ Float32Value $ ldexpf x $ fromIntegral y             _ -> Nothing         )       ),@@ -1294,7 +1294,7 @@           FloatType Float64,           \case             [FloatValue (Float64Value x), IntValue (Int32Value y)] ->-              Just $ FloatValue $ Float64Value $ x * (2 ** fromIntegral y)+              Just $ FloatValue $ Float64Value $ ldexp x $ fromIntegral y             _ -> Nothing         )       ),
src/Language/Futhark/Prop.hs view
@@ -89,12 +89,12 @@     UncheckedSlice,     UncheckedExp,     UncheckedModExp,-    UncheckedSigExp,+    UncheckedModTypeExp,     UncheckedTypeParam,     UncheckedPat,     UncheckedValBind,     UncheckedTypeBind,-    UncheckedSigBind,+    UncheckedModTypeBind,     UncheckedModBind,     UncheckedDec,     UncheckedSpec,@@ -110,9 +110,9 @@     Pat,     ModExp,     ModParam,-    SigExp,+    ModTypeExp,     ModBind,-    SigBind,+    ModTypeBind,     ValBind,     Dec,     Spec,@@ -1209,7 +1209,7 @@ decImports :: DecBase f vn -> [(String, Loc)] decImports (OpenDec x _) = modExpImports x decImports (ModDec md) = modExpImports $ modExp md-decImports SigDec {} = []+decImports ModTypeDec {} = [] decImports TypeDec {} = [] decImports ValDec {} = [] decImports (LocalDec d _) = decImports d@@ -1236,31 +1236,31 @@       where         onDec OpenDec {} = mempty         onDec ModDec {} = mempty-        onDec (SigDec sb) =-          M.singleton (sigName sb) (onSigExp (sigExp sb))+        onDec (ModTypeDec sb) =+          M.singleton (modTypeName sb) (onModTypeExp (modTypeExp sb))         onDec TypeDec {} = mempty         onDec ValDec {} = mempty         onDec (LocalDec d _) = onDec d         onDec ImportDec {} = mempty -        onSigExp (SigVar v _ _) = S.singleton $ qualLeaf v-        onSigExp (SigParens e _) = onSigExp e-        onSigExp (SigSpecs ss _) = foldMap onSpec ss-        onSigExp (SigWith e _ _) = onSigExp e-        onSigExp (SigArrow _ e1 e2 _) = onSigExp e1 <> onSigExp e2+        onModTypeExp (ModTypeVar v _ _) = S.singleton $ qualLeaf v+        onModTypeExp (ModTypeParens e _) = onModTypeExp e+        onModTypeExp (ModTypeSpecs ss _) = foldMap onSpec ss+        onModTypeExp (ModTypeWith e _ _) = onModTypeExp e+        onModTypeExp (ModTypeArrow _ e1 e2 _) = onModTypeExp e1 <> onModTypeExp e2          onSpec ValSpec {} = mempty         onSpec TypeSpec {} = mempty         onSpec TypeAbbrSpec {} = mempty-        onSpec (ModSpec vn e _ _) = S.singleton vn <> onSigExp e-        onSpec (IncludeSpec e _) = onSigExp e+        onSpec (ModSpec vn e _ _) = S.singleton vn <> onModTypeExp e+        onSpec (IncludeSpec e _) = onModTypeExp e      mtypes_used = foldMap onDec $ progDecs prog       where         onDec (OpenDec x _) = onModExp x         onDec (ModDec md) =-          maybe mempty (onSigExp . fst) (modSignature md) <> onModExp (modExp md)-        onDec SigDec {} = mempty+          maybe mempty (onModTypeExp . fst) (modType md) <> onModExp (modExp md)+        onDec ModTypeDec {} = mempty         onDec TypeDec {} = mempty         onDec ValDec {} = mempty         onDec LocalDec {} = mempty@@ -1271,17 +1271,17 @@         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 (ModAscript me se _ _) = onModExp me <> onModTypeExp se         onModExp (ModLambda p r me _) =-          onModParam p <> maybe mempty (onSigExp . fst) r <> onModExp me+          onModParam p <> maybe mempty (onModTypeExp . fst) r <> onModExp me -        onModParam = onSigExp . modParamType+        onModParam = onModTypeExp . 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+        onModTypeExp (ModTypeVar v _ _) = S.singleton $ qualLeaf v+        onModTypeExp (ModTypeParens e _) = onModTypeExp e+        onModTypeExp ModTypeSpecs {} = mempty+        onModTypeExp (ModTypeWith e _ _) = onModTypeExp e+        onModTypeExp (ModTypeArrow _ e1 e2 _) = onModTypeExp e1 <> onModTypeExp e2  -- | Extract a leading @((name, namespace, file), remainder)@ from a -- documentation comment string.  These are formatted as@@ -1321,7 +1321,7 @@     holesInDec (OpenDec me _) = holesInModExp me     holesInDec (LocalDec d _) = holesInDec d     holesInDec TypeDec {} = mempty-    holesInDec SigDec {} = mempty+    holesInDec ModTypeDec {} = mempty     holesInDec ImportDec {} = mempty      holesInModExp (ModDecs ds _) = foldMap holesInDec ds@@ -1455,7 +1455,7 @@ type ModBind = ModBindBase Info VName  -- | A type-checked module type binding.-type SigBind = SigBindBase Info VName+type ModTypeBind = ModTypeBindBase Info VName  -- | A type-checked module expression. type ModExp = ModExpBase Info VName@@ -1464,7 +1464,7 @@ type ModParam = ModParamBase Info VName  -- | A type-checked module type expression.-type SigExp = SigExpBase Info VName+type ModTypeExp = ModTypeExpBase Info VName  -- | A type-checked declaration. type Dec = DecBase Info VName@@ -1509,7 +1509,7 @@ type UncheckedModExp = ModExpBase NoInfo Name  -- | A module type expression with no type annotations.-type UncheckedSigExp = SigExpBase NoInfo Name+type UncheckedModTypeExp = ModTypeExpBase NoInfo Name  -- | A type parameter with no type annotations. type UncheckedTypeParam = TypeParamBase Name@@ -1524,7 +1524,7 @@ type UncheckedTypeBind = TypeBindBase NoInfo Name  -- | A module type binding with no type annotations.-type UncheckedSigBind = SigBindBase NoInfo Name+type UncheckedModTypeBind = ModTypeBindBase NoInfo Name  -- | A module binding with no type annotations. type UncheckedModBind = ModBindBase NoInfo Name
src/Language/Futhark/Query.hs view
@@ -120,7 +120,7 @@ modParamDefs :: ModParam -> Defs modParamDefs (ModParam p se _ loc) =   M.singleton p (DefBound $ BoundModule $ locOf loc)-    <> sigExpDefs se+    <> modTypeExpDefs se  modExpDefs :: ModExp -> Defs modExpDefs ModVar {} =@@ -143,7 +143,7 @@   M.singleton (modName mbind) (DefBound $ BoundModule $ locOf mbind)     <> mconcat (map modParamDefs (modParams mbind))     <> modExpDefs (modExp mbind)-    <> case modSignature mbind of+    <> case modType mbind of       Nothing -> mempty       Just (_, Info substs) ->         M.map DefIndirect substs@@ -159,28 +159,28 @@       M.singleton v $ DefBound $ BoundType $ locOf loc     ModSpec v se _ loc ->       M.singleton v (DefBound $ BoundModuleType $ locOf loc)-        <> sigExpDefs se-    IncludeSpec se _ -> sigExpDefs se+        <> modTypeExpDefs se+    IncludeSpec se _ -> modTypeExpDefs se -sigExpDefs :: SigExp -> Defs-sigExpDefs se =+modTypeExpDefs :: ModTypeExp -> Defs+modTypeExpDefs se =   case se of-    SigVar _ (Info substs) _ -> M.map DefIndirect substs-    SigParens e _ -> sigExpDefs e-    SigSpecs specs _ -> mconcat $ map specDefs specs-    SigWith e _ _ -> sigExpDefs e-    SigArrow _ e1 e2 _ -> sigExpDefs e1 <> sigExpDefs e2+    ModTypeVar _ (Info substs) _ -> M.map DefIndirect substs+    ModTypeParens e _ -> modTypeExpDefs e+    ModTypeSpecs specs _ -> mconcat $ map specDefs specs+    ModTypeWith e _ _ -> modTypeExpDefs e+    ModTypeArrow _ e1 e2 _ -> modTypeExpDefs e1 <> modTypeExpDefs e2 -sigBindDefs :: SigBind -> Defs+sigBindDefs :: ModTypeBind -> Defs sigBindDefs sbind =-  M.singleton (sigName sbind) (DefBound $ BoundModuleType $ locOf sbind)-    <> sigExpDefs (sigExp sbind)+  M.singleton (modTypeName sbind) (DefBound $ BoundModuleType $ locOf sbind)+    <> modTypeExpDefs (modTypeExp sbind)  decDefs :: Dec -> Defs decDefs (ValDec vbind) = valBindDefs vbind decDefs (TypeDec vbind) = typeBindDefs vbind decDefs (ModDec mbind) = modBindDefs mbind-decDefs (SigDec mbind) = sigBindDefs mbind+decDefs (ModTypeDec mbind) = sigBindDefs mbind decDefs (OpenDec me _) = modExpDefs me decDefs (LocalDec dec _) = decDefs dec decDefs ImportDec {} = mempty@@ -312,19 +312,19 @@     ValSpec _ _ te _ _ _ -> atPosInTypeExp te pos     TypeAbbrSpec tbind -> atPosInTypeBind tbind pos     TypeSpec {} -> Nothing-    ModSpec _ se _ _ -> atPosInSigExp se pos-    IncludeSpec se _ -> atPosInSigExp se pos+    ModSpec _ se _ _ -> atPosInModTypeExp se pos+    IncludeSpec se _ -> atPosInModTypeExp se pos -atPosInSigExp :: SigExp -> Pos -> Maybe RawAtPos-atPosInSigExp se pos =+atPosInModTypeExp :: ModTypeExp -> Pos -> Maybe RawAtPos+atPosInModTypeExp se pos =   case se of-    SigVar qn _ loc -> do+    ModTypeVar 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-    SigArrow _ e1 e2 _ -> atPosInSigExp e1 pos `mplus` atPosInSigExp e2 pos+    ModTypeParens e _ -> atPosInModTypeExp e pos+    ModTypeSpecs specs _ -> msum $ map (`atPosInSpec` pos) specs+    ModTypeWith e _ _ -> atPosInModTypeExp e pos+    ModTypeArrow _ e1 e2 _ -> atPosInModTypeExp e1 pos `mplus` atPosInModTypeExp e2 pos  atPosInValBind :: ValBind -> Pos -> Maybe RawAtPos atPosInValBind vbind pos =@@ -341,12 +341,12 @@     `mplus` atPosInModExp e pos     `mplus` case sig of       Nothing -> Nothing-      Just (se, _) -> atPosInSigExp se pos+      Just (se, _) -> atPosInModTypeExp se pos   where-    inParam (ModParam _ se _ _) = atPosInSigExp se pos+    inParam (ModParam _ se _ _) = atPosInModTypeExp se pos -atPosInSigBind :: SigBind -> Pos -> Maybe RawAtPos-atPosInSigBind = atPosInSigExp . sigExp+atPosInModTypeBind :: ModTypeBind -> Pos -> Maybe RawAtPos+atPosInModTypeBind = atPosInModTypeExp . modTypeExp  atPosInDec :: Dec -> Pos -> Maybe RawAtPos atPosInDec dec pos = do@@ -355,7 +355,7 @@     ValDec vbind -> atPosInValBind vbind pos     TypeDec tbind -> atPosInTypeBind tbind pos     ModDec mbind -> atPosInModBind mbind pos-    SigDec sbind -> atPosInSigBind sbind pos+    ModTypeDec sbind -> atPosInModTypeBind sbind pos     OpenDec e _ -> atPosInModExp e pos     LocalDec dec' _ -> atPosInDec dec' pos     ImportDec {} -> Nothing
src/Language/Futhark/Semantic.hs view
@@ -12,7 +12,7 @@     Namespace (..),     Env (..),     TySet,-    FunSig (..),+    FunModType (..),     NameMap,     BoundV (..),     Mod (..),@@ -96,15 +96,15 @@ -- or a parametric module ("functor" in SML). data Mod   = ModEnv Env-  | ModFun FunSig+  | ModFun FunModType   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+data FunModType = FunModType+  { funModTypeAbs :: TySet,+    funModTypeMod :: Mod,+    funModTypeMty :: MTy   }   deriving (Show) @@ -137,7 +137,7 @@ data Env = Env   { envVtable :: M.Map VName BoundV,     envTypeTable :: M.Map VName TypeBinding,-    envSigTable :: M.Map VName MTy,+    envModTypeTable :: M.Map VName MTy,     envModTable :: M.Map VName Mod,     envNameMap :: NameMap   }@@ -160,7 +160,7 @@  instance Pretty Mod where   pretty (ModEnv e) = pretty e-  pretty (ModFun (FunSig _ mod mty)) = pretty mod <+> "->" </> pretty mty+  pretty (ModFun (FunModType _ mod mty)) = pretty mod <+> "->" </> pretty mty  instance Pretty Env where   pretty (Env vtable ttable sigtable modtable _) =
src/Language/Futhark/Syntax.hs view
@@ -66,9 +66,9 @@     -- * Module language     ImportName (..),     SpecBase (..),-    SigExpBase (..),+    ModTypeExpBase (..),     TypeRefBase (..),-    SigBindBase (..),+    ModTypeBindBase (..),     ModExpBase (..),     ModBindBase (..),     ModParamBase (..),@@ -1123,8 +1123,8 @@   | 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+  | ModSpec vn (ModTypeExpBase f vn) (Maybe DocComment) SrcLoc+  | IncludeSpec (ModTypeExpBase f vn) SrcLoc  deriving instance Show (SpecBase Info VName) @@ -1138,16 +1138,16 @@   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+data ModTypeExpBase f vn+  = ModTypeVar (QualName vn) (f (M.Map VName VName)) SrcLoc+  | ModTypeParens (ModTypeExpBase f vn) SrcLoc+  | ModTypeSpecs [SpecBase f vn] SrcLoc+  | ModTypeWith (ModTypeExpBase f vn) (TypeRefBase f vn) SrcLoc+  | ModTypeArrow (Maybe vn) (ModTypeExpBase f vn) (ModTypeExpBase f vn) SrcLoc -deriving instance Show (SigExpBase Info VName)+deriving instance Show (ModTypeExpBase Info VName) -deriving instance Show (SigExpBase NoInfo Name)+deriving instance Show (ModTypeExpBase NoInfo Name)  -- | A type refinement. data TypeRefBase f vn = TypeRef (QualName vn) [TypeParamBase vn] (TypeExp f vn) SrcLoc@@ -1159,27 +1159,27 @@ 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+instance Located (ModTypeExpBase f vn) where+  locOf (ModTypeVar _ _ loc) = locOf loc+  locOf (ModTypeParens _ loc) = locOf loc+  locOf (ModTypeSpecs _ loc) = locOf loc+  locOf (ModTypeWith _ _ loc) = locOf loc+  locOf (ModTypeArrow _ _ _ loc) = locOf loc  -- | Module type binding.-data SigBindBase f vn = SigBind-  { sigName :: vn,-    sigExp :: SigExpBase f vn,-    sigDoc :: Maybe DocComment,-    sigLoc :: SrcLoc+data ModTypeBindBase f vn = ModTypeBind+  { modTypeName :: vn,+    modTypeExp :: ModTypeExpBase f vn,+    modTypeDoc :: Maybe DocComment,+    modTypeLoc :: SrcLoc   } -deriving instance Show (SigBindBase Info VName)+deriving instance Show (ModTypeBindBase Info VName) -deriving instance Show (SigBindBase NoInfo Name)+deriving instance Show (ModTypeBindBase NoInfo Name) -instance Located (SigBindBase f vn) where-  locOf = locOf . sigLoc+instance Located (ModTypeBindBase f vn) where+  locOf = locOf . modTypeLoc  -- | Canonical reference to a Futhark code file.  Does not include the -- @.fut@ extension.  This is most often a path relative to the@@ -1205,10 +1205,10 @@       (f (M.Map VName VName))       (f (M.Map VName VName))       SrcLoc-  | ModAscript (ModExpBase f vn) (SigExpBase f vn) (f (M.Map VName VName)) SrcLoc+  | ModAscript (ModExpBase f vn) (ModTypeExpBase f vn) (f (M.Map VName VName)) SrcLoc   | ModLambda       (ModParamBase f vn)-      (Maybe (SigExpBase f vn, f (M.Map VName VName)))+      (Maybe (ModTypeExpBase f vn, f (M.Map VName VName)))       (ModExpBase f vn)       SrcLoc @@ -1229,7 +1229,7 @@ data ModBindBase f vn = ModBind   { modName :: vn,     modParams :: [ModParamBase f vn],-    modSignature :: Maybe (SigExpBase f vn, f (M.Map VName VName)),+    modType :: Maybe (ModTypeExpBase f vn, f (M.Map VName VName)),     modExp :: ModExpBase f vn,     modDoc :: Maybe DocComment,     modLocation :: SrcLoc@@ -1245,7 +1245,7 @@ -- | A module parameter. data ModParamBase f vn = ModParam   { modParamName :: vn,-    modParamType :: SigExpBase f vn,+    modParamType :: ModTypeExpBase f vn,     modParamAbs :: f [VName],     modParamLocation :: SrcLoc   }@@ -1261,7 +1261,7 @@ data DecBase f vn   = ValDec (ValBindBase f vn)   | TypeDec (TypeBindBase f vn)-  | SigDec (SigBindBase f vn)+  | ModTypeDec (ModTypeBindBase f vn)   | ModDec (ModBindBase f vn)   | OpenDec (ModExpBase f vn) SrcLoc   | LocalDec (DecBase f vn) SrcLoc@@ -1274,7 +1274,7 @@ instance Located (DecBase f vn) where   locOf (ValDec d) = locOf d   locOf (TypeDec d) = locOf d-  locOf (SigDec d) = locOf d+  locOf (ModTypeDec d) = locOf d   locOf (ModDec d) = locOf d   locOf (OpenDec _ loc) = locOf loc   locOf (LocalDec _ loc) = locOf loc
src/Language/Futhark/TypeChecker.hs view
@@ -178,7 +178,7 @@       check Term (valBindName vb) (srclocOf vb)     f (TypeDec (TypeBind name _ _ _ _ _ loc)) =       check Type name loc-    f (SigDec (SigBind name _ _ loc)) =+    f (ModTypeDec (ModTypeBind name _ _ loc)) =       check Signature name loc     f (ModDec (ModBind name _ _ _ _ loc)) =       check Term name loc@@ -279,7 +279,7 @@ checkSpecs (ModSpec name sig doc loc : specs) =   bindSpaced [(Term, name)] $ do     name' <- checkName Term name loc-    (_sig_abs, mty, sig') <- checkSigExp sig+    (_sig_abs, mty, sig') <- checkModTypeExp sig     let senv =           mempty             { envNameMap = M.singleton (Term, name) $ qualName name',@@ -292,7 +292,7 @@         ModSpec name' sig' doc loc : specs'       ) checkSpecs (IncludeSpec e loc : specs) = do-  (e_abs, env_abs, e_env, e') <- checkSigExpToEnv e+  (e_abs, env_abs, e_env, e') <- checkModTypeExpToEnv e    mapM_ (warnIfShadowing . fmap baseName) $ M.keys env_abs @@ -309,28 +309,28 @@     warnAbout qn =       warn loc $ "Inclusion shadows type" <+> dquotes (pretty qn) <+> "." -checkSigExp :: SigExpBase NoInfo Name -> TypeM (TySet, MTy, SigExpBase Info VName)-checkSigExp (SigParens e loc) = do-  (abs, mty, e') <- checkSigExp e-  pure (abs, mty, SigParens e' loc)-checkSigExp (SigVar name NoInfo loc) = do+checkModTypeExp :: ModTypeExpBase NoInfo Name -> TypeM (TySet, MTy, ModTypeExpBase Info VName)+checkModTypeExp (ModTypeParens e loc) = do+  (abs, mty, e') <- checkModTypeExp e+  pure (abs, mty, ModTypeParens e' loc)+checkModTypeExp (ModTypeVar name NoInfo loc) = do   (name', mty) <- lookupMTy loc name   (mty', substs) <- newNamesForMTy mty-  pure (mtyAbs mty', mty', SigVar name' (Info substs) loc)-checkSigExp (SigSpecs specs loc) = do+  pure (mtyAbs mty', mty', ModTypeVar name' (Info substs) loc)+checkModTypeExp (ModTypeSpecs specs loc) = do   checkForDuplicateSpecs specs   (abstypes, env, specs') <- checkSpecs specs-  pure (abstypes, MTy abstypes $ ModEnv env, SigSpecs specs' loc)-checkSigExp (SigWith s (TypeRef tname ps te trloc) loc) = do-  (abs, s_abs, s_env, s') <- checkSigExpToEnv s+  pure (abstypes, MTy abstypes $ ModEnv env, ModTypeSpecs specs' loc)+checkModTypeExp (ModTypeWith s (TypeRef tname ps te trloc) loc) = do+  (abs, s_abs, s_env, s') <- checkModTypeExpToEnv s   checkTypeParams ps $ \ps' -> do     (ext, te', te_t, _) <- bindingTypeParams ps' $ checkTypeDecl te     unless (null ext) $       typeError te' mempty "Anonymous dimensions are not allowed here."     (tname', s_abs', s_env') <- refineEnv loc s_abs s_env tname ps' te_t-    pure (abs, MTy s_abs' $ ModEnv s_env', SigWith s' (TypeRef tname' ps' te' trloc) loc)-checkSigExp (SigArrow maybe_pname e1 e2 loc) = do-  (e1_abs, MTy s_abs e1_mod, e1') <- checkSigExp e1+    pure (abs, MTy s_abs' $ ModEnv s_env', ModTypeWith s' (TypeRef tname' ps' te' trloc) loc)+checkModTypeExp (ModTypeArrow maybe_pname e1 e2 loc) = do+  (e1_abs, MTy s_abs e1_mod, e1') <- checkModTypeExp e1   (env_for_e2, maybe_pname') <-     case maybe_pname of       Just pname -> bindSpaced [(Term, pname)] $ do@@ -344,34 +344,34 @@           )       Nothing ->         pure (mempty, Nothing)-  (e2_abs, e2_mod, e2') <- localEnv env_for_e2 $ checkSigExp e2+  (e2_abs, e2_mod, e2') <- localEnv env_for_e2 $ checkModTypeExp e2   pure     ( e1_abs <> e2_abs,-      MTy mempty $ ModFun $ FunSig s_abs e1_mod e2_mod,-      SigArrow maybe_pname' e1' e2' loc+      MTy mempty $ ModFun $ FunModType s_abs e1_mod e2_mod,+      ModTypeArrow maybe_pname' e1' e2' loc     ) -checkSigExpToEnv ::-  SigExpBase NoInfo Name ->-  TypeM (TySet, TySet, Env, SigExpBase Info VName)-checkSigExpToEnv e = do-  (abs, MTy mod_abs mod, e') <- checkSigExp e+checkModTypeExpToEnv ::+  ModTypeExpBase NoInfo Name ->+  TypeM (TySet, TySet, Env, ModTypeExpBase Info VName)+checkModTypeExpToEnv e = do+  (abs, MTy mod_abs mod, e') <- checkModTypeExp e   case mod of     ModEnv env -> pure (abs, mod_abs, env, e')     ModFun {} -> unappliedFunctor $ srclocOf e -checkSigBind :: SigBindBase NoInfo Name -> TypeM (TySet, Env, SigBindBase Info VName)-checkSigBind (SigBind name e doc loc) = do-  (abs, env, e') <- checkSigExp e+checkModTypeBind :: ModTypeBindBase NoInfo Name -> TypeM (TySet, Env, ModTypeBindBase Info VName)+checkModTypeBind (ModTypeBind name e doc loc) = do+  (abs, env, e') <- checkModTypeExp e   bindSpaced [(Signature, name)] $ do     name' <- checkName Signature name loc     pure       ( abs,         mempty-          { envSigTable = M.singleton name' env,+          { envModTypeTable = M.singleton name' env,             envNameMap = M.singleton (Signature, name) (qualName name')           },-        SigBind name' e' doc loc+        ModTypeBind name' e' doc loc       )  checkOneModExp ::@@ -418,7 +418,7 @@       typeError loc mempty "Cannot apply non-parametric module." checkOneModExp (ModAscript me se NoInfo loc) = do   (me_abs, me_mod, me') <- checkOneModExp me-  (se_abs, se_mty, se') <- checkSigExp se+  (se_abs, se_mty, se') <- checkModTypeExp se   match_subst <- badOnLeft $ matchMTys me_mod se_mty (locOf loc)   pure (se_abs <> me_abs, se_mty, ModAscript me' se' (Info match_subst) loc) checkOneModExp (ModLambda param maybe_fsig_e body_e loc) =@@ -427,7 +427,7 @@       checkModBody (fst <$> maybe_fsig_e) body_e loc     pure       ( abs,-        MTy mempty $ ModFun $ FunSig param_abs param_mod mty,+        MTy mempty $ ModFun $ FunModType param_abs param_mod mty,         ModLambda param' maybe_fsig_e' body_e' loc       ) @@ -443,7 +443,7 @@   (ModParamBase Info VName -> TySet -> Mod -> TypeM a) ->   TypeM a withModParam (ModParam pname psig_e NoInfo loc) m = do-  (_abs, MTy p_abs p_mod, psig_e') <- checkSigExp psig_e+  (_abs, MTy p_abs p_mod, psig_e') <- checkModTypeExp psig_e   bindSpaced [(Term, pname)] $ do     pname' <- checkName Term pname loc     let in_body_env = mempty {envModTable = M.singleton pname' p_mod}@@ -460,12 +460,12 @@     withModParams ps $ \ps' -> m $ (p', pabs, pmod) : ps'  checkModBody ::-  Maybe (SigExpBase NoInfo Name) ->+  Maybe (ModTypeExpBase NoInfo Name) ->   ModExpBase NoInfo Name ->   SrcLoc ->   TypeM     ( TySet,-      Maybe (SigExp, Info (M.Map VName VName)),+      Maybe (ModTypeExp, Info (M.Map VName VName)),       ModExp,       MTy     )@@ -480,7 +480,7 @@           body_mty         )     Just fsig_e -> do-      (fsig_abs, fsig_mty, fsig_e') <- checkSigExp fsig_e+      (fsig_abs, fsig_mty, fsig_e') <- checkModTypeExp fsig_e       fsig_subst <- badOnLeft $ matchMTys body_mty fsig_mty (locOf loc)       pure         ( fsig_abs <> body_e_abs,@@ -508,13 +508,13 @@       withModParams ps $ \params_stuff -> do         let (ps', ps_abs, ps_mod) = unzip3 params_stuff         (abs, 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'+        let addParam (x, y) mty' = MTy mempty $ ModFun $ FunModType x y mty'         pure           ( abs,             p' : ps',             maybe_fsig_e',             body_e',-            FunSig p_abs p_mod $ foldr addParam mty $ zip ps_abs ps_mod+            FunModType p_abs p_mod $ foldr addParam mty $ zip ps_abs ps_mod           )   bindSpaced [(Term, name)] $ do     name' <- checkName Term name loc@@ -755,9 +755,9 @@ checkOneDec (ModDec struct) = do   (abs, modenv, struct') <- checkModBind struct   pure (abs, modenv, ModDec struct')-checkOneDec (SigDec sig) = do-  (abs, sigenv, sig') <- checkSigBind sig-  pure (abs, sigenv, SigDec sig')+checkOneDec (ModTypeDec sig) = do+  (abs, sigenv, sig') <- checkModTypeBind sig+  pure (abs, sigenv, ModTypeDec sig') checkOneDec (TypeDec tdec) = do   (tenv, tdec') <- checkTypeBind tdec   pure (mempty, tenv, TypeDec tdec')
src/Language/Futhark/TypeChecker/Modules.hs view
@@ -25,8 +25,8 @@ substituteTypesInMod :: TypeSubs -> Mod -> Mod substituteTypesInMod substs (ModEnv e) =   ModEnv $ substituteTypesInEnv substs e-substituteTypesInMod substs (ModFun (FunSig abs mod mty)) =-  ModFun $ FunSig abs (substituteTypesInMod substs mod) (substituteTypesInMTy substs mty)+substituteTypesInMod substs (ModFun (FunModType abs mod mty)) =+  ModFun $ FunModType abs (substituteTypesInMod substs mod) (substituteTypesInMTy substs mty)  substituteTypesInMTy :: TypeSubs -> MTy -> MTy substituteTypesInMTy substs (MTy abs mod) = MTy abs $ substituteTypesInMod substs mod@@ -97,7 +97,7 @@            in Env                 { envVtable = vtable',                   envTypeTable = ttable',-                  envSigTable = mempty,+                  envModTypeTable = mempty,                   envModTable = mtable',                   envNameMap = M.map (fmap substitute) names                 }@@ -122,10 +122,10 @@         substituteInMod (ModEnv env) =           ModEnv $ substituteInEnv env         substituteInMod (ModFun funsig) =-          ModFun $ substituteInFunSig funsig+          ModFun $ substituteInFunModType funsig -        substituteInFunSig (FunSig abs mod mty) =-          FunSig+        substituteInFunModType (FunModType abs mod mty) =+          FunModType             (M.mapKeys (fmap substitute) abs)             (substituteInMod mod)             (substituteInMTy substs mty)@@ -169,7 +169,7 @@ modTypeAbbrs :: Mod -> M.Map VName TypeBinding modTypeAbbrs (ModEnv env) =   envTypeAbbrs env-modTypeAbbrs (ModFun (FunSig _ mod mty)) =+modTypeAbbrs (ModFun (FunModType _ mod mty)) =   modTypeAbbrs mod <> mtyTypeAbbrs mty  envTypeAbbrs :: Env -> M.Map VName TypeBinding@@ -309,8 +309,8 @@     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 (funModTypeMod mod_fun) (funModTypeMod sig_fun)+        <> resolveMTyNames' (funModTypeMty mod_fun) (funModTypeMty sig_fun)     resolveModNames _ _ =       mempty @@ -453,8 +453,8 @@     matchMods       old_abs_subst_to_type       quals-      (ModFun (FunSig mod_abs mod_pmod mod_mod))-      (ModFun (FunSig sig_abs sig_pmod sig_mod))+      (ModFun (FunModType mod_abs mod_pmod mod_mod))+      (ModFun (FunModType sig_abs sig_pmod sig_mod))       loc = do         -- We need to use different substitutions when matching         -- parameter and body signatures - this is because the@@ -602,14 +602,14 @@ -- | Apply a parametric module to an argument. applyFunctor ::   Loc ->-  FunSig ->+  FunModType ->   MTy ->   TypeM     ( MTy,       M.Map VName VName,       M.Map VName VName     )-applyFunctor applyloc (FunSig p_abs p_mod body_mty) a_mty = do+applyFunctor applyloc (FunModType 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.
src/Language/Futhark/TypeChecker/Monad.hs view
@@ -30,7 +30,7 @@     module Language.Futhark.Warnings,     Env (..),     TySet,-    FunSig (..),+    FunModType (..),     ImportTable,     NameMap,     BoundV (..),@@ -240,7 +240,7 @@ lookupMTy :: SrcLoc -> QualName Name -> TypeM (QualName VName, MTy) lookupMTy loc qn = do   (scope, qn'@(QualName _ name)) <- checkQualNameWithEnv Signature qn loc-  (qn',) <$> maybe explode pure (M.lookup name $ envSigTable scope)+  (qn',) <$> maybe explode pure (M.lookup name $ envModTypeTable scope)   where     explode = unknownVariable Signature qn loc 
src/Language/Futhark/TypeChecker/Terms.hs view
@@ -631,7 +631,7 @@                 let f x = if x == v then Just (ExpSubst e') else Nothing                 pure (applySubst f body_t, [])           _ ->-            unscopeType loc (patNames pat') body_t+            unscopeType loc (map sizeName sizes' <> patNames pat') body_t        pure $         AppExp@@ -1695,7 +1695,7 @@     let keep_type_vars = overloadedTypeVars now_substs      cur_lvl <- curLevel-    let candidate k (lvl, _) = (k `S.notMember` keep_type_vars) && lvl >= cur_lvl+    let candidate k (lvl, _) = (k `S.notMember` keep_type_vars) && lvl >= (cur_lvl - length params)         new_substs = M.filterWithKey candidate now_substs      (tparams', RetType ret_dims restype') <-
src/Language/Futhark/TypeChecker/Terms/Pat.hs view
@@ -343,7 +343,7 @@   checkTypeParams tps $ \tps' -> bindingTypeParams tps' $ do     let descend ps' (p : ps) =           checkPat [] p NoneInferred $ \p' ->-            binding (patIdents $ fmap toStruct p') $ descend (p' : ps') ps+            binding (patIdents $ fmap toStruct p') $ incLevel $ descend (p' : ps') ps         descend ps' [] = m tps' $ reverse ps'      descend [] orig_ps
src/Language/Futhark/TypeChecker/Types.hs view
@@ -547,11 +547,11 @@           }  applyType ::-  (Monoid als) =>+  (Monoid u) =>   [TypeParam] ->-  TypeBase Size als ->+  TypeBase Size u ->   [StructTypeArg] ->-  TypeBase Size als+  TypeBase Size u applyType ps t args = substTypesAny (`M.lookup` substs) t   where     substs = M.fromList $ zipWith mkSubst ps args@@ -564,10 +564,10 @@       error $ "applyType mkSubst: cannot substitute " ++ prettyString a ++ " for " ++ prettyString p  substTypesRet ::-  (Monoid as) =>-  (VName -> Maybe (Subst (RetTypeBase Size as))) ->-  TypeBase Size as ->-  RetTypeBase Size as+  (Monoid u) =>+  (VName -> Maybe (Subst (RetTypeBase Size u))) ->+  TypeBase Size u ->+  RetTypeBase Size u substTypesRet lookupSubst ot =   uncurry (flip RetType) $ runState (onType ot) []   where@@ -614,8 +614,8 @@           pure $ Scalar $ TypeVar u v targs'     onType (Scalar (Record ts)) =       Scalar . Record <$> traverse onType ts-    onType (Scalar (Arrow als v d t1 t2)) =-      Scalar <$> (Arrow als v d <$> onType t1 <*> onRetType t2)+    onType (Scalar (Arrow u v d t1 t2)) =+      Scalar <$> (Arrow u v d <$> onType t1 <*> onRetType t2)     onType (Scalar (Sum ts)) =       Scalar . Sum <$> traverse (traverse onType) ts @@ -642,10 +642,10 @@ -- | 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 (RetTypeBase Size as))) ->-  TypeBase Size as ->-  TypeBase Size as+  (Monoid u) =>+  (VName -> Maybe (Subst (RetTypeBase Size u))) ->+  TypeBase Size u ->+  TypeBase Size u substTypesAny lookupSubst ot =   case substTypesRet lookupSubst ot of     RetType [] ot' -> ot'