futhark 0.9.1 → 0.10.1
raw patch · 115 files changed
+3602/−8917 lines, 115 filesdep +utf8-stringdep −HUnitdep −bifunctorsdep −extradep ~basedep ~containersdep ~megaparsecPVP ok
version bump matches the API change (PVP)
Dependencies added: utf8-string
Dependencies removed: HUnit, bifunctors, extra, raw-strings-qq, semigroups, th-lift-instances
Dependency ranges changed: base, containers, megaparsec, mtl, parser-combinators, text
API changes (from Hackage documentation)
- Futhark.Optimise.MemoryBlockMerging: memoryBlockMergingCoalescing :: Pass ExplicitMemory ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging: memoryBlockMergingReuse :: Pass ExplicitMemory ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging.ActualVariables: findActualVariables :: VarMemMappings MemorySrc -> FirstUses -> FunDef ExplicitMemory -> ActualVariables
- Futhark.Optimise.MemoryBlockMerging.ActualVariables: instance Control.Monad.Reader.Class.MonadReader Futhark.Optimise.MemoryBlockMerging.ActualVariables.Context (Futhark.Optimise.MemoryBlockMerging.ActualVariables.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.ActualVariables: instance Control.Monad.State.Class.MonadState Futhark.Optimise.MemoryBlockMerging.Types.ActualVariables (Futhark.Optimise.MemoryBlockMerging.ActualVariables.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.ActualVariables: instance Futhark.Optimise.MemoryBlockMerging.ActualVariables.LookInKernelExp Futhark.Representation.ExplicitMemory.ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging.ActualVariables: instance Futhark.Optimise.MemoryBlockMerging.ActualVariables.LookInKernelExp Futhark.Representation.ExplicitMemory.InKernel
- Futhark.Optimise.MemoryBlockMerging.ActualVariables: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.ActualVariables.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.ActualVariables: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.ActualVariables.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.ActualVariables: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.ActualVariables.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.ActualVariables: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.ActualVariables.Context
- Futhark.Optimise.MemoryBlockMerging.AllExpVars: findAllExpVars :: LoreConstraints lore => Exp lore -> Names
- Futhark.Optimise.MemoryBlockMerging.AllExpVars: instance Control.Monad.Writer.Class.MonadWriter Futhark.Representation.AST.Syntax.Core.Names (Futhark.Optimise.MemoryBlockMerging.AllExpVars.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.AllExpVars: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.AllExpVars.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.AllExpVars: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.AllExpVars.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.AllExpVars: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.AllExpVars.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo: AuxiliaryInfo :: Name -> VarMemMappings MemorySrc -> MemAliases -> VarAliases -> FirstUses -> LastUses -> Interferences -> PotentialKernelDataRaceInterferences -> ActualVariables -> Names -> AuxiliaryInfo
- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo: [auxActualVariables] :: AuxiliaryInfo -> ActualVariables
- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo: [auxExistentials] :: AuxiliaryInfo -> Names
- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo: [auxFirstUses] :: AuxiliaryInfo -> FirstUses
- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo: [auxInterferences] :: AuxiliaryInfo -> Interferences
- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo: [auxLastUses] :: AuxiliaryInfo -> LastUses
- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo: [auxMemAliases] :: AuxiliaryInfo -> MemAliases
- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo: [auxName] :: AuxiliaryInfo -> Name
- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo: [auxPotentialKernelDataRaceInterferences] :: AuxiliaryInfo -> PotentialKernelDataRaceInterferences
- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo: [auxVarAliases] :: AuxiliaryInfo -> VarAliases
- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo: [auxVarMemMappings] :: AuxiliaryInfo -> VarMemMappings MemorySrc
- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo: data AuxiliaryInfo
- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo: getAuxiliaryInfo :: FunDef ExplicitMemory -> AuxiliaryInfo
- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo.AuxiliaryInfo
- Futhark.Optimise.MemoryBlockMerging.Coalescing: coalesceInProg :: Prog ExplicitMemory -> PassM (Prog ExplicitMemory)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.AllocationMovingUp: moveUpAllocsFunDef :: FunDef ExplicitMemory -> FunDef ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Core: coreCoalesceFunDef :: MonadFreshNames m => FunDef ExplicitMemory -> VarMemMappings MemorySrc -> MemAliases -> VarAliases -> FirstUses -> LastUses -> ActualVariables -> Names -> m (FunDef ExplicitMemory)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Core: instance Control.Monad.Reader.Class.MonadReader Futhark.Optimise.MemoryBlockMerging.Coalescing.Core.Context (Futhark.Optimise.MemoryBlockMerging.Coalescing.Core.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Core: instance Control.Monad.State.Class.MonadState Futhark.Optimise.MemoryBlockMerging.Coalescing.Core.Current (Futhark.Optimise.MemoryBlockMerging.Coalescing.Core.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Core: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.Coalescing.Core.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Core: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.Coalescing.Core.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Core: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.Coalescing.Core.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Core: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Coalescing.Core.Context
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Core: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Coalescing.Core.Current
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps: Exp :: Int -> Int -> Exp lore -> Exp'
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps: data Exp'
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps: findExpsFunDef :: LoreConstraints lore => FunDef lore -> Exps
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps: instance Control.Monad.Writer.Class.MonadWriter Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps.Exps (Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps.Exp'
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2: findSafetyCondition2FunDef :: FunDef ExplicitMemory -> AllocatedBlocksBeforeCreation
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2: instance Control.Monad.State.Class.MonadState Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2.CurrentAllocatedBlocks (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2: instance Control.Monad.Writer.Class.MonadWriter Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2.AllocatedBlocksBeforeCreation (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2: instance Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2.IsAlloc Futhark.Representation.ExplicitMemory.ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2: instance Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2.IsAlloc Futhark.Representation.ExplicitMemory.InKernel
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3: getVarUsesBetween :: FunDef ExplicitMemory -> VName -> VName -> Names
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3: instance Control.Monad.Reader.Class.MonadReader Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3.Context (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3: instance Control.Monad.State.Class.MonadState Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3.Current (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3.Context
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3.Current
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5: findSafetyCondition5FunDef :: FunDef ExplicitMemory -> FirstUses -> VarsInUseBeforeMem
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5: instance Control.Monad.Reader.Class.MonadReader Futhark.Optimise.MemoryBlockMerging.Types.FirstUses (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5: instance Control.Monad.State.Class.MonadState Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5.DeclarationsSoFar (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5: instance Control.Monad.Writer.Class.MonadWriter Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5.VarsInUseBeforeMem (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5: instance Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5.ExtractKernelDefVars Futhark.Representation.ExplicitMemory.ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5: instance Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5.ExtractKernelDefVars Futhark.Representation.ExplicitMemory.InKernel
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.CrudeMovingUp: instance GHC.Classes.Eq Futhark.Optimise.MemoryBlockMerging.CrudeMovingUp.Origin
- Futhark.Optimise.MemoryBlockMerging.CrudeMovingUp: instance GHC.Classes.Ord Futhark.Optimise.MemoryBlockMerging.CrudeMovingUp.Origin
- Futhark.Optimise.MemoryBlockMerging.CrudeMovingUp: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.CrudeMovingUp.Origin
- Futhark.Optimise.MemoryBlockMerging.CrudeMovingUp: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.CrudeMovingUp.PrimBinding
- Futhark.Optimise.MemoryBlockMerging.CrudeMovingUp: moveUpInFunDef :: FunDef ExplicitMemory -> (Body ExplicitMemory -> Maybe [FParam ExplicitMemory] -> [VName]) -> FunDef ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging.Existentials: findExistentials :: LoreConstraints lore => FunDef lore -> Names
- Futhark.Optimise.MemoryBlockMerging.Existentials: instance Control.Monad.Writer.Class.MonadWriter Futhark.Representation.AST.Syntax.Core.Names (Futhark.Optimise.MemoryBlockMerging.Existentials.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Existentials: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.Existentials.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Existentials: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.Existentials.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Existentials: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.Existentials.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse: createsNewArrayBase :: Exp lore -> Bool
- Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse: findFirstUses :: VarMemMappings MemorySrc -> MemAliases -> FunDef ExplicitMemory -> FirstUses
- Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse: instance Control.Monad.Reader.Class.MonadReader Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse.Context (Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse: instance Control.Monad.State.Class.MonadState Futhark.Optimise.MemoryBlockMerging.Types.FirstUses (Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse: instance Control.Monad.Writer.Class.MonadWriter () (Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse: instance Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse.ArrayUtils Futhark.Representation.ExplicitMemory.ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse: instance Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse.ArrayUtils Futhark.Representation.ExplicitMemory.InKernel
- Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse.Context
- Futhark.Optimise.MemoryBlockMerging.Liveness.Interference: findInterferences :: VarMemMappings MemorySrc -> MemAliases -> FirstUses -> LastUses -> Names -> FunDef ExplicitMemory -> (Interferences, PotentialKernelDataRaceInterferences)
- Futhark.Optimise.MemoryBlockMerging.Liveness.Interference: instance Control.Monad.Reader.Class.MonadReader Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.Context (Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.Interference: instance Control.Monad.State.Class.MonadState Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.Current (Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.Interference: instance Control.Monad.Writer.Class.MonadWriter Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.InterferencesList (Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.Interference: instance Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.KernelInterferences Futhark.Representation.ExplicitMemory.ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging.Liveness.Interference: instance Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.KernelInterferences Futhark.Representation.ExplicitMemory.InKernel
- Futhark.Optimise.MemoryBlockMerging.Liveness.Interference: instance Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.SpecialBodyExceptions Futhark.Representation.ExplicitMemory.ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging.Liveness.Interference: instance Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.SpecialBodyExceptions Futhark.Representation.ExplicitMemory.InKernel
- Futhark.Optimise.MemoryBlockMerging.Liveness.Interference: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.Interference: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.Interference: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.Interference: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.Context
- Futhark.Optimise.MemoryBlockMerging.Liveness.Interference: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Liveness.Interference.Current
- Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse: findLastUses :: VarMemMappings MemorySrc -> MemAliases -> FirstUses -> Names -> FunDef ExplicitMemory -> LastUses
- Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse: instance Control.Monad.Reader.Class.MonadReader Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse.Context (Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse: instance Control.Monad.State.Class.MonadState Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse.Current (Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse: instance Control.Monad.Writer.Class.MonadWriter Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse.LastUsesList (Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse.Context
- Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse.Current
- Futhark.Optimise.MemoryBlockMerging.MemoryAliases: findMemAliases :: FunDef ExplicitMemory -> VarMemMappings MemorySrc -> MemAliases
- Futhark.Optimise.MemoryBlockMerging.MemoryAliases: instance Control.Monad.Reader.Class.MonadReader (Futhark.Optimise.MemoryBlockMerging.Types.VarMemMappings Futhark.Optimise.MemoryBlockMerging.Types.MemorySrc) (Futhark.Optimise.MemoryBlockMerging.MemoryAliases.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.MemoryAliases: instance Control.Monad.Writer.Class.MonadWriter [Futhark.Optimise.MemoryBlockMerging.Types.MemAliases] (Futhark.Optimise.MemoryBlockMerging.MemoryAliases.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.MemoryAliases: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.MemoryAliases.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.MemoryAliases: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.MemoryAliases.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.MemoryAliases: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.MemoryAliases.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.MemoryUpdater: instance Control.Monad.Reader.Class.MonadReader Futhark.Optimise.MemoryBlockMerging.MemoryUpdater.Context (Futhark.Optimise.MemoryBlockMerging.MemoryUpdater.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.MemoryUpdater: instance Control.Monad.State.Class.MonadState (Futhark.FreshNames.VNameSource, [(Futhark.Optimise.MemoryBlockMerging.Types.MName, Language.Futhark.Core.VName)]) (Futhark.Optimise.MemoryBlockMerging.MemoryUpdater.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.MemoryUpdater: instance Futhark.MonadFreshNames.MonadFreshNames (Futhark.Optimise.MemoryBlockMerging.MemoryUpdater.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.MemoryUpdater: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.MemoryUpdater.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.MemoryUpdater: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.MemoryUpdater.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.MemoryUpdater: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.MemoryUpdater.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.MemoryUpdater: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.MemoryUpdater.Context
- Futhark.Optimise.MemoryBlockMerging.MemoryUpdater: transformFromVarMemMappings :: MonadFreshNames m => VarMemMappings MemoryLoc -> VarMemMappings MName -> Map MName SubExp -> Map MName SubExp -> Bool -> FunDef ExplicitMemory -> m (FunDef ExplicitMemory)
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: (<&&>) :: Monad m => m Bool -> m Bool -> m Bool
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: (<||>) :: Monad m => m Bool -> m Bool -> m Bool
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: anyM :: Monad m => (a -> m Bool) -> [a] -> m Bool
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: class FullMap lore
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: class FullWalk lore
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: class FullWalkAliases lore
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: expandIxFun :: Map VName (PrimExp VName) -> IxFun -> IxFun
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: expandPrimExp :: Map VName (PrimExp VName) -> PrimExp VName -> PrimExp VName
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: expandWithAliases :: forall v. Ord v => MemAliases -> Map v Names -> Map v Names
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: filterSetM :: (Ord a, Monad m) => (a -> m Bool) -> Set a -> m (Set a)
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: fixpointIterateMay :: (a -> Maybe a) -> a -> a
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: fromJust :: String -> Maybe a -> a
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: fullMapExpM :: (FullMap lore, Monad m) => Mapper lore lore m -> KernelMapper InKernel InKernel m -> Exp lore -> m (Exp lore)
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: fullWalkAliasesExpM :: (FullWalkAliases lore, Monad m) => Walker (Aliases lore) m -> KernelWalker (Aliases InKernel) m -> Exp (Aliases lore) -> m ()
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: fullWalkExpM :: (FullWalk lore, Monad m) => Walker lore m -> KernelWalker InKernel m -> Exp lore -> m ()
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: insertOrNew :: Ord a => Set a -> Maybe (Set a) -> Maybe (Set a)
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: insertOrUpdate :: (Ord k, Ord v) => k -> v -> Map k (Set v) -> Map k (Set v)
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: insertOrUpdateMany :: (Ord k, Ord v) => k -> Set v -> Map k (Set v) -> Map k (Set v)
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: instance Futhark.Optimise.MemoryBlockMerging.Miscellaneous.FullMap Futhark.Representation.ExplicitMemory.ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: instance Futhark.Optimise.MemoryBlockMerging.Miscellaneous.FullMap Futhark.Representation.ExplicitMemory.InKernel
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: instance Futhark.Optimise.MemoryBlockMerging.Miscellaneous.FullWalk Futhark.Representation.ExplicitMemory.ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: instance Futhark.Optimise.MemoryBlockMerging.Miscellaneous.FullWalk Futhark.Representation.ExplicitMemory.InKernel
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: instance Futhark.Optimise.MemoryBlockMerging.Miscellaneous.FullWalkAliases Futhark.Representation.ExplicitMemory.ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: instance Futhark.Optimise.MemoryBlockMerging.Miscellaneous.FullWalkAliases Futhark.Representation.ExplicitMemory.InKernel
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: lookupEmptyable :: (Ord a, Monoid b) => a -> Map a b -> b
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: makeCommutativeMap :: Ord v => Map v (Set v) -> Map v (Set v)
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: mapFromListSetUnion :: (Ord k, Ord v) => [(k, Set v)] -> Map k (Set v)
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: mapMaybeM :: Monad m => (a -> m (Maybe b)) -> [a] -> m [b]
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: maybeFromBoolM :: Monad m => (a -> m Bool) -> a -> m (Maybe a)
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: newDeclarationsStm :: Stm lore -> [VName]
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: removeEmptyMaps :: Map k (Set v) -> Map k (Set v)
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: removeKeyFromMapElems :: Ord k => Map k (Set k) -> Map k (Set k)
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: sortByKeyM :: (Ord t, Monad m) => (a -> m t) -> [a] -> m [a]
- Futhark.Optimise.MemoryBlockMerging.Miscellaneous: whenM :: Monad m => m Bool -> m () -> m ()
- Futhark.Optimise.MemoryBlockMerging.PrimExps: findPrimExpsFunDef :: FunDef ExplicitMemory -> PrimExps
- Futhark.Optimise.MemoryBlockMerging.PrimExps: instance Control.Monad.State.Class.MonadState Futhark.Optimise.MemoryBlockMerging.PrimExps.CurrentTypes (Futhark.Optimise.MemoryBlockMerging.PrimExps.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.PrimExps: instance Control.Monad.Writer.Class.MonadWriter Futhark.Optimise.MemoryBlockMerging.PrimExps.PrimExps (Futhark.Optimise.MemoryBlockMerging.PrimExps.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.PrimExps: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.PrimExps.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.PrimExps: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.PrimExps.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.PrimExps: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.PrimExps.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse: reuseInProg :: Prog ExplicitMemory -> PassM (Prog ExplicitMemory)
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeMovingUp: moveUpAllocSizesFunDef :: FunDef ExplicitMemory -> FunDef ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses: findSizeUsesFunDef :: FunDef ExplicitMemory -> UsesBefore
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses: instance Control.Monad.Reader.Class.MonadReader Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses.SizeVars (Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses: instance Control.Monad.State.Class.MonadState Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses.DeclarationsSoFar (Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses: instance Control.Monad.Writer.Class.MonadWriter Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses.UsesBefore (Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes: instance Control.Monad.Writer.Class.MonadWriter Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes.Sizes (Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes: instance Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes.AllocSizeUtils Futhark.Representation.ExplicitMemory.ExplicitMemory
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes: instance Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes.AllocSizeUtils Futhark.Representation.ExplicitMemory.InKernel
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes: memBlockSizesFunDef :: LoreConstraints lore => FunDef lore -> Sizes
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes: memBlockSizesParamsBodyNonRec :: LoreConstraints lore => [FParam lore] -> Body lore -> Sizes
- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes: type Sizes = Map MName (SubExp, Space)
- Futhark.Optimise.MemoryBlockMerging.Reuse.Core: coreReuseFunDef :: MonadFreshNames m => FunDef ExplicitMemory -> FirstUses -> Interferences -> PotentialKernelDataRaceInterferences -> VarMemMappings MemorySrc -> ActualVariables -> Names -> m (FunDef ExplicitMemory)
- Futhark.Optimise.MemoryBlockMerging.Reuse.Core: instance Control.Monad.Reader.Class.MonadReader Futhark.Optimise.MemoryBlockMerging.Reuse.Core.Context (Futhark.Optimise.MemoryBlockMerging.Reuse.Core.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.Core: instance Control.Monad.State.Class.MonadState Futhark.Optimise.MemoryBlockMerging.Reuse.Core.Current (Futhark.Optimise.MemoryBlockMerging.Reuse.Core.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.Core: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.Reuse.Core.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.Core: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.Reuse.Core.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.Core: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.Reuse.Core.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.Reuse.Core: instance GHC.Classes.Eq Futhark.Optimise.MemoryBlockMerging.Reuse.Core.VarWithLooseEquality
- Futhark.Optimise.MemoryBlockMerging.Reuse.Core: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Reuse.Core.Context
- Futhark.Optimise.MemoryBlockMerging.Reuse.Core: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Reuse.Core.Current
- Futhark.Optimise.MemoryBlockMerging.Reuse.Core: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Reuse.Core.Replacement
- Futhark.Optimise.MemoryBlockMerging.Reuse.Core: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Reuse.Core.VarWithLooseEquality
- Futhark.Optimise.MemoryBlockMerging.Types: FromRes :: VName -> StmOrRes
- Futhark.Optimise.MemoryBlockMerging.Types: FromStm :: VName -> StmOrRes
- Futhark.Optimise.MemoryBlockMerging.Types: MemoryLoc :: MName -> IxFun -> MemoryLoc
- Futhark.Optimise.MemoryBlockMerging.Types: MemorySrc :: MName -> IxFun -> Shape -> MemorySrc
- Futhark.Optimise.MemoryBlockMerging.Types: [memLocIxFun] :: MemoryLoc -> IxFun
- Futhark.Optimise.MemoryBlockMerging.Types: [memLocName] :: MemoryLoc -> MName
- Futhark.Optimise.MemoryBlockMerging.Types: [memSrcIxFun] :: MemorySrc -> IxFun
- Futhark.Optimise.MemoryBlockMerging.Types: [memSrcName] :: MemorySrc -> MName
- Futhark.Optimise.MemoryBlockMerging.Types: [memSrcShape] :: MemorySrc -> Shape
- Futhark.Optimise.MemoryBlockMerging.Types: data MemoryLoc
- Futhark.Optimise.MemoryBlockMerging.Types: data MemorySrc
- Futhark.Optimise.MemoryBlockMerging.Types: data StmOrRes
- Futhark.Optimise.MemoryBlockMerging.Types: instance GHC.Base.Monoid Futhark.Optimise.MemoryBlockMerging.Types.Log
- Futhark.Optimise.MemoryBlockMerging.Types: instance GHC.Base.Semigroup Futhark.Optimise.MemoryBlockMerging.Types.Log
- Futhark.Optimise.MemoryBlockMerging.Types: instance GHC.Classes.Eq Futhark.Optimise.MemoryBlockMerging.Types.Log
- Futhark.Optimise.MemoryBlockMerging.Types: instance GHC.Classes.Eq Futhark.Optimise.MemoryBlockMerging.Types.MemoryLoc
- Futhark.Optimise.MemoryBlockMerging.Types: instance GHC.Classes.Eq Futhark.Optimise.MemoryBlockMerging.Types.MemorySrc
- Futhark.Optimise.MemoryBlockMerging.Types: instance GHC.Classes.Eq Futhark.Optimise.MemoryBlockMerging.Types.StmOrRes
- Futhark.Optimise.MemoryBlockMerging.Types: instance GHC.Classes.Ord Futhark.Optimise.MemoryBlockMerging.Types.Log
- Futhark.Optimise.MemoryBlockMerging.Types: instance GHC.Classes.Ord Futhark.Optimise.MemoryBlockMerging.Types.StmOrRes
- Futhark.Optimise.MemoryBlockMerging.Types: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Types.Log
- Futhark.Optimise.MemoryBlockMerging.Types: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Types.MemoryLoc
- Futhark.Optimise.MemoryBlockMerging.Types: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Types.MemorySrc
- Futhark.Optimise.MemoryBlockMerging.Types: instance GHC.Show.Show Futhark.Optimise.MemoryBlockMerging.Types.StmOrRes
- Futhark.Optimise.MemoryBlockMerging.Types: type ActualVariables = Map VName Names
- Futhark.Optimise.MemoryBlockMerging.Types: type FirstUses = Map VName MNames
- Futhark.Optimise.MemoryBlockMerging.Types: type Interferences = Map MName MNames
- Futhark.Optimise.MemoryBlockMerging.Types: type KernelFirstUse = (MName, VName, PrimType, IxFun)
- Futhark.Optimise.MemoryBlockMerging.Types: type LastUses = Map StmOrRes MNames
- Futhark.Optimise.MemoryBlockMerging.Types: type MName = VName
- Futhark.Optimise.MemoryBlockMerging.Types: type MNames = Names
- Futhark.Optimise.MemoryBlockMerging.Types: type MemAliases = Map MName MNames
- Futhark.Optimise.MemoryBlockMerging.Types: type PotentialKernelDataRaceInterferenceGroup = [KernelFirstUse]
- Futhark.Optimise.MemoryBlockMerging.Types: type PotentialKernelDataRaceInterferences = [PotentialKernelDataRaceInterferenceGroup]
- Futhark.Optimise.MemoryBlockMerging.Types: type VarAliases = Map VName Names
- Futhark.Optimise.MemoryBlockMerging.Types: type VarMemMappings t = Map VName t
- Futhark.Optimise.MemoryBlockMerging.VariableAliases: findVarAliases :: FunDef ExplicitMemory -> VarAliases
- Futhark.Optimise.MemoryBlockMerging.VariableAliases: instance Control.Monad.Writer.Class.MonadWriter [Futhark.Optimise.MemoryBlockMerging.Types.VarAliases] (Futhark.Optimise.MemoryBlockMerging.VariableAliases.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.VariableAliases: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.VariableAliases.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.VariableAliases: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.VariableAliases.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.VariableAliases: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.VariableAliases.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.VariableMemory: findVarMemMappings :: FunDef ExplicitMemory -> VarMemMappings MemorySrc
- Futhark.Optimise.MemoryBlockMerging.VariableMemory: instance Control.Monad.Writer.Class.MonadWriter (Futhark.Optimise.MemoryBlockMerging.Types.VarMemMappings Futhark.Optimise.MemoryBlockMerging.Types.MemorySrc) (Futhark.Optimise.MemoryBlockMerging.VariableMemory.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.VariableMemory: instance GHC.Base.Applicative (Futhark.Optimise.MemoryBlockMerging.VariableMemory.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.VariableMemory: instance GHC.Base.Functor (Futhark.Optimise.MemoryBlockMerging.VariableMemory.FindM lore)
- Futhark.Optimise.MemoryBlockMerging.VariableMemory: instance GHC.Base.Monad (Futhark.Optimise.MemoryBlockMerging.VariableMemory.FindM lore)
- Futhark.Pass.ExtractKernels.Split: splitMap :: MonadFreshNames m => Scope SOACS -> a -> m [a]
- Futhark.Representation.AST.Attributes.Types: shapeMapping' :: [TypeBase Shape u] -> [[a]] -> Map VName a
- Futhark.Representation.ExplicitMemory.IndexFunction: base :: IxFun num -> Shape num
- Futhark.Representation.ExplicitMemory.IndexFunction: getInfoMaxUnification :: IxFn -> Maybe (IxFn, Slice (PrimExp VName), VName)
- Futhark.Representation.ExplicitMemory.IndexFunction: ixFunHasIndex :: IxFun num -> Bool
- Futhark.Representation.ExplicitMemory.IndexFunction: ixFunsCompatibleRaw :: Eq num => IxFun num -> IxFun num -> Bool
- Futhark.Representation.ExplicitMemory.IndexFunction: offsetIndexDWIM :: Int -> IxFn -> PrimExp VName -> IxFn
- Futhark.Representation.ExplicitMemory.IndexFunction: subsInIndexIxFun :: IxFn -> VName -> VName -> IxFn
- Futhark.Representation.ExplicitMemory.Lmad: IxFun :: [Lmad num] -> Shape num -> Bool -> IxFun num
- Futhark.Representation.ExplicitMemory.Lmad: base :: IxFun num -> Shape num
- Futhark.Representation.ExplicitMemory.Lmad: data IxFun num
- Futhark.Representation.ExplicitMemory.Lmad: getMonotonicity :: (Eq num, IntegralExp num) => IxFun num -> DimInfo
- Futhark.Representation.ExplicitMemory.Lmad: index :: (IntegralExp num, Eq num) => IxFun num -> Indices num -> num -> num
- Futhark.Representation.ExplicitMemory.Lmad: instance Data.Foldable.Foldable Futhark.Representation.ExplicitMemory.Lmad.IxFun
- Futhark.Representation.ExplicitMemory.Lmad: instance Data.Foldable.Foldable Futhark.Representation.ExplicitMemory.Lmad.Lmad
- Futhark.Representation.ExplicitMemory.Lmad: instance Data.Traversable.Traversable Futhark.Representation.ExplicitMemory.Lmad.IxFun
- Futhark.Representation.ExplicitMemory.Lmad: instance Data.Traversable.Traversable Futhark.Representation.ExplicitMemory.Lmad.Lmad
- Futhark.Representation.ExplicitMemory.Lmad: instance Futhark.Representation.AST.Attributes.Names.FreeIn num => Futhark.Representation.AST.Attributes.Names.FreeIn (Futhark.Representation.ExplicitMemory.Lmad.IxFun num)
- Futhark.Representation.ExplicitMemory.Lmad: instance Futhark.Representation.AST.Attributes.Names.FreeIn num => Futhark.Representation.AST.Attributes.Names.FreeIn (Futhark.Representation.ExplicitMemory.Lmad.Lmad num)
- Futhark.Representation.ExplicitMemory.Lmad: instance Futhark.Transform.Substitute.Substitute num => Futhark.Transform.Rename.Rename (Futhark.Representation.ExplicitMemory.Lmad.IxFun num)
- Futhark.Representation.ExplicitMemory.Lmad: instance Futhark.Transform.Substitute.Substitute num => Futhark.Transform.Rename.Rename (Futhark.Representation.ExplicitMemory.Lmad.Lmad num)
- Futhark.Representation.ExplicitMemory.Lmad: instance Futhark.Transform.Substitute.Substitute num => Futhark.Transform.Substitute.Substitute (Futhark.Representation.ExplicitMemory.Lmad.IxFun num)
- Futhark.Representation.ExplicitMemory.Lmad: instance Futhark.Transform.Substitute.Substitute num => Futhark.Transform.Substitute.Substitute (Futhark.Representation.ExplicitMemory.Lmad.Lmad num)
- Futhark.Representation.ExplicitMemory.Lmad: instance GHC.Base.Functor Futhark.Representation.ExplicitMemory.Lmad.IxFun
- Futhark.Representation.ExplicitMemory.Lmad: instance GHC.Base.Functor Futhark.Representation.ExplicitMemory.Lmad.Lmad
- Futhark.Representation.ExplicitMemory.Lmad: instance GHC.Classes.Eq Futhark.Representation.ExplicitMemory.Lmad.DimInfo
- Futhark.Representation.ExplicitMemory.Lmad: instance GHC.Classes.Eq num => GHC.Classes.Eq (Futhark.Representation.ExplicitMemory.Lmad.IxFun num)
- Futhark.Representation.ExplicitMemory.Lmad: instance GHC.Classes.Eq num => GHC.Classes.Eq (Futhark.Representation.ExplicitMemory.Lmad.Lmad num)
- Futhark.Representation.ExplicitMemory.Lmad: instance GHC.Show.Show Futhark.Representation.ExplicitMemory.Lmad.DimInfo
- Futhark.Representation.ExplicitMemory.Lmad: instance GHC.Show.Show num => GHC.Show.Show (Futhark.Representation.ExplicitMemory.Lmad.IxFun num)
- Futhark.Representation.ExplicitMemory.Lmad: instance GHC.Show.Show num => GHC.Show.Show (Futhark.Representation.ExplicitMemory.Lmad.Lmad num)
- Futhark.Representation.ExplicitMemory.Lmad: instance Text.PrettyPrint.Mainland.Class.Pretty Futhark.Representation.ExplicitMemory.Lmad.DimInfo
- Futhark.Representation.ExplicitMemory.Lmad: instance Text.PrettyPrint.Mainland.Class.Pretty num => Text.PrettyPrint.Mainland.Class.Pretty (Futhark.Representation.ExplicitMemory.Lmad.IxFun num)
- Futhark.Representation.ExplicitMemory.Lmad: instance Text.PrettyPrint.Mainland.Class.Pretty num => Text.PrettyPrint.Mainland.Class.Pretty (Futhark.Representation.ExplicitMemory.Lmad.Lmad num)
- Futhark.Representation.ExplicitMemory.Lmad: iota :: IntegralExp num => Shape num -> IxFun num
- Futhark.Representation.ExplicitMemory.Lmad: isContiguous :: (Eq num, IntegralExp num) => IxFun num -> Bool
- Futhark.Representation.ExplicitMemory.Lmad: isDirect :: (Eq num, IntegralExp num) => IxFun num -> Bool
- Futhark.Representation.ExplicitMemory.Lmad: isLinear :: (Eq num, IntegralExp num) => IxFun num -> Bool
- Futhark.Representation.ExplicitMemory.Lmad: linearWithOffset :: (Eq num, IntegralExp num) => IxFun num -> num -> Maybe num
- Futhark.Representation.ExplicitMemory.Lmad: offsetIndex :: (Eq num, IntegralExp num) => IxFun num -> num -> IxFun num
- Futhark.Representation.ExplicitMemory.Lmad: permute :: IntegralExp num => IxFun num -> Permutation -> IxFun num
- Futhark.Representation.ExplicitMemory.Lmad: rank :: IntegralExp num => IxFun num -> Int
- Futhark.Representation.ExplicitMemory.Lmad: rearrangeWithOffset :: (Eq num, IntegralExp num) => IxFun num -> num -> Maybe (num, [(Int, num)])
- Futhark.Representation.ExplicitMemory.Lmad: rebase :: (Eq num, IntegralExp num) => IxFun num -> IxFun num -> IxFun num
- Futhark.Representation.ExplicitMemory.Lmad: repeat :: (Eq num, IntegralExp num) => IxFun num -> [Shape num] -> Shape num -> IxFun num
- Futhark.Representation.ExplicitMemory.Lmad: reshape :: (Eq num, IntegralExp num) => IxFun num -> ShapeChange num -> IxFun num
- Futhark.Representation.ExplicitMemory.Lmad: rotate :: (Eq num, IntegralExp num) => IxFun num -> Indices num -> IxFun num
- Futhark.Representation.ExplicitMemory.Lmad: shape :: (Eq num, IntegralExp num) => IxFun num -> Shape num
- Futhark.Representation.ExplicitMemory.Lmad: slice :: (Eq num, IntegralExp num) => IxFun num -> Slice num -> IxFun num
- Futhark.Representation.ExplicitMemory.Lmad: strideIndex :: (Eq num, IntegralExp num) => IxFun num -> num -> IxFun num
- Futhark.Representation.ExplicitMemory.Lmad: substituteInIxFun :: Map VName (PrimExp VName) -> IxFun (PrimExp VName) -> IxFun (PrimExp VName)
- Language.Futhark.Syntax: Unzip :: ExpBase f vn -> [f CompType] -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: Zip :: Int -> ExpBase f vn -> [ExpBase f vn] -> f CompType -> SrcLoc -> ExpBase f vn
+ Futhark.CLI.Misc: mainImports :: String -> [String] -> IO ()
+ Futhark.CodeGen.Backends.GenericPython.Definitions: pyTuning :: String
+ Futhark.CodeGen.ImpCode: ArrayValues :: [PrimValue] -> ArrayContents
+ Futhark.CodeGen.ImpCode: ArrayZeros :: Int -> ArrayContents
+ Futhark.CodeGen.ImpCode: data ArrayContents
+ Futhark.CodeGen.ImpCode: instance GHC.Show.Show Futhark.CodeGen.ImpCode.ArrayContents
+ Futhark.CodeGen.ImpCode: instance Text.PrettyPrint.Mainland.Class.Pretty Futhark.CodeGen.ImpCode.ArrayContents
+ Futhark.CodeGen.ImpCode.Kernels: ArrayValues :: [PrimValue] -> ArrayContents
+ Futhark.CodeGen.ImpCode.Kernels: ArrayZeros :: Int -> ArrayContents
+ Futhark.CodeGen.ImpCode.Kernels: data ArrayContents
+ Futhark.CodeGen.ImpCode.OpenCL: ArrayValues :: [PrimValue] -> ArrayContents
+ Futhark.CodeGen.ImpCode.OpenCL: ArrayZeros :: Int -> ArrayContents
+ Futhark.CodeGen.ImpCode.OpenCL: data ArrayContents
+ Futhark.CodeGen.ImpCode.Sequential: ArrayValues :: [PrimValue] -> ArrayContents
+ Futhark.CodeGen.ImpCode.Sequential: ArrayZeros :: Int -> ArrayContents
+ Futhark.CodeGen.ImpCode.Sequential: data ArrayContents
+ Futhark.CodeGen.ImpGen: sAlloc_ :: VName -> MemSize -> Space -> ImpM lore op ()
+ Futhark.CodeGen.ImpGen: sDeclareMem :: String -> Count Bytes -> Space -> ImpM lore op (VName, MemSize)
+ Futhark.CodeGen.ImpGen: sUpdate :: VName -> Slice Exp -> SubExp -> ImpM lore op ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: Locking :: VName -> Exp -> Exp -> Exp -> ([Exp] -> Exp) -> Locking
+ Futhark.CodeGen.ImpGen.Kernels.Base: [lockingArray] :: Locking -> VName
+ Futhark.CodeGen.ImpGen.Kernels.Base: [lockingIsUnlocked] :: Locking -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: [lockingMapping] :: Locking -> [Exp] -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: [lockingToLock] :: Locking -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: [lockingToUnlock] :: Locking -> Exp
+ Futhark.CodeGen.ImpGen.Kernels.Base: atomicUpdate :: ExplicitMemorish lore => [VName] -> [Exp] -> Lambda lore -> Locking -> ImpM lore KernelOp ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: atomicUpdateLocking :: ExplicitMemorish lore => Lambda lore -> Either (AtomicUpdate lore) (Locking -> AtomicUpdate lore)
+ Futhark.CodeGen.ImpGen.Kernels.Base: data Locking
+ Futhark.CodeGen.ImpGen.Kernels.Base: sCopy :: PrimType -> MemLocation -> MemLocation -> Count Elements -> CallKernelGen ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: sIota :: VName -> Exp -> Exp -> Exp -> IntType -> CallKernelGen ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: sReplicate :: VName -> Shape -> SubExp -> CallKernelGen ()
+ Futhark.CodeGen.ImpGen.Kernels.Base: type AtomicUpdate lore = [VName] -> [Exp] -> ImpM lore KernelOp ()
+ Futhark.CodeGen.ImpGen.Kernels.SegGenRed: compileSegGenRed :: Pattern ExplicitMemory -> KernelSpace -> [GenReduceOp InKernel] -> Body InKernel -> CallKernelGen ()
+ Futhark.CodeGen.ImpGen.Kernels.SegRed: compileSegRed' :: Pattern ExplicitMemory -> KernelSpace -> Commutativity -> Lambda InKernel -> [SubExp] -> ([(VName, [Exp])] -> [(VName, [Exp])] -> InKernelGen ()) -> CallKernelGen ()
+ Futhark.CodeGen.OpenCL.Kernels: Threshold :: WhichSize
+ Futhark.Construct: eOutOfBounds :: MonadBinder m => VName -> [m (Exp (Lore m))] -> m (Exp (Lore m))
+ Futhark.Pass.ExpandAllocations: instance Control.Monad.Error.Class.MonadError GHC.Base.String Futhark.Pass.ExpandAllocations.OffsetM
+ Futhark.Pass.ExpandAllocations: instance Futhark.Representation.AST.Attributes.Scope.HasScope Futhark.Representation.ExplicitMemory.InKernel Futhark.Pass.ExpandAllocations.OffsetM
+ Futhark.Pass.ExpandAllocations: instance Futhark.Representation.AST.Attributes.Scope.LocalScope Futhark.Representation.ExplicitMemory.InKernel Futhark.Pass.ExpandAllocations.OffsetM
+ Futhark.Pass.ExpandAllocations: instance GHC.Base.Applicative Futhark.Pass.ExpandAllocations.OffsetM
+ Futhark.Pass.ExpandAllocations: instance GHC.Base.Functor Futhark.Pass.ExpandAllocations.OffsetM
+ Futhark.Pass.ExpandAllocations: instance GHC.Base.Monad Futhark.Pass.ExpandAllocations.OffsetM
+ Futhark.Representation.ExplicitMemory.IndexFunction: IxFun :: NonEmpty (LMAD num) -> Shape num -> Bool -> IxFun num
+ Futhark.Representation.ExplicitMemory.IndexFunction: [base] :: IxFun num -> Shape num
+ Futhark.Representation.ExplicitMemory.IndexFunction: [ixfunContig] :: IxFun num -> Bool
+ Futhark.Representation.ExplicitMemory.IndexFunction: [ixfunLMADs] :: IxFun num -> NonEmpty (LMAD num)
+ Futhark.Representation.ExplicitMemory.IndexFunction: instance Data.Foldable.Foldable Futhark.Representation.ExplicitMemory.IndexFunction.LMAD
+ Futhark.Representation.ExplicitMemory.IndexFunction: instance Data.Traversable.Traversable Futhark.Representation.ExplicitMemory.IndexFunction.LMAD
+ Futhark.Representation.ExplicitMemory.IndexFunction: instance Futhark.Representation.AST.Attributes.Names.FreeIn num => Futhark.Representation.AST.Attributes.Names.FreeIn (Futhark.Representation.ExplicitMemory.IndexFunction.LMAD num)
+ Futhark.Representation.ExplicitMemory.IndexFunction: instance Futhark.Transform.Substitute.Substitute num => Futhark.Transform.Rename.Rename (Futhark.Representation.ExplicitMemory.IndexFunction.LMAD num)
+ Futhark.Representation.ExplicitMemory.IndexFunction: instance Futhark.Transform.Substitute.Substitute num => Futhark.Transform.Substitute.Substitute (Futhark.Representation.ExplicitMemory.IndexFunction.LMAD num)
+ Futhark.Representation.ExplicitMemory.IndexFunction: instance GHC.Base.Functor Futhark.Representation.ExplicitMemory.IndexFunction.LMAD
+ Futhark.Representation.ExplicitMemory.IndexFunction: instance GHC.Classes.Eq Futhark.Representation.ExplicitMemory.IndexFunction.Monotonicity
+ Futhark.Representation.ExplicitMemory.IndexFunction: instance GHC.Classes.Eq num => GHC.Classes.Eq (Futhark.Representation.ExplicitMemory.IndexFunction.LMAD num)
+ Futhark.Representation.ExplicitMemory.IndexFunction: instance GHC.Classes.Eq num => GHC.Classes.Eq (Futhark.Representation.ExplicitMemory.IndexFunction.LMADDim num)
+ Futhark.Representation.ExplicitMemory.IndexFunction: instance GHC.Show.Show Futhark.Representation.ExplicitMemory.IndexFunction.Monotonicity
+ Futhark.Representation.ExplicitMemory.IndexFunction: instance GHC.Show.Show num => GHC.Show.Show (Futhark.Representation.ExplicitMemory.IndexFunction.LMAD num)
+ Futhark.Representation.ExplicitMemory.IndexFunction: instance GHC.Show.Show num => GHC.Show.Show (Futhark.Representation.ExplicitMemory.IndexFunction.LMADDim num)
+ Futhark.Representation.ExplicitMemory.IndexFunction: instance Text.PrettyPrint.Mainland.Class.Pretty Futhark.Representation.ExplicitMemory.IndexFunction.Monotonicity
+ Futhark.Representation.ExplicitMemory.IndexFunction: instance Text.PrettyPrint.Mainland.Class.Pretty num => Text.PrettyPrint.Mainland.Class.Pretty (Futhark.Representation.ExplicitMemory.IndexFunction.LMAD num)
+ Futhark.Representation.ExplicitMemory.IndexFunction: isContiguous :: (Eq num, IntegralExp num) => IxFun num -> Bool
+ Futhark.Representation.Kernels.Kernel: GenReduceOp :: SubExp -> [VName] -> [SubExp] -> Shape -> LambdaT lore -> GenReduceOp lore
+ Futhark.Representation.Kernels.Kernel: SegGenRed :: KernelSpace -> [GenReduceOp lore] -> [Type] -> Body lore -> Kernel lore
+ Futhark.Representation.Kernels.Kernel: [genReduceDest] :: GenReduceOp lore -> [VName]
+ Futhark.Representation.Kernels.Kernel: [genReduceNeutral] :: GenReduceOp lore -> [SubExp]
+ Futhark.Representation.Kernels.Kernel: [genReduceOp] :: GenReduceOp lore -> LambdaT lore
+ Futhark.Representation.Kernels.Kernel: [genReduceShape] :: GenReduceOp lore -> Shape
+ Futhark.Representation.Kernels.Kernel: [genReduceWidth] :: GenReduceOp lore -> SubExp
+ Futhark.Representation.Kernels.Kernel: data GenReduceOp lore
+ Futhark.Representation.Kernels.Kernel: instance Futhark.Representation.AST.Annotations.Annotations lore => GHC.Classes.Eq (Futhark.Representation.Kernels.Kernel.GenReduceOp lore)
+ Futhark.Representation.Kernels.Kernel: instance Futhark.Representation.AST.Annotations.Annotations lore => GHC.Classes.Ord (Futhark.Representation.Kernels.Kernel.GenReduceOp lore)
+ Futhark.Representation.Kernels.Kernel: instance Futhark.Representation.AST.Annotations.Annotations lore => GHC.Show.Show (Futhark.Representation.Kernels.Kernel.GenReduceOp lore)
+ Futhark.Test: determineTuning :: MonadIO m => Maybe FilePath -> FilePath -> m ([String], String)
+ Language.Futhark.Interpreter: [envShapes] :: Env -> Map VName Shape
+ Language.Futhark.TypeChecker.Monad: askImportName :: TypeM ImportName
- Futhark.CodeGen.Backends.GenericC: type StaticArray op s = VName -> SpaceId -> PrimType -> [PrimValue] -> CompilerM op s ()
+ Futhark.CodeGen.Backends.GenericC: type StaticArray op s = VName -> SpaceId -> PrimType -> ArrayContents -> CompilerM op s ()
- Futhark.CodeGen.Backends.GenericC.Options: RequiredArgument :: OptionArgument
+ Futhark.CodeGen.Backends.GenericC.Options: RequiredArgument :: String -> OptionArgument
- Futhark.CodeGen.Backends.GenericCSharp: type StaticArray op s = VName -> SpaceId -> PrimType -> [PrimValue] -> CompilerM op s ()
+ Futhark.CodeGen.Backends.GenericCSharp: type StaticArray op s = VName -> SpaceId -> PrimType -> ArrayContents -> CompilerM op s ()
- Futhark.CodeGen.Backends.GenericCSharp.AST: CreateArray :: CSType -> [CSExp] -> CSExp
+ Futhark.CodeGen.Backends.GenericCSharp.AST: CreateArray :: CSType -> Either Int [CSExp] -> CSExp
- Futhark.CodeGen.Backends.GenericPython: type StaticArray op s = VName -> SpaceId -> PrimType -> [PrimValue] -> CompilerM op s ()
+ Futhark.CodeGen.Backends.GenericPython: type StaticArray op s = VName -> SpaceId -> PrimType -> ArrayContents -> CompilerM op s ()
- Futhark.CodeGen.Backends.GenericPython.Options: RequiredArgument :: OptionArgument
+ Futhark.CodeGen.Backends.GenericPython.Options: RequiredArgument :: String -> OptionArgument
- Futhark.CodeGen.ImpCode: DeclareArray :: VName -> Space -> PrimType -> [PrimValue] -> Code a
+ Futhark.CodeGen.ImpCode: DeclareArray :: VName -> Space -> PrimType -> ArrayContents -> Code a
- Futhark.CodeGen.ImpGen: sStaticArray :: String -> Space -> PrimType -> [PrimValue] -> ImpM lore op VName
+ Futhark.CodeGen.ImpGen: sStaticArray :: String -> Space -> PrimType -> ArrayContents -> ImpM lore op VName
- Futhark.Internalise.AccurateSizes: argShapes :: [VName] -> [TypeBase Shape u0] -> [TypeBase Shape u1] -> [SubExp]
+ Futhark.Internalise.AccurateSizes: argShapes :: MonadBinder m => [VName] -> [TypeBase Shape u0] -> [TypeBase Shape u1] -> m [SubExp]
- Futhark.Pass.ExtractKernels.BlockedKernel: blockedGenReduce :: (MonadFreshNames m, HasScope Kernels m) => SubExp -> [(VName, SubExp)] -> [KernelInput] -> [GenReduceOp InKernel] -> Lambda InKernel -> [VName] -> m ([VName], Stms Kernels)
+ Futhark.Pass.ExtractKernels.BlockedKernel: blockedGenReduce :: (MonadFreshNames m, HasScope Kernels m) => Pattern Kernels -> SubExp -> [(VName, SubExp)] -> [KernelInput] -> [GenReduceOp InKernel] -> Lambda InKernel -> [VName] -> m (Stms Kernels)
- Futhark.Representation.AST.Attributes.Types: shapeMapping :: [TypeBase Shape u0] -> [TypeBase Shape u1] -> Map VName SubExp
+ Futhark.Representation.AST.Attributes.Types: shapeMapping :: [TypeBase Shape u0] -> [TypeBase Shape u1] -> Map VName (Set SubExp)
- Futhark.Representation.ExplicitMemory.IndexFunction: index :: (Pretty num, IntegralExp num) => IxFun num -> Indices num -> num -> num
+ Futhark.Representation.ExplicitMemory.IndexFunction: index :: (IntegralExp num, Eq num) => IxFun num -> Indices num -> num -> num
- Futhark.Representation.ExplicitMemory.IndexFunction: iota :: Shape num -> IxFun num
+ Futhark.Representation.ExplicitMemory.IndexFunction: iota :: IntegralExp num => Shape num -> IxFun num
- Futhark.Representation.ExplicitMemory.IndexFunction: isDirect :: IxFun num -> Bool
+ Futhark.Representation.ExplicitMemory.IndexFunction: isDirect :: (Eq num, IntegralExp num) => IxFun num -> Bool
- Futhark.Representation.ExplicitMemory.IndexFunction: repeat :: IxFun num -> [Shape num] -> Shape num -> IxFun num
+ Futhark.Representation.ExplicitMemory.IndexFunction: repeat :: (Eq num, IntegralExp num) => IxFun num -> [Shape num] -> Shape num -> IxFun num
- Futhark.Representation.ExplicitMemory.IndexFunction: rotate :: IntegralExp num => IxFun num -> Indices num -> IxFun num
+ Futhark.Representation.ExplicitMemory.IndexFunction: rotate :: (Eq num, IntegralExp num) => IxFun num -> Indices num -> IxFun num
- Futhark.Representation.ExplicitMemory.IndexFunction: shape :: IntegralExp num => IxFun num -> Shape num
+ Futhark.Representation.ExplicitMemory.IndexFunction: shape :: (Eq num, IntegralExp num) => IxFun num -> Shape num
- Language.Futhark.Interpreter: Env :: Map VName TermBinding -> Map VName TypeBinding -> Env
+ Language.Futhark.Interpreter: Env :: Map VName TermBinding -> Map VName TypeBinding -> Map VName Shape -> Env
- Language.Futhark.TypeChecker.Types: substituteTypes :: TypeSubs -> StructType -> StructType
+ Language.Futhark.TypeChecker.Types: substituteTypes :: Monoid als => TypeSubs -> TypeBase (DimDecl VName) als -> TypeBase (DimDecl VName) als
Files
- futhark.cabal +26/−838
- futlib/prelude.fut +7/−0
- futlib/soacs.fut +4/−4
- rts/c/cuda.h +35/−11
- rts/c/opencl.h +38/−28
- rts/c/tuning.h +40/−0
- rts/csharp/opencl.cs +38/−12
- rts/python/opencl.py +21/−1
- rts/python/tuning.py +11/−0
- src/Futhark/CLI/Bench.hs +15/−3
- src/Futhark/CLI/Datacmp.hs +2/−4
- src/Futhark/CLI/Dev.hs +22/−10
- src/Futhark/CLI/Misc.hs +13/−0
- src/Futhark/CLI/Pkg.hs +4/−1
- src/Futhark/CLI/REPL.hs +1/−1
- src/Futhark/CLI/Test.hs +14/−7
- src/Futhark/CodeGen/Backends/CCUDA.hs +34/−11
- src/Futhark/CodeGen/Backends/CCUDA/Boilerplate.hs +16/−1
- src/Futhark/CodeGen/Backends/COpenCL.hs +40/−16
- src/Futhark/CodeGen/Backends/COpenCL/Boilerplate.hs +39/−2
- src/Futhark/CodeGen/Backends/CSOpenCL.hs +9/−3
- src/Futhark/CodeGen/Backends/CSOpenCL/Boilerplate.hs +1/−0
- src/Futhark/CodeGen/Backends/GenericC.hs +37/−27
- src/Futhark/CodeGen/Backends/GenericC/Options.hs +23/−8
- src/Futhark/CodeGen/Backends/GenericCSharp.hs +12/−5
- src/Futhark/CodeGen/Backends/GenericCSharp/AST.hs +3/−2
- src/Futhark/CodeGen/Backends/GenericPython.hs +21/−8
- src/Futhark/CodeGen/Backends/GenericPython/Definitions.hs +4/−0
- src/Futhark/CodeGen/Backends/GenericPython/Options.hs +6/−4
- src/Futhark/CodeGen/Backends/PyOpenCL.hs +62/−13
- src/Futhark/CodeGen/Backends/PyOpenCL/Boilerplate.hs +2/−0
- src/Futhark/CodeGen/Backends/SequentialPython.hs +1/−1
- src/Futhark/CodeGen/ImpCode.hs +16/−2
- src/Futhark/CodeGen/ImpCode/Kernels.hs +3/−3
- src/Futhark/CodeGen/ImpGen.hs +42/−24
- src/Futhark/CodeGen/ImpGen/Kernels.hs +9/−88
- src/Futhark/CodeGen/ImpGen/Kernels/Base.hs +188/−52
- src/Futhark/CodeGen/ImpGen/Kernels/SegGenRed.hs +275/−0
- src/Futhark/CodeGen/ImpGen/Kernels/SegRed.hs +70/−105
- src/Futhark/CodeGen/ImpGen/Kernels/ToOpenCL.hs +45/−35
- src/Futhark/CodeGen/OpenCL/Kernels.hs +3/−1
- src/Futhark/Construct.hs +17/−11
- src/Futhark/Doc/Generator.hs +3/−4
- src/Futhark/Internalise.hs +22/−91
- src/Futhark/Internalise/AccurateSizes.hs +19/−6
- src/Futhark/Internalise/Defunctionalise.hs +29/−65
- src/Futhark/Internalise/Monomorphise.hs +106/−63
- src/Futhark/Optimise/CSE.hs +32/−16
- src/Futhark/Optimise/InliningDeadFun.hs +4/−3
- src/Futhark/Optimise/MemoryBlockMerging.hs +0/−28
- src/Futhark/Optimise/MemoryBlockMerging/ActualVariables.hs +0/−358
- src/Futhark/Optimise/MemoryBlockMerging/AllExpVars.hs +0/−96
- src/Futhark/Optimise/MemoryBlockMerging/AuxiliaryInfo.hs +0/−63
- src/Futhark/Optimise/MemoryBlockMerging/Coalescing.hs +0/−31
- src/Futhark/Optimise/MemoryBlockMerging/Coalescing/AllocationMovingUp.hs +0/−94
- src/Futhark/Optimise/MemoryBlockMerging/Coalescing/Core.hs +0/−624
- src/Futhark/Optimise/MemoryBlockMerging/Coalescing/Exps.hs +0/−70
- src/Futhark/Optimise/MemoryBlockMerging/Coalescing/SafetyCondition2.hs +0/−110
- src/Futhark/Optimise/MemoryBlockMerging/Coalescing/SafetyCondition3.hs +0/−136
- src/Futhark/Optimise/MemoryBlockMerging/Coalescing/SafetyCondition5.hs +0/−120
- src/Futhark/Optimise/MemoryBlockMerging/CrudeMovingUp.hs +0/−263
- src/Futhark/Optimise/MemoryBlockMerging/Existentials.hs +0/−80
- src/Futhark/Optimise/MemoryBlockMerging/Liveness/FirstUse.hs +0/−198
- src/Futhark/Optimise/MemoryBlockMerging/Liveness/Interference.hs +0/−520
- src/Futhark/Optimise/MemoryBlockMerging/Liveness/LastUse.hs +0/−281
- src/Futhark/Optimise/MemoryBlockMerging/MemoryAliases.hs +0/−162
- src/Futhark/Optimise/MemoryBlockMerging/MemoryUpdater.hs +0/−418
- src/Futhark/Optimise/MemoryBlockMerging/Miscellaneous.hs +0/−263
- src/Futhark/Optimise/MemoryBlockMerging/PrimExps.hs +0/−105
- src/Futhark/Optimise/MemoryBlockMerging/Reuse.hs +0/−30
- src/Futhark/Optimise/MemoryBlockMerging/Reuse/AllocationSizeMovingUp.hs +0/−32
- src/Futhark/Optimise/MemoryBlockMerging/Reuse/AllocationSizeUses.hs +0/−127
- src/Futhark/Optimise/MemoryBlockMerging/Reuse/AllocationSizes.hs +0/−132
- src/Futhark/Optimise/MemoryBlockMerging/Reuse/Core.hs +0/−747
- src/Futhark/Optimise/MemoryBlockMerging/Types.hs +0/−89
- src/Futhark/Optimise/MemoryBlockMerging/VariableAliases.hs +0/−82
- src/Futhark/Optimise/MemoryBlockMerging/VariableMemory.hs +0/−99
- src/Futhark/Pass/ExpandAllocations.hs +182/−81
- src/Futhark/Pass/ExplicitAllocations.hs +7/−0
- src/Futhark/Pass/ExtractKernels.hs +27/−107
- src/Futhark/Pass/ExtractKernels/BlockedKernel.hs +24/−142
- src/Futhark/Pass/ExtractKernels/Segmented.hs +1/−1
- src/Futhark/Pass/ExtractKernels/Split.hs +0/−41
- src/Futhark/Passes.hs +2/−50
- src/Futhark/Representation/AST/Attributes/Types.hs +11/−10
- src/Futhark/Representation/Aliases.hs +0/−1
- src/Futhark/Representation/ExplicitMemory.hs +5/−3
- src/Futhark/Representation/ExplicitMemory/IndexFunction.hs +789/−447
- src/Futhark/Representation/ExplicitMemory/Lmad.hs +0/−761
- src/Futhark/Representation/Kernels/Kernel.hs +95/−0
- src/Futhark/Representation/Kernels/Simplify.hs +61/−11
- src/Futhark/Test.hs +25/−6
- src/Futhark/Transform/FirstOrderTransform.hs +26/−13
- src/Futhark/Version.hs +7/−2
- src/Language/Futhark/Attributes.hs +0/−3
- src/Language/Futhark/Interpreter.hs +120/−107
- src/Language/Futhark/Parser/Parser.y +11/−12
- src/Language/Futhark/Pretty.hs +1/−4
- src/Language/Futhark/Syntax.hs +0/−10
- src/Language/Futhark/Traversals.hs +0/−5
- src/Language/Futhark/TypeChecker.hs +11/−5
- src/Language/Futhark/TypeChecker/Monad.hs +16/−4
- src/Language/Futhark/TypeChecker/Terms.hs +58/−71
- src/Language/Futhark/TypeChecker/Types.hs +9/−8
- src/Language/Futhark/TypeChecker/Unify.hs +2/−4
- src/futhark.hs +1/−0
- src/futharki.hs +0/−19
- src/wrapper.hs +0/−29
- unittests/Futhark/Analysis/ScalExpTests.hs +2/−6
- unittests/Futhark/Optimise/AlgSimplifyTests.hs +5/−5
- unittests/Futhark/Representation/AST/AttributesTests.hs +0/−1
- unittests/Futhark/Representation/ExplicitMemory/IndexFunction/Alg.hs +178/−0
- unittests/Futhark/Representation/ExplicitMemory/IndexFunctionTests.hs +371/−0
- unittests/Futhark/Representation/ExplicitMemory/IndexFunctionWrapper.hs +65/−0
- unittests/futhark_tests.hs +6/−0
futhark.cabal view
@@ -2,12 +2,19 @@ -- -- see: https://github.com/sol/hpack ----- hash: d926568e0952a1cecfa1efa7cf687ddc7cd27d1c642a929d85ca5b0e8465b408+-- hash: ca599dc882c25837255ce48c8e6ab8c5a9f1ff05234e05f6d73705f5cf9fe1c6 name: futhark-version: 0.9.1+version: 0.10.1 synopsis: An optimising compiler for a functional, array-oriented language.-description: See the website at https://futhark-lang.org+description: Futhark is a small programming language designed to be compiled to+ efficient parallel code. It is a statically typed, data-parallel,+ and purely functional array language in the ML family, and comes+ with a heavily optimising ahead-of-time compiler that presently+ generates GPU code via CUDA and OpenCL, although the language itself+ is hardware-agnostic.+ .+ For more information, see the website at https://futhark-lang.org category: Language homepage: https://futhark-lang.org bug-reports: https://github.com/diku-dk/futhark/issues@@ -29,6 +36,7 @@ rts/c/opencl.h rts/c/panic.h rts/c/timing.h+ rts/c/tuning.h rts/c/values.h rts/csharp/exceptions.cs rts/csharp/functions.cs@@ -43,6 +51,7 @@ rts/python/opencl.py rts/python/panic.py rts/python/scalar.py+ rts/python/tuning.py rts/python/values.py source-repository head@@ -58,7 +67,6 @@ , ansi-terminal >=0.6.3.1 , array >=0.4 , base >=4 && <5- , bifunctors >=5.4.2 , binary >=0.8.3 , blaze-html >=0.9.0.1 , bytestring >=0.10.8@@ -67,7 +75,6 @@ , directory >=1.3.0.0 , directory-tree >=0.12.1 , dlist >=0.6.0.1- , extra >=1.5.3 , file-embed >=0.0.9 , filepath >=1.4.1.1 , free >=4.12.4@@ -87,15 +94,14 @@ , process >=1.4.3.0 , process-extras >=0.7.2 , random- , raw-strings-qq >=1.1 , regex-tdfa >=1.2 , srcloc >=0.4 , template-haskell >=2.11.1 , temporary , text >=1.2.2.2- , th-lift-instances >=0.1.11 , time >=1.6.0.1 , transformers >=0.3+ , utf8-string >=1 , vector >=0.12 , vector-binary-instances >=0.2.2.0 , versions >=3.3.1@@ -104,9 +110,6 @@ build-tools: alex , happy- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.* exposed-modules: Futhark.Actions Futhark.Analysis.AlgSimplify@@ -174,6 +177,7 @@ Futhark.CodeGen.ImpGen.CUDA Futhark.CodeGen.ImpGen.Kernels Futhark.CodeGen.ImpGen.Kernels.Base+ Futhark.CodeGen.ImpGen.Kernels.SegGenRed Futhark.CodeGen.ImpGen.Kernels.SegRed Futhark.CodeGen.ImpGen.Kernels.ToOpenCL Futhark.CodeGen.ImpGen.Kernels.Transpose@@ -209,34 +213,6 @@ Futhark.Optimise.InPlaceLowering Futhark.Optimise.InPlaceLowering.LowerIntoStm Futhark.Optimise.InPlaceLowering.SubstituteIndices- Futhark.Optimise.MemoryBlockMerging- Futhark.Optimise.MemoryBlockMerging.ActualVariables- Futhark.Optimise.MemoryBlockMerging.AllExpVars- Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo- Futhark.Optimise.MemoryBlockMerging.Coalescing- Futhark.Optimise.MemoryBlockMerging.Coalescing.AllocationMovingUp- Futhark.Optimise.MemoryBlockMerging.Coalescing.Core- Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3- Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5- Futhark.Optimise.MemoryBlockMerging.CrudeMovingUp- Futhark.Optimise.MemoryBlockMerging.Existentials- Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse- Futhark.Optimise.MemoryBlockMerging.Liveness.Interference- Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse- Futhark.Optimise.MemoryBlockMerging.MemoryAliases- Futhark.Optimise.MemoryBlockMerging.MemoryUpdater- Futhark.Optimise.MemoryBlockMerging.Miscellaneous- Futhark.Optimise.MemoryBlockMerging.PrimExps- Futhark.Optimise.MemoryBlockMerging.Reuse- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeMovingUp- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes- Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses- Futhark.Optimise.MemoryBlockMerging.Reuse.Core- Futhark.Optimise.MemoryBlockMerging.Types- Futhark.Optimise.MemoryBlockMerging.VariableAliases- Futhark.Optimise.MemoryBlockMerging.VariableMemory Futhark.Optimise.Simplify Futhark.Optimise.Simplify.ClosedForm Futhark.Optimise.Simplify.Engine@@ -257,7 +233,6 @@ Futhark.Pass.ExtractKernels.ISRWIM Futhark.Pass.ExtractKernels.Kernelise Futhark.Pass.ExtractKernels.Segmented- Futhark.Pass.ExtractKernels.Split Futhark.Pass.FirstOrderTransform Futhark.Pass.KernelBabysitting Futhark.Pass.ResolveAssertions@@ -288,7 +263,6 @@ Futhark.Representation.AST.Traversals Futhark.Representation.ExplicitMemory Futhark.Representation.ExplicitMemory.IndexFunction- Futhark.Representation.ExplicitMemory.Lmad Futhark.Representation.ExplicitMemory.Simplify Futhark.Representation.Kernels Futhark.Representation.Kernels.Kernel@@ -341,757 +315,13 @@ main-is: src/futhark.hs ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists -threaded -rtsopts "-with-rtsopts=-N -qg" build-depends:- aeson- , ansi-terminal >=0.6.3.1- , array >=0.4- , base >=4 && <5- , bifunctors >=5.4.2- , binary >=0.8.3- , blaze-html >=0.9.0.1- , bytestring >=0.10.8- , containers >=0.5- , data-binary-ieee754 >=0.1- , directory >=1.3.0.0- , directory-tree >=0.12.1- , dlist >=0.6.0.1- , extra >=1.5.3- , file-embed >=0.0.9- , filepath >=1.4.1.1- , free >=4.12.4+ base , futhark- , gitrev >=1.2.0- , haskeline- , http-client >=0.5.7.0- , http-client-tls >=0.3.5.1- , http-conduit >=2.2.4- , language-c-quote >=0.12- , mainland-pretty >=0.6.1- , markdown >=0.1.16- , megaparsec >=7.0.1- , mtl >=2.2.1- , neat-interpolation >=0.3- , parallel >=3.2.1.0- , parser-combinators >=1.0.0- , process >=1.4.3.0- , process-extras >=0.7.2- , random- , raw-strings-qq >=1.1- , regex-tdfa >=1.2- , srcloc >=0.4- , template-haskell >=2.11.1- , temporary- , text >=1.2.2.2- , th-lift-instances >=0.1.11- , time >=1.6.0.1- , transformers >=0.3- , vector >=0.12- , vector-binary-instances >=0.2.2.0- , versions >=3.3.1- , zip-archive >=0.3.1.1- , zlib >=0.6.1.2- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.*+ , text other-modules: Paths_futhark default-language: Haskell2010 -executable futhark-bench- main-is: src/wrapper.hs- other-modules:- Paths_futhark- ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists- build-depends:- aeson- , ansi-terminal >=0.6.3.1- , array >=0.4- , base >=4 && <5- , bifunctors >=5.4.2- , binary >=0.8.3- , blaze-html >=0.9.0.1- , bytestring >=0.10.8- , containers >=0.5- , data-binary-ieee754 >=0.1- , directory >=1.3.0.0- , directory-tree >=0.12.1- , dlist >=0.6.0.1- , extra >=1.5.3- , file-embed >=0.0.9- , filepath >=1.4.1.1- , free >=4.12.4- , gitrev >=1.2.0- , haskeline- , http-client >=0.5.7.0- , http-client-tls >=0.3.5.1- , http-conduit >=2.2.4- , language-c-quote >=0.12- , mainland-pretty >=0.6.1- , markdown >=0.1.16- , megaparsec >=7.0.1- , mtl >=2.2.1- , neat-interpolation >=0.3- , parallel >=3.2.1.0- , parser-combinators >=1.0.0- , process >=1.4.3.0- , process-extras >=0.7.2- , random- , raw-strings-qq >=1.1- , regex-tdfa >=1.2- , srcloc >=0.4- , template-haskell >=2.11.1- , temporary- , text >=1.2.2.2- , th-lift-instances >=0.1.11- , time >=1.6.0.1- , transformers >=0.3- , vector >=0.12- , vector-binary-instances >=0.2.2.0- , versions >=3.3.1- , zip-archive >=0.3.1.1- , zlib >=0.6.1.2- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.*- default-language: Haskell2010--executable futhark-c- main-is: src/wrapper.hs- other-modules:- Paths_futhark- ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists- build-depends:- aeson- , ansi-terminal >=0.6.3.1- , array >=0.4- , base >=4 && <5- , bifunctors >=5.4.2- , binary >=0.8.3- , blaze-html >=0.9.0.1- , bytestring >=0.10.8- , containers >=0.5- , data-binary-ieee754 >=0.1- , directory >=1.3.0.0- , directory-tree >=0.12.1- , dlist >=0.6.0.1- , extra >=1.5.3- , file-embed >=0.0.9- , filepath >=1.4.1.1- , free >=4.12.4- , gitrev >=1.2.0- , haskeline- , http-client >=0.5.7.0- , http-client-tls >=0.3.5.1- , http-conduit >=2.2.4- , language-c-quote >=0.12- , mainland-pretty >=0.6.1- , markdown >=0.1.16- , megaparsec >=7.0.1- , mtl >=2.2.1- , neat-interpolation >=0.3- , parallel >=3.2.1.0- , parser-combinators >=1.0.0- , process >=1.4.3.0- , process-extras >=0.7.2- , random- , raw-strings-qq >=1.1- , regex-tdfa >=1.2- , srcloc >=0.4- , template-haskell >=2.11.1- , temporary- , text >=1.2.2.2- , th-lift-instances >=0.1.11- , time >=1.6.0.1- , transformers >=0.3- , vector >=0.12- , vector-binary-instances >=0.2.2.0- , versions >=3.3.1- , zip-archive >=0.3.1.1- , zlib >=0.6.1.2- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.*- default-language: Haskell2010--executable futhark-cs- main-is: src/wrapper.hs- other-modules:- Paths_futhark- ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists- build-depends:- aeson- , ansi-terminal >=0.6.3.1- , array >=0.4- , base >=4 && <5- , bifunctors >=5.4.2- , binary >=0.8.3- , blaze-html >=0.9.0.1- , bytestring >=0.10.8- , containers >=0.5- , data-binary-ieee754 >=0.1- , directory >=1.3.0.0- , directory-tree >=0.12.1- , dlist >=0.6.0.1- , extra >=1.5.3- , file-embed >=0.0.9- , filepath >=1.4.1.1- , free >=4.12.4- , gitrev >=1.2.0- , haskeline- , http-client >=0.5.7.0- , http-client-tls >=0.3.5.1- , http-conduit >=2.2.4- , language-c-quote >=0.12- , mainland-pretty >=0.6.1- , markdown >=0.1.16- , megaparsec >=7.0.1- , mtl >=2.2.1- , neat-interpolation >=0.3- , parallel >=3.2.1.0- , parser-combinators >=1.0.0- , process >=1.4.3.0- , process-extras >=0.7.2- , random- , raw-strings-qq >=1.1- , regex-tdfa >=1.2- , srcloc >=0.4- , template-haskell >=2.11.1- , temporary- , text >=1.2.2.2- , th-lift-instances >=0.1.11- , time >=1.6.0.1- , transformers >=0.3- , vector >=0.12- , vector-binary-instances >=0.2.2.0- , versions >=3.3.1- , zip-archive >=0.3.1.1- , zlib >=0.6.1.2- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.*- default-language: Haskell2010--executable futhark-csopencl- main-is: src/wrapper.hs- other-modules:- Paths_futhark- ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists- build-depends:- aeson- , ansi-terminal >=0.6.3.1- , array >=0.4- , base >=4 && <5- , bifunctors >=5.4.2- , binary >=0.8.3- , blaze-html >=0.9.0.1- , bytestring >=0.10.8- , containers >=0.5- , data-binary-ieee754 >=0.1- , directory >=1.3.0.0- , directory-tree >=0.12.1- , dlist >=0.6.0.1- , extra >=1.5.3- , file-embed >=0.0.9- , filepath >=1.4.1.1- , free >=4.12.4- , gitrev >=1.2.0- , haskeline- , http-client >=0.5.7.0- , http-client-tls >=0.3.5.1- , http-conduit >=2.2.4- , language-c-quote >=0.12- , mainland-pretty >=0.6.1- , markdown >=0.1.16- , megaparsec >=7.0.1- , mtl >=2.2.1- , neat-interpolation >=0.3- , parallel >=3.2.1.0- , parser-combinators >=1.0.0- , process >=1.4.3.0- , process-extras >=0.7.2- , random- , raw-strings-qq >=1.1- , regex-tdfa >=1.2- , srcloc >=0.4- , template-haskell >=2.11.1- , temporary- , text >=1.2.2.2- , th-lift-instances >=0.1.11- , time >=1.6.0.1- , transformers >=0.3- , vector >=0.12- , vector-binary-instances >=0.2.2.0- , versions >=3.3.1- , zip-archive >=0.3.1.1- , zlib >=0.6.1.2- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.*- default-language: Haskell2010--executable futhark-dataset- main-is: src/wrapper.hs- other-modules:- Paths_futhark- ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists- build-depends:- aeson- , ansi-terminal >=0.6.3.1- , array >=0.4- , base >=4 && <5- , bifunctors >=5.4.2- , binary >=0.8.3- , blaze-html >=0.9.0.1- , bytestring >=0.10.8- , containers >=0.5- , data-binary-ieee754 >=0.1- , directory >=1.3.0.0- , directory-tree >=0.12.1- , dlist >=0.6.0.1- , extra >=1.5.3- , file-embed >=0.0.9- , filepath >=1.4.1.1- , free >=4.12.4- , gitrev >=1.2.0- , haskeline- , http-client >=0.5.7.0- , http-client-tls >=0.3.5.1- , http-conduit >=2.2.4- , language-c-quote >=0.12- , mainland-pretty >=0.6.1- , markdown >=0.1.16- , megaparsec >=7.0.1- , mtl >=2.2.1- , neat-interpolation >=0.3- , parallel >=3.2.1.0- , parser-combinators >=1.0.0- , process >=1.4.3.0- , process-extras >=0.7.2- , random- , raw-strings-qq >=1.1- , regex-tdfa >=1.2- , srcloc >=0.4- , template-haskell >=2.11.1- , temporary- , text >=1.2.2.2- , th-lift-instances >=0.1.11- , time >=1.6.0.1- , transformers >=0.3- , vector >=0.12- , vector-binary-instances >=0.2.2.0- , versions >=3.3.1- , zip-archive >=0.3.1.1- , zlib >=0.6.1.2- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.*- default-language: Haskell2010--executable futhark-doc- main-is: src/wrapper.hs- other-modules:- Paths_futhark- ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists- build-depends:- aeson- , ansi-terminal >=0.6.3.1- , array >=0.4- , base >=4 && <5- , bifunctors >=5.4.2- , binary >=0.8.3- , blaze-html >=0.9.0.1- , bytestring >=0.10.8- , containers >=0.5- , data-binary-ieee754 >=0.1- , directory >=1.3.0.0- , directory-tree >=0.12.1- , dlist >=0.6.0.1- , extra >=1.5.3- , file-embed >=0.0.9- , filepath >=1.4.1.1- , free >=4.12.4- , gitrev >=1.2.0- , haskeline- , http-client >=0.5.7.0- , http-client-tls >=0.3.5.1- , http-conduit >=2.2.4- , language-c-quote >=0.12- , mainland-pretty >=0.6.1- , markdown >=0.1.16- , megaparsec >=7.0.1- , mtl >=2.2.1- , neat-interpolation >=0.3- , parallel >=3.2.1.0- , parser-combinators >=1.0.0- , process >=1.4.3.0- , process-extras >=0.7.2- , random- , raw-strings-qq >=1.1- , regex-tdfa >=1.2- , srcloc >=0.4- , template-haskell >=2.11.1- , temporary- , text >=1.2.2.2- , th-lift-instances >=0.1.11- , time >=1.6.0.1- , transformers >=0.3- , vector >=0.12- , vector-binary-instances >=0.2.2.0- , versions >=3.3.1- , zip-archive >=0.3.1.1- , zlib >=0.6.1.2- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.*- default-language: Haskell2010--executable futhark-opencl- main-is: src/wrapper.hs- other-modules:- Paths_futhark- ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists- build-depends:- aeson- , ansi-terminal >=0.6.3.1- , array >=0.4- , base >=4 && <5- , bifunctors >=5.4.2- , binary >=0.8.3- , blaze-html >=0.9.0.1- , bytestring >=0.10.8- , containers >=0.5- , data-binary-ieee754 >=0.1- , directory >=1.3.0.0- , directory-tree >=0.12.1- , dlist >=0.6.0.1- , extra >=1.5.3- , file-embed >=0.0.9- , filepath >=1.4.1.1- , free >=4.12.4- , gitrev >=1.2.0- , haskeline- , http-client >=0.5.7.0- , http-client-tls >=0.3.5.1- , http-conduit >=2.2.4- , language-c-quote >=0.12- , mainland-pretty >=0.6.1- , markdown >=0.1.16- , megaparsec >=7.0.1- , mtl >=2.2.1- , neat-interpolation >=0.3- , parallel >=3.2.1.0- , parser-combinators >=1.0.0- , process >=1.4.3.0- , process-extras >=0.7.2- , random- , raw-strings-qq >=1.1- , regex-tdfa >=1.2- , srcloc >=0.4- , template-haskell >=2.11.1- , temporary- , text >=1.2.2.2- , th-lift-instances >=0.1.11- , time >=1.6.0.1- , transformers >=0.3- , vector >=0.12- , vector-binary-instances >=0.2.2.0- , versions >=3.3.1- , zip-archive >=0.3.1.1- , zlib >=0.6.1.2- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.*- default-language: Haskell2010--executable futhark-pkg- main-is: src/wrapper.hs- other-modules:- Paths_futhark- ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists- build-depends:- aeson- , ansi-terminal >=0.6.3.1- , array >=0.4- , base >=4 && <5- , bifunctors >=5.4.2- , binary >=0.8.3- , blaze-html >=0.9.0.1- , bytestring >=0.10.8- , containers >=0.5- , data-binary-ieee754 >=0.1- , directory >=1.3.0.0- , directory-tree >=0.12.1- , dlist >=0.6.0.1- , extra >=1.5.3- , file-embed >=0.0.9- , filepath >=1.4.1.1- , free >=4.12.4- , gitrev >=1.2.0- , haskeline- , http-client >=0.5.7.0- , http-client-tls >=0.3.5.1- , http-conduit >=2.2.4- , language-c-quote >=0.12- , mainland-pretty >=0.6.1- , markdown >=0.1.16- , megaparsec >=7.0.1- , mtl >=2.2.1- , neat-interpolation >=0.3- , parallel >=3.2.1.0- , parser-combinators >=1.0.0- , process >=1.4.3.0- , process-extras >=0.7.2- , random- , raw-strings-qq >=1.1- , regex-tdfa >=1.2- , srcloc >=0.4- , template-haskell >=2.11.1- , temporary- , text >=1.2.2.2- , th-lift-instances >=0.1.11- , time >=1.6.0.1- , transformers >=0.3- , vector >=0.12- , vector-binary-instances >=0.2.2.0- , versions >=3.3.1- , zip-archive >=0.3.1.1- , zlib >=0.6.1.2- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.*- default-language: Haskell2010--executable futhark-py- main-is: src/wrapper.hs- other-modules:- Paths_futhark- ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists- build-depends:- aeson- , ansi-terminal >=0.6.3.1- , array >=0.4- , base >=4 && <5- , bifunctors >=5.4.2- , binary >=0.8.3- , blaze-html >=0.9.0.1- , bytestring >=0.10.8- , containers >=0.5- , data-binary-ieee754 >=0.1- , directory >=1.3.0.0- , directory-tree >=0.12.1- , dlist >=0.6.0.1- , extra >=1.5.3- , file-embed >=0.0.9- , filepath >=1.4.1.1- , free >=4.12.4- , gitrev >=1.2.0- , haskeline- , http-client >=0.5.7.0- , http-client-tls >=0.3.5.1- , http-conduit >=2.2.4- , language-c-quote >=0.12- , mainland-pretty >=0.6.1- , markdown >=0.1.16- , megaparsec >=7.0.1- , mtl >=2.2.1- , neat-interpolation >=0.3- , parallel >=3.2.1.0- , parser-combinators >=1.0.0- , process >=1.4.3.0- , process-extras >=0.7.2- , random- , raw-strings-qq >=1.1- , regex-tdfa >=1.2- , srcloc >=0.4- , template-haskell >=2.11.1- , temporary- , text >=1.2.2.2- , th-lift-instances >=0.1.11- , time >=1.6.0.1- , transformers >=0.3- , vector >=0.12- , vector-binary-instances >=0.2.2.0- , versions >=3.3.1- , zip-archive >=0.3.1.1- , zlib >=0.6.1.2- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.*- default-language: Haskell2010--executable futhark-pyopencl- main-is: src/wrapper.hs- other-modules:- Paths_futhark- ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists- build-depends:- aeson- , ansi-terminal >=0.6.3.1- , array >=0.4- , base >=4 && <5- , bifunctors >=5.4.2- , binary >=0.8.3- , blaze-html >=0.9.0.1- , bytestring >=0.10.8- , containers >=0.5- , data-binary-ieee754 >=0.1- , directory >=1.3.0.0- , directory-tree >=0.12.1- , dlist >=0.6.0.1- , extra >=1.5.3- , file-embed >=0.0.9- , filepath >=1.4.1.1- , free >=4.12.4- , gitrev >=1.2.0- , haskeline- , http-client >=0.5.7.0- , http-client-tls >=0.3.5.1- , http-conduit >=2.2.4- , language-c-quote >=0.12- , mainland-pretty >=0.6.1- , markdown >=0.1.16- , megaparsec >=7.0.1- , mtl >=2.2.1- , neat-interpolation >=0.3- , parallel >=3.2.1.0- , parser-combinators >=1.0.0- , process >=1.4.3.0- , process-extras >=0.7.2- , random- , raw-strings-qq >=1.1- , regex-tdfa >=1.2- , srcloc >=0.4- , template-haskell >=2.11.1- , temporary- , text >=1.2.2.2- , th-lift-instances >=0.1.11- , time >=1.6.0.1- , transformers >=0.3- , vector >=0.12- , vector-binary-instances >=0.2.2.0- , versions >=3.3.1- , zip-archive >=0.3.1.1- , zlib >=0.6.1.2- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.*- default-language: Haskell2010--executable futhark-test- main-is: src/wrapper.hs- other-modules:- Paths_futhark- ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists- build-depends:- aeson- , ansi-terminal >=0.6.3.1- , array >=0.4- , base >=4 && <5- , bifunctors >=5.4.2- , binary >=0.8.3- , blaze-html >=0.9.0.1- , bytestring >=0.10.8- , containers >=0.5- , data-binary-ieee754 >=0.1- , directory >=1.3.0.0- , directory-tree >=0.12.1- , dlist >=0.6.0.1- , extra >=1.5.3- , file-embed >=0.0.9- , filepath >=1.4.1.1- , free >=4.12.4- , gitrev >=1.2.0- , haskeline- , http-client >=0.5.7.0- , http-client-tls >=0.3.5.1- , http-conduit >=2.2.4- , language-c-quote >=0.12- , mainland-pretty >=0.6.1- , markdown >=0.1.16- , megaparsec >=7.0.1- , mtl >=2.2.1- , neat-interpolation >=0.3- , parallel >=3.2.1.0- , parser-combinators >=1.0.0- , process >=1.4.3.0- , process-extras >=0.7.2- , random- , raw-strings-qq >=1.1- , regex-tdfa >=1.2- , srcloc >=0.4- , template-haskell >=2.11.1- , temporary- , text >=1.2.2.2- , th-lift-instances >=0.1.11- , time >=1.6.0.1- , transformers >=0.3- , vector >=0.12- , vector-binary-instances >=0.2.2.0- , versions >=3.3.1- , zip-archive >=0.3.1.1- , zlib >=0.6.1.2- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.*- default-language: Haskell2010--executable futharki- main-is: src/futharki.hs- other-modules:- Paths_futhark- ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists- build-depends:- aeson- , ansi-terminal >=0.6.3.1- , array >=0.4- , base >=4 && <5- , bifunctors >=5.4.2- , binary >=0.8.3- , blaze-html >=0.9.0.1- , bytestring >=0.10.8- , containers >=0.5- , data-binary-ieee754 >=0.1- , directory >=1.3.0.0- , directory-tree >=0.12.1- , dlist >=0.6.0.1- , extra >=1.5.3- , file-embed >=0.0.9- , filepath >=1.4.1.1- , free >=4.12.4- , gitrev >=1.2.0- , haskeline- , http-client >=0.5.7.0- , http-client-tls >=0.3.5.1- , http-conduit >=2.2.4- , language-c-quote >=0.12- , mainland-pretty >=0.6.1- , markdown >=0.1.16- , megaparsec >=7.0.1- , mtl >=2.2.1- , neat-interpolation >=0.3- , parallel >=3.2.1.0- , parser-combinators >=1.0.0- , process >=1.4.3.0- , process-extras >=0.7.2- , random- , raw-strings-qq >=1.1- , regex-tdfa >=1.2- , srcloc >=0.4- , template-haskell >=2.11.1- , temporary- , text >=1.2.2.2- , th-lift-instances >=0.1.11- , time >=1.6.0.1- , transformers >=0.3- , vector >=0.12- , vector-binary-instances >=0.2.2.0- , versions >=3.3.1- , zip-archive >=0.3.1.1- , zlib >=0.6.1.2- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.*- default-language: Haskell2010- test-suite unit type: exitcode-stdio-1.0 main-is: futhark_tests.hs@@ -1099,62 +329,17 @@ unittests ghc-options: -Wall -Wcompat -Wredundant-constraints -Wincomplete-record-updates -Wmissing-export-lists build-depends:- HUnit- , QuickCheck >=2.8- , aeson- , ansi-terminal >=0.6.3.1- , array >=0.4- , base >=4 && <5- , bifunctors >=5.4.2- , binary >=0.8.3- , blaze-html >=0.9.0.1- , bytestring >=0.10.8- , containers >=0.5- , data-binary-ieee754 >=0.1- , directory >=1.3.0.0- , directory-tree >=0.12.1- , dlist >=0.6.0.1- , extra >=1.5.3- , file-embed >=0.0.9- , filepath >=1.4.1.1- , free >=4.12.4+ QuickCheck >=2.8+ , base+ , containers , futhark- , gitrev >=1.2.0- , haskeline- , http-client >=0.5.7.0- , http-client-tls >=0.3.5.1- , http-conduit >=2.2.4- , language-c-quote >=0.12- , mainland-pretty >=0.6.1- , markdown >=0.1.16- , megaparsec >=7.0.1- , mtl >=2.2.1- , neat-interpolation >=0.3- , parallel >=3.2.1.0- , parser-combinators >=1.0.0- , process >=1.4.3.0- , process-extras >=0.7.2- , random- , raw-strings-qq >=1.1- , regex-tdfa >=1.2- , srcloc >=0.4+ , megaparsec+ , mtl+ , parser-combinators , tasty , tasty-hunit , tasty-quickcheck- , template-haskell >=2.11.1- , temporary- , text >=1.2.2.2- , th-lift-instances >=0.1.11- , time >=1.6.0.1- , transformers >=0.3- , vector >=0.12- , vector-binary-instances >=0.2.2.0- , versions >=3.3.1- , zip-archive >=0.3.1.1- , zlib >=0.6.1.2- if !impl(ghc >= 8.0)- build-depends:- semigroups ==0.18.*+ , text other-modules: Futhark.Analysis.ScalExpTests Futhark.Optimise.AlgSimplifyTests@@ -1164,6 +349,9 @@ Futhark.Representation.AST.AttributesTests Futhark.Representation.AST.Syntax.CoreTests Futhark.Representation.AST.SyntaxTests+ Futhark.Representation.ExplicitMemory.IndexFunction.Alg+ Futhark.Representation.ExplicitMemory.IndexFunctionTests+ Futhark.Representation.ExplicitMemory.IndexFunctionWrapper Futhark.Representation.PrimitiveTests Language.Futhark.CoreTests Language.Futhark.SyntaxTests
futlib/prelude.fut view
@@ -16,6 +16,13 @@ -- | Create integer from double-precision float. let t64 (x: f64): i32 = i32.f64 x +-- | Semantically just identity, but serves as an optimisation+-- inhibitor. The compiler will treat this function as a black box.+-- You can use this to work around optimisation deficiencies (or+-- bugs), although it should hopefully rarely be necessary.+let opaque 't (x: t): t =+ intrinsics.opaque x+ -- | Semantically just identity, but in `futharki` the argument value -- will be printed. let trace 't (x: t): t =
futlib/soacs.fut view
@@ -87,10 +87,10 @@ let reduce 'a (op: a -> a -> a) (ne: a) (as: []a): a = intrinsics.reduce (op, ne, as) --- | As `reduce`, but the operator must also be commutative. This--- is potentially faster than `reduce`. For simple built-in--- operators, like addition, the compiler already knows that the--- operator is associative.+-- | As `reduce`, but the operator must also be commutative. This is+-- potentially faster than `reduce`. For simple built-in operators,+-- like addition, the compiler already knows that the operator is+-- commutative, so plain `reduce`@term will work just as well. -- -- **Work:** *O(n)* --
rts/c/cuda.h view
@@ -242,15 +242,31 @@ return x[chosen].arch_str; } -static char *cuda_nvrtc_build(struct cuda_context *ctx, const char *src)+static char *cuda_nvrtc_build(struct cuda_context *ctx, const char *src,+ const char *extra_opts[]) { nvrtcProgram prog; NVRTC_SUCCEED(nvrtcCreateProgram(&prog, src, "futhark-cuda", 0, NULL, NULL));+ int arch_set = 0, num_extra_opts; - size_t n_opts, i = 0, i_dyn, n_opts_alloc = 20 + ctx->cfg.num_sizes;+ // 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.+ for (num_extra_opts = 0; extra_opts[num_extra_opts] != NULL; num_extra_opts++) {+ if (strstr(extra_opts[num_extra_opts], "-arch")+ == extra_opts[num_extra_opts] ||+ strstr(extra_opts[num_extra_opts], "--gpu-architecture")+ == extra_opts[num_extra_opts]) {+ arch_set = 1;+ }+ }++ size_t n_opts, i = 0, i_dyn, n_opts_alloc = 20 + num_extra_opts + ctx->cfg.num_sizes; const char **opts = malloc(n_opts_alloc * sizeof(const char *));- opts[i++] = "-arch";- opts[i++] = cuda_nvrtc_get_arch(ctx->dev);+ if (!arch_set) {+ opts[i++] = "-arch";+ opts[i++] = cuda_nvrtc_get_arch(ctx->dev);+ } opts[i++] = "-default-device"; if (ctx->cfg.debugging) { opts[i++] = "-G";@@ -265,6 +281,13 @@ } opts[i++] = msgprintf("-DLOCKSTEP_WIDTH=%zu", ctx->lockstep_width); opts[i++] = msgprintf("-DMAX_THREADS_PER_BLOCK=%zu", ctx->max_block_size);++ // It is crucial that the extra_opts are last, so that the free()+ // logic below does not cause problems.+ for (int j = 0; extra_opts[j] != NULL; j++) {+ opts[i++] = extra_opts[j];+ }+ n_opts = i; if (ctx->cfg.debugging) {@@ -288,7 +311,7 @@ NVRTC_SUCCEED(res); } - for (i = i_dyn; i < n_opts; i++) { free((char *)opts[i]); }+ for (i = i_dyn; i < n_opts-num_extra_opts; i++) { free((char *)opts[i]); } free(opts); char *ptx;@@ -356,7 +379,7 @@ if (*size_value == 0) { *size_value = default_value; } else if (max_value > 0 && *size_value > max_value) {- fprintf(stderr, "Note: Device limits %zu to %zu (down from %zu)\n",+ fprintf(stderr, "Note: Device limits %s to %zu (down from %zu)\n", size_name, max_value, *size_value); *size_value = max_value; }@@ -394,16 +417,17 @@ } static void cuda_module_setup(struct cuda_context *ctx,- const char *src_fragments[])+ const char *src_fragments[],+ const char *extra_opts[]) { char *ptx = NULL, *src = NULL; if (ctx->cfg.load_ptx_from == NULL && ctx->cfg.load_program_from == NULL) { src = concat_fragments(src_fragments);- ptx = cuda_nvrtc_build(ctx, src);+ ptx = cuda_nvrtc_build(ctx, src, extra_opts); } else if (ctx->cfg.load_ptx_from == NULL) { load_string_from_file(ctx->cfg.load_program_from, &src, NULL);- ptx = cuda_nvrtc_build(ctx, src);+ ptx = cuda_nvrtc_build(ctx, src, extra_opts); } else { if (ctx->cfg.load_program_from != NULL) { fprintf(stderr,@@ -432,7 +456,7 @@ } } -void cuda_setup(struct cuda_context *ctx, const char *src_fragments[])+void cuda_setup(struct cuda_context *ctx, const char *src_fragments[], const char *extra_opts[]) { CUDA_SUCCEED(cuInit(0)); @@ -450,7 +474,7 @@ ctx->lockstep_width = device_query(ctx->dev, WARP_SIZE); cuda_size_setup(ctx);- cuda_module_setup(ctx, src_fragments);+ cuda_module_setup(ctx, src_fragments, extra_opts); } CUresult cuda_free_all(struct cuda_context *ctx);
rts/c/opencl.h view
@@ -70,8 +70,7 @@ cfg->default_group_size = 0; cfg->default_num_groups = 0; cfg->default_tile_size = 0;-- cfg->default_threshold = 32*1024;+ cfg->default_threshold = 0; cfg->default_group_size_changed = 0; cfg->default_tile_size_changed = 0;@@ -397,31 +396,29 @@ } static cl_build_status build_opencl_program(cl_program program, cl_device_id device, const char* options) {- cl_int ret_val = clBuildProgram(program, 1, &device, options, NULL, NULL);+ cl_int clBuildProgram_error = clBuildProgram(program, 1, &device, options, NULL, NULL); // Avoid termination due to CL_BUILD_PROGRAM_FAILURE- if (ret_val != CL_SUCCESS && ret_val != CL_BUILD_PROGRAM_FAILURE) {- assert(ret_val == 0);+ if (clBuildProgram_error != CL_SUCCESS &&+ clBuildProgram_error != CL_BUILD_PROGRAM_FAILURE) {+ OPENCL_SUCCEED_FATAL(clBuildProgram_error); } cl_build_status build_status;- ret_val = clGetProgramBuildInfo(program,- device,- CL_PROGRAM_BUILD_STATUS,- sizeof(cl_build_status),- &build_status,- NULL);- assert(ret_val == 0);+ OPENCL_SUCCEED_FATAL(clGetProgramBuildInfo(program,+ device,+ CL_PROGRAM_BUILD_STATUS,+ sizeof(cl_build_status),+ &build_status,+ NULL)); if (build_status != CL_SUCCESS) { char *build_log; size_t ret_val_size;- ret_val = clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 0, NULL, &ret_val_size);- assert(ret_val == 0);+ OPENCL_SUCCEED_FATAL(clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 0, NULL, &ret_val_size)); build_log = malloc(ret_val_size+1);- clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, ret_val_size, build_log, NULL);- assert(ret_val == 0);+ OPENCL_SUCCEED_FATAL(clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, ret_val_size, build_log, NULL)); // The spec technically does not say whether the build log is zero-terminated, so let's be careful. build_log[ret_val_size] = '\0';@@ -445,7 +442,8 @@ static cl_program setup_opencl_with_command_queue(struct opencl_context *ctx, cl_command_queue queue, const char *srcs[],- int required_types) {+ int required_types,+ const char *extra_build_opts[]) { int error; ctx->queue = queue;@@ -478,7 +476,7 @@ OPENCL_SUCCEED_FATAL(clGetDeviceInfo(device_option.device, CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, sizeof(cl_uint), &supported, NULL)); if (!supported) {- panic(1, "Program uses double-precision floats, but this is not supported on the chosen device: %s",+ panic(1, "Program uses double-precision floats, but this is not supported on the chosen device: %s\n", device_option.device_name); } }@@ -578,7 +576,7 @@ } cl_program prog;- error = 0;+ error = CL_SUCCESS; const char* src_ptr[] = {fut_opencl_src}; if (ctx->cfg.dump_program_to != NULL) {@@ -590,12 +588,18 @@ if (ctx->cfg.load_binary_from == NULL) { prog = clCreateProgramWithSource(ctx->ctx, 1, src_ptr, &src_size, &error);- assert(error == 0);+ OPENCL_SUCCEED_FATAL(error); int compile_opts_size = 1024;+ for (int i = 0; i < ctx->cfg.num_sizes; i++) { compile_opts_size += strlen(ctx->cfg.size_names[i]) + 20; }++ for (int i = 0; extra_build_opts[i] != NULL; i++) {+ compile_opts_size += strlen(extra_build_opts[i] + 1);+ }+ char *compile_opts = malloc(compile_opts_size); int w = snprintf(compile_opts, compile_opts_size,@@ -609,6 +613,11 @@ (int)ctx->cfg.size_values[i]); } + for (int i = 0; extra_build_opts[i] != NULL; i++) {+ w += snprintf(compile_opts+w, compile_opts_size-w,+ "%s ", extra_build_opts[i]);+ }+ OPENCL_SUCCEED_FATAL(build_opencl_program(prog, device_option.device, compile_opts)); free(compile_opts);@@ -650,7 +659,8 @@ static cl_program setup_opencl(struct opencl_context *ctx, const char *srcs[],- int required_types) {+ int required_types,+ const char *extra_build_opts[]) { ctx->lockstep_width = 0; // Real value set later. @@ -669,15 +679,15 @@ 0 }; - cl_int error;-- ctx->ctx = clCreateContext(properties, 1, &device_option.device, NULL, NULL, &error);- assert(error == 0);+ cl_int clCreateContext_error;+ ctx->ctx = clCreateContext(properties, 1, &device_option.device, NULL, NULL, &clCreateContext_error);+ OPENCL_SUCCEED_FATAL(clCreateContext_error); - cl_command_queue queue = clCreateCommandQueue(ctx->ctx, device_option.device, 0, &error);- assert(error == 0);+ cl_int clCreateCommandQueue_error;+ cl_command_queue queue = clCreateCommandQueue(ctx->ctx, device_option.device, 0, &clCreateCommandQueue_error);+ OPENCL_SUCCEED_FATAL(clCreateCommandQueue_error); - return setup_opencl_with_command_queue(ctx, queue, srcs, required_types);+ return setup_opencl_with_command_queue(ctx, queue, srcs, required_types, extra_build_opts); } // Allocate memory from driver. The problem is that OpenCL may perform
+ rts/c/tuning.h view
@@ -0,0 +1,40 @@+// Start of tuning.h++static char* load_tuning_file(const char *fname,+ void *cfg,+ int (*set_size)(void*, const char*, size_t)) {+ const int max_line_len = 1024;+ char* line = malloc(max_line_len);++ FILE *f = fopen(fname, "r");++ if (f == NULL) {+ snprintf(line, max_line_len, "Cannot open file: %s", strerror(errno));+ return line;+ }++ int lineno = 0;+ while (fgets(line, max_line_len, f) != NULL) {+ lineno++;+ char *eql = strstr(line, "=");+ if (eql) {+ *eql = 0;+ int value = atoi(eql+1);+ if (set_size(cfg, line, value) != 0) {+ strncpy(eql+1, line, max_line_len-strlen(line)-1);+ snprintf(line, max_line_len, "Unknown name '%s' on line %d.", eql+1, lineno);+ return line;+ }+ } else {+ snprintf(line, max_line_len, "Invalid line %d (must be of form 'name=int').",+ lineno);+ return line;+ }+ }++ free(line);++ return NULL;+}++// End of tuning.h
rts/csharp/opencl.cs view
@@ -374,10 +374,13 @@ cfg.DumpProgramTo = null; cfg.LoadProgramFrom = null; - cfg.DefaultGroupSize = 256;- cfg.DefaultNumGroups = 128;- cfg.DefaultTileSize = 32;- cfg.DefaultThreshold = 32*1024;+ // The following are dummy sizes that mean the concrete defaults+ // will be set during initialisation via hardware-inspection-based+ // heuristics.+ cfg.DefaultGroupSize = 0;+ cfg.DefaultNumGroups = 0;+ cfg.DefaultTileSize = 0;+ cfg.DefaultThreshold = 0; cfg.NumSizes = num_sizes; cfg.SizeNames = size_names;@@ -774,6 +777,9 @@ int MaxTileSize = (int) Math.Sqrt(MaxGroupSize); + // Make sure this function is defined.+ PostOpenCLSetup(ref ctx, ref device_option);+ if (MaxGroupSize < ctx.OpenCL.Cfg.DefaultGroupSize) { Console.Error.WriteLine("Note: Device limits default group size to {0} (down from {1}).\n", MaxGroupSize, ctx.OpenCL.Cfg.DefaultGroupSize);@@ -798,16 +804,16 @@ string size_name = ctx.OpenCL.Cfg.SizeNames[i]; int max_value, default_value; max_value = default_value = 0;- if (size_class == "group_size") {+ if (size_class.StartsWith("group_size")) { max_value = MaxGroupSize; default_value = ctx.OpenCL.Cfg.DefaultGroupSize;- } else if (size_class == "num_groups") {+ } else if (size_class.StartsWith("num_groups")) { max_value = MaxGroupSize; // Futhark assumes this constraint. default_value = ctx.OpenCL.Cfg.DefaultNumGroups;- } else if (size_class == "tile_size"){+ } else if (size_class.StartsWith("tile_size")){ max_value = (int) Math.Sqrt(MaxGroupSize); default_value = ctx.OpenCL.Cfg.DefaultTileSize;- } else if (size_class == "threshold") {+ } else if (size_class.StartsWith("threshold")) { max_value = 0; // No limit. default_value = ctx.OpenCL.Cfg.DefaultThreshold; } else {@@ -835,9 +841,6 @@ ctx.OpenCL.Queue = CL10.CreateCommandQueue(ctx.OpenCL.Context, device, 0, out error); Debug.Assert(error == 0); - // Make sure this function is defined.- PostOpenCLSetup(ref ctx, ref device_option);- if (ctx.Debugging) { Console.Error.WriteLine("Lockstep width: {0}\n", (int)ctx.OpenCL.LockstepWidth); Console.Error.WriteLine("Default group size: {0}\n", (int)ctx.OpenCL.Cfg.DefaultGroupSize);@@ -916,8 +919,31 @@ var name = name_and_value[0]; var value = Convert.ToInt32(name_and_value[1]);- if (!FutharkContextConfigSetSize(ref config, name, value))+ if (name == "default_num_groups") {+ config.OpenCL.DefaultNumGroups = value;+ }+ else if (name == "default_group_size") {+ config.OpenCL.DefaultGroupSize = value;+ }+ else if (name == "default_tile_size") {+ config.OpenCL.DefaultTileSize = value;+ }+ else if (name == "default_threshold") {+ config.OpenCL.DefaultThreshold = value;+ }+ else if (!FutharkContextConfigSetSize(ref config, name, value)) { panic(1, "Unknown size: {0}", name); }+}++private void FutharkConfigLoadTuning(ref FutharkContextConfig config, string fname)+{+ StreamReader file = new StreamReader(fname);+ String line;+ while((line = file.ReadLine()) != null)+ {+ FutharkConfigSetSize(ref config, line);+ }+ file.Close(); }
rts/python/opencl.py view
@@ -55,6 +55,10 @@ device_matches += 1 raise Exception('No OpenCL platform and device matching constraints found.') +def size_assignment(s):+ name, value = s.split('=')+ return (name, int(value))+ def check_types(self, required_types): if 'f64' in required_types: if self.device.get_info(cl.device_info.PREFERRED_VECTOR_WIDTH_DOUBLE) == 0:@@ -107,6 +111,22 @@ self.max_num_groups = 0 self.free_list = {} + if 'default_group_size' in sizes:+ default_group_size = sizes['default_group_size']+ del sizes['default_group_size']++ if 'default_num_groups' in sizes:+ default_num_groups = sizes['default_num_groups']+ del sizes['default_num_groups']++ if 'default_tile_size' in sizes:+ default_tile_size = sizes['default_tile_size']+ del sizes['default_tile_size']++ if 'default_threshold' in sizes:+ default_threshold = sizes['default_threshold']+ del sizes['default_threshold']+ default_group_size_set = default_group_size != None default_tile_size_set = default_tile_size != None default_sizes = apply_size_heuristics(self, size_heuristics,@@ -137,7 +157,7 @@ if k in all_sizes: all_sizes[k]['value'] = v else:- raise Exception('Unknown size: {}'.format(k))+ raise Exception('Unknown size: {}\nKnown sizes: {}'.format(k, ' '.join(all_sizes.keys()))) self.sizes = {} for (k,v) in all_sizes.items():
+ rts/python/tuning.py view
@@ -0,0 +1,11 @@+### start of tuning.py+###+### Reading the .tuning file.++def read_tuning_file(kvs, f):+ for line in f.read().splitlines():+ size, value = line.split('=')+ kvs[size] = int(value)+ return kvs++### end of tuning.py
src/Futhark/CLI/Bench.hs view
@@ -45,11 +45,12 @@ , optExcludeCase :: [String] , optIgnoreFiles :: [Regex] , optEntryPoint :: Maybe String+ , optTuning :: Maybe String } initialBenchOptions :: BenchOptions initialBenchOptions = BenchOptions "c" "futhark" "" 10 [] Nothing (-1) False- ["nobench", "disable"] [] Nothing+ ["nobench", "disable"] [] Nothing (Just "tuning") -- | The name we use for compiled programs. binaryName :: FilePath -> FilePath@@ -158,9 +159,13 @@ runBenchmark :: BenchOptions -> (FilePath, [InputOutputs]) -> IO [BenchResult] runBenchmark opts (program, cases) = mapM forInputOutputs $ filter relevant cases where forInputOutputs (InputOutputs entry_name runs) = do- putStr $ "Results for " ++ program' ++ ":\n"+ (tuning_opts, tuning_desc) <- determineTuning (optTuning opts) program++ putStr $ "Results for " ++ program' ++ tuning_desc ++ ":\n"+ let opts' = opts { optExtraOptions =+ optExtraOptions opts ++ tuning_opts } BenchResult program' . catMaybes <$>- mapM (runBenchmarkCase opts program entry_name pad_to) runs+ mapM (runBenchmarkCase opts' program entry_name pad_to) runs where program' = if entry_name == "main" then program else program ++ ":" ++ T.unpack entry_name@@ -368,6 +373,13 @@ config { optEntryPoint = Just s }) "NAME") "Only run this entry point."+ , Option [] ["tuning"]+ (ReqArg (\s -> Right $ \config -> config { optTuning = Just s })+ "EXTENSION")+ "Look for tuning files with this extension (defaults: .tuning)."+ , Option [] ["no-tuning"]+ (NoArg $ Right $ \config -> config { optTuning = Nothing })+ "Do not load tuning files." ] where max_timeout :: Int max_timeout = maxBound `div` 1000000
src/Futhark/CLI/Datacmp.hs view
@@ -3,7 +3,6 @@ import qualified Data.ByteString.Lazy.Char8 as BS import System.Exit-import System.IO import Futhark.Test.Values import Futhark.Util.Options@@ -21,9 +20,8 @@ (Just vs_a, Just vs_b) -> case compareValues vs_a vs_b of [] -> return ()- es -> do- mapM_ (hPrint stderr) es- exitWith $ ExitFailure 2+ es -> do mapM_ print es+ exitWith $ ExitFailure 2 f _ _ = Nothing
src/Futhark/CLI/Dev.hs view
@@ -66,6 +66,9 @@ -- ^ Nothing is distinct from a empty pipeline - -- it means we don't even run the internaliser. , futharkAction :: UntypedAction+ , futharkPrintAST :: Bool+ -- ^ If true, prints programs as raw ASTs instead+ -- of their prettyprinted form. } @@ -121,7 +124,8 @@ representation PolyAction{} = "<any>" newConfig :: Config-newConfig = Config newFutharkConfig (Pipeline []) $ PolyAction printAction printAction printAction+newConfig = Config newFutharkConfig (Pipeline [])+ (PolyAction printAction printAction printAction) False changeFutharkConfig :: (FutharkConfig -> FutharkConfig) -> Config -> Config@@ -251,7 +255,7 @@ , Option "t" ["type-check"] (NoArg $ Right $ \opts -> opts { futharkPipeline = TypeCheck })- "Type-check the program and print errors on standard error."+ "Print on standard output the type-checked program." , Option [] ["pretty-print"] (NoArg $ Right $ \opts ->@@ -284,6 +288,9 @@ , Option [] ["defunctionalise"] (NoArg $ Right $ \opts -> opts { futharkPipeline = Defunctionalise }) "Defunctionalise the program."+ , Option [] ["ast"]+ (NoArg $ Right $ \opts -> opts { futharkPrintAST = True })+ "Output ASTs instead of prettyprinted programs." , typedPassOption soacsProg Kernels firstOrderTransform "f" , soacsPassOption fuseSOACs "o" , soacsPassOption inlineAndRemoveDeadFunctions []@@ -336,27 +343,32 @@ Nothing m file config = case futharkPipeline config of- TypeCheck -> do- -- No pipeline; just read the program and type check- (warnings, _, _) <- readProgram file- liftIO $ hPutStr stderr $ show warnings PrettyPrint -> liftIO $ do maybe_prog <- parseFuthark file <$> T.readFile file case maybe_prog of Left err -> fail $ show err- Right prog -> putStrLn $ pretty prog+ Right prog | futharkPrintAST config -> print prog+ | otherwise -> putStrLn $ pretty prog+ TypeCheck -> do+ (_, imports, _) <- readProgram file+ liftIO $ forM_ (map snd imports) $ \fm ->+ putStrLn $ if futharkPrintAST config+ then show $ fileProg fm+ else pretty $ fileProg fm Defunctorise -> do (_, imports, src) <- readProgram file- liftIO $ mapM_ (putStrLn . pretty) $+ liftIO $ mapM_ (putStrLn . if futharkPrintAST config then show else pretty) $ evalState (Defunctorise.transformProg imports) src Monomorphise -> do (_, imports, src) <- readProgram file- liftIO $ mapM_ (putStrLn . pretty) $ flip evalState src $+ liftIO $ mapM_ (putStrLn . if futharkPrintAST config then show else pretty) $+ flip evalState src $ Defunctorise.transformProg imports >>= Monomorphise.transformProg Defunctionalise -> do (_, imports, src) <- readProgram file- liftIO $ mapM_ (putStrLn . pretty) $ flip evalState src $+ liftIO $ mapM_ (putStrLn . if futharkPrintAST config then show else pretty) $+ flip evalState src $ Defunctorise.transformProg imports >>= Monomorphise.transformProg >>= Defunctionalise.transformProg
src/Futhark/CLI/Misc.hs view
@@ -2,9 +2,11 @@ -- Various small subcommands that are too simple to deserve their own file. module Futhark.CLI.Misc ( mainCheck+ , mainImports ) where +import Data.List (isPrefixOf) import Control.Monad.State import System.IO import System.Exit@@ -29,3 +31,14 @@ _ -> Nothing where check file = do (warnings, _, _) <- readProgram file liftIO $ hPutStr stderr $ show warnings++mainImports :: String -> [String] -> IO ()+mainImports = mainWithOptions () [] "program" $ \args () ->+ case args of+ [file] -> Just $ runFutharkM' $ findImports file+ _ -> Nothing+ where findImports file = do+ (_, prog_imports, _) <- readProgram file+ liftIO $ putStr $ unlines $ map (++ ".fut")+ $ filter (\f -> not ("futlib/" `isPrefixOf` f))+ $ map fst prog_imports
src/Futhark/CLI/Pkg.hs view
@@ -283,7 +283,7 @@ Nothing -> liftIO $ T.putStrLn $ "Added new required package " <> p <> " " <> prettySemVer v <> "." putPkgManifest m'- liftIO $ T.putStrLn "Remember to run 'futhark-pkg sync'."+ liftIO $ T.putStrLn "Remember to run 'futhark pkg sync'." doRemove :: String -> [String] -> IO () doRemove = cmdMain "PKGPATH" $ \args cfg ->@@ -326,6 +326,9 @@ m <- getPkgManifest rs <- traverse (mapM (traverse upgrade)) $ manifestRequire m putPkgManifest m { manifestRequire = rs }+ if rs == manifestRequire m+ then liftIO $ T.putStrLn "Nothing to upgrade."+ else liftIO $ T.putStrLn "Remember to run 'futhark pkg sync'." _ -> Nothing where upgrade req = do v <- lookupNewestRev $ requiredPkg req
src/Futhark/CLI/REPL.hs view
@@ -91,7 +91,7 @@ confirmQuit :: Haskeline.InputT IO Bool confirmQuit = do- c <- Haskeline.getInputChar "Quit futharki? (y/n) "+ c <- Haskeline.getInputChar "Quit REPL? (y/n) " case c of Nothing -> return True -- EOF Just 'y' -> return True
src/Futhark/CLI/Test.hs view
@@ -169,9 +169,13 @@ context ("Compiling with --backend=" <> T.pack backend) $ do compileTestProgram extra_options futhark backend program warnings mapM_ (testMetrics progs program) structures- unless (mode == Compile) $+ unless (mode == Compile) $ do+ (tuning_opts, _) <-+ liftIO $ determineTuning (configTuning progs) program+ let progs' = progs { configExtraOptions =+ tuning_opts ++ configExtraOptions progs } context "Running compiled program" $- accErrors_ $ map (runCompiledEntry program progs) ios+ accErrors_ $ map (runCompiledEntry program progs') ios unless (mode == Compile || mode == Compiled) $ context "Interpreting" $ accErrors_ $ map (runInterpretedEntry futhark program) ios@@ -380,8 +384,10 @@ _ <- forkIO $ mapM_ (putMVar testmvar) included isTTY <- (&& not (configLineOutput config)) <$> hIsTerminalDevice stdout - let report = if isTTY then reportTable else reportText- clear = if isTTY then clearFromCursorToScreenEnd else putStr "\n"+ let report | isTTY = reportTable+ | otherwise = reportText+ clear | isTTY = clearFromCursorToScreenEnd+ |otherwise = putStr "\n" numTestCases tc = case testAction $ testCaseTest tc of@@ -442,9 +448,8 @@ -- Removes "Now testing" output. when isTTY $ cursorUpLine 1 >> clearLine - let excluded_str = if null excluded- then ""- else " (" ++ show (length excluded) ++ " program(s) excluded).\n"+ let excluded_str | null excluded = ""+ | otherwise = " (" ++ show (length excluded) ++ " program(s) excluded).\n" putStr excluded_str exitWith $ case testStatusFail ts of 0 -> ExitSuccess _ -> ExitFailure 1@@ -473,6 +478,7 @@ , configRunner = "" , configExtraOptions = [] , configExtraCompilerOptions = []+ , configTuning = Just "tuning" } , configLineOutput = False }@@ -482,6 +488,7 @@ , configFuthark :: FilePath , configRunner :: FilePath , configExtraCompilerOptions :: [String]+ , configTuning :: Maybe String , configExtraOptions :: [String] -- ^ Extra options passed to the programs being run. }
src/Futhark/CodeGen/Backends/CCUDA.hs view
@@ -51,24 +51,31 @@ cliOptions :: [Option] cliOptions = [ Option { optionLongName = "dump-cuda" , optionShortName = Nothing- , optionArgument = RequiredArgument- , optionAction = [C.cstm|futhark_context_config_dump_program_to(cfg, optarg);|]+ , optionArgument = RequiredArgument "FILE"+ , optionAction = [C.cstm|{futhark_context_config_dump_program_to(cfg, optarg);+ entry_point = NULL;}|] } , Option { optionLongName = "load-cuda" , optionShortName = Nothing- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "FILE" , optionAction = [C.cstm|futhark_context_config_load_program_from(cfg, optarg);|] } , Option { optionLongName = "dump-ptx" , optionShortName = Nothing- , optionArgument = RequiredArgument- , optionAction = [C.cstm|futhark_context_config_dump_ptx_to(cfg, optarg);|]+ , optionArgument = RequiredArgument "FILE"+ , optionAction = [C.cstm|{futhark_context_config_dump_ptx_to(cfg, optarg);+ entry_point = NULL;}|] } , Option { optionLongName = "load-ptx" , optionShortName = Nothing- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "FILE" , optionAction = [C.cstm|futhark_context_config_load_ptx_from(cfg, optarg);|] }+ , Option { optionLongName = "nvrtc-option"+ , optionShortName = Nothing+ , optionArgument = RequiredArgument "OPT"+ , optionAction = [C.cstm|futhark_context_config_add_nvrtc_option(cfg, optarg);|]+ } , Option { optionLongName = "print-sizes" , optionShortName = Nothing , optionArgument = NoArgument@@ -81,6 +88,17 @@ exit(0); }|] }+ , Option { optionLongName = "tuning"+ , optionShortName = Nothing+ , optionArgument = RequiredArgument "FILE"+ , optionAction = [C.cstm|{+ char *fname = optarg;+ char *ret = load_tuning_file(optarg, cfg, (int(*)(void*, const char*, size_t))+ futhark_context_config_set_size);+ if (ret != NULL) {+ panic(1, "When loading tuning from '%s': %s\n", optarg, ret);+ }}|]+ } ] writeCUDAScalar :: GC.WriteScalar OpenCL ()@@ -138,12 +156,17 @@ ++ "' from '" ++ show srcSpace ++ "'." staticCUDAArray :: GC.StaticArray OpenCL ()-staticCUDAArray name "device" t vals = do+staticCUDAArray name "device" t vs = do let ct = GC.primTypeToCType t- vals' = [[C.cinit|$exp:v|] | v <- map GC.compilePrimValue vals]- num_elems = length vals name_realtype <- newVName $ baseString name ++ "_realtype"- GC.libDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:num_elems] = {$inits:vals'};|]+ num_elems <- case vs of+ ArrayValues vs' -> do+ let vs'' = [[C.cinit|$exp:v|] | v <- map GC.compilePrimValue vs']+ GC.libDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:(length vs'')] = {$inits:vs''};|]+ return $ length vs''+ ArrayZeros n -> do+ GC.libDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:n];|]+ return n -- Fake a memory block. GC.contextField (pretty name) [C.cty|struct memblock_device|] Nothing -- During startup, copy the data to where we need it.@@ -274,4 +297,4 @@ printSizes = intercalate [[C.cstm|fprintf(stderr, ", ");|]] . map printSize printSize e =- [[C.cstm|fprintf(stderr, "%zu", $exp:e);|]]+ [[C.cstm|fprintf(stderr, "%d", $exp:e);|]]
src/Futhark/CodeGen/Backends/CCUDA/Boilerplate.hs view
@@ -77,6 +77,8 @@ ([C.cedecl|struct $id:s;|], [C.cedecl|struct $id:s { struct cuda_config cu_cfg; size_t sizes[$int:num_sizes];+ int num_nvrtc_opts;+ const char **nvrtc_opts; };|]) let size_value_inits = map (\i -> [C.cstm|cfg->sizes[$int:i] = 0;|])@@ -89,6 +91,9 @@ return NULL; } + cfg->num_nvrtc_opts = 0;+ cfg->nvrtc_opts = malloc(sizeof(const char*));+ cfg->nvrtc_opts[0] = NULL; $stms:size_value_inits cuda_config_init(&cfg->cu_cfg, $int:num_sizes, size_names, size_vars,@@ -99,9 +104,19 @@ GC.publicDef_ "context_config_free" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg);|], [C.cedecl|void $id:s(struct $id:cfg* cfg) {+ free(cfg->nvrtc_opts); free(cfg); }|]) + GC.publicDef_ "context_config_add_nvrtc_option" GC.InitDecl $ \s ->+ ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *opt);|],+ [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *opt) {+ cfg->nvrtc_opts[cfg->num_nvrtc_opts] = opt;+ cfg->num_nvrtc_opts++;+ cfg->nvrtc_opts = realloc(cfg->nvrtc_opts, (cfg->num_nvrtc_opts+1) * sizeof(const char*));+ cfg->nvrtc_opts[cfg->num_nvrtc_opts] = NULL;+ }|])+ GC.publicDef_ "context_config_set_debugging" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|], [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) {@@ -222,7 +237,7 @@ create_lock(&ctx->lock); $stms:init_fields - cuda_setup(&ctx->cuda, cuda_program);+ cuda_setup(&ctx->cuda, cuda_program, cfg->nvrtc_opts); $stms:(map (loadKernelByName) kernel_names) $stms:final_inits
src/Futhark/CodeGen/Backends/COpenCL.hs view
@@ -45,6 +45,7 @@ } include_opencl_h = unlines ["#define CL_USE_DEPRECATED_OPENCL_1_2_APIS", "#ifdef __APPLE__",+ "#define CL_SILENCE_DEPRECATION", "#include <OpenCL/cl.h>", "#else", "#include <CL/cl.h>",@@ -53,54 +54,61 @@ cliOptions :: [Option] cliOptions = [ Option { optionLongName = "platform" , optionShortName = Just 'p'- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "NAME" , optionAction = [C.cstm|futhark_context_config_set_platform(cfg, optarg);|] } , Option { optionLongName = "device" , optionShortName = Just 'd'- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "NAME" , optionAction = [C.cstm|futhark_context_config_set_device(cfg, optarg);|] } , Option { optionLongName = "default-group-size" , optionShortName = Nothing- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "INT" , optionAction = [C.cstm|futhark_context_config_set_default_group_size(cfg, atoi(optarg));|] } , Option { optionLongName = "default-num-groups" , optionShortName = Nothing- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "INT" , optionAction = [C.cstm|futhark_context_config_set_default_num_groups(cfg, atoi(optarg));|] } , Option { optionLongName = "default-tile-size" , optionShortName = Nothing- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "INT" , optionAction = [C.cstm|futhark_context_config_set_default_tile_size(cfg, atoi(optarg));|] } , Option { optionLongName = "default-threshold" , optionShortName = Nothing- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "INT" , optionAction = [C.cstm|futhark_context_config_set_default_threshold(cfg, atoi(optarg));|] } , Option { optionLongName = "dump-opencl" , optionShortName = Nothing- , optionArgument = RequiredArgument- , optionAction = [C.cstm|futhark_context_config_dump_program_to(cfg, optarg);|]+ , optionArgument = RequiredArgument "FILE"+ , optionAction = [C.cstm|{futhark_context_config_dump_program_to(cfg, optarg);+ entry_point = NULL;}|] } , Option { optionLongName = "load-opencl" , optionShortName = Nothing- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "FILE" , optionAction = [C.cstm|futhark_context_config_load_program_from(cfg, optarg);|] } , Option { optionLongName = "dump-opencl-binary" , optionShortName = Nothing- , optionArgument = RequiredArgument- , optionAction = [C.cstm|futhark_context_config_dump_binary_to(cfg, optarg);|]+ , optionArgument = RequiredArgument "FILE"+ , optionAction = [C.cstm|{futhark_context_config_dump_binary_to(cfg, optarg);+ entry_point = NULL;}|] } , Option { optionLongName = "load-opencl-binary" , optionShortName = Nothing- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "FILE" , optionAction = [C.cstm|futhark_context_config_load_binary_from(cfg, optarg);|] }+ , Option { optionLongName = "build-option"+ , optionShortName = Nothing+ , optionArgument = RequiredArgument "OPT"+ , optionAction = [C.cstm|futhark_context_config_add_build_option(cfg, optarg);|]+ } , Option { optionLongName = "print-sizes" , optionShortName = Nothing , optionArgument = NoArgument@@ -115,7 +123,7 @@ } , Option { optionLongName = "size" , optionShortName = Nothing- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "NAME=INT" , optionAction = [C.cstm|{ char *name = optarg; char *equals = strstr(optarg, "=");@@ -130,6 +138,17 @@ panic(1, "Invalid argument for size option: %s\n", optarg); }}|] }+ , Option { optionLongName = "tuning"+ , optionShortName = Nothing+ , optionArgument = RequiredArgument "FILE"+ , optionAction = [C.cstm|{+ char *fname = optarg;+ char *ret = load_tuning_file(optarg, cfg, (int(*)(void*, const char*, size_t))+ futhark_context_config_set_size);+ if (ret != NULL) {+ panic(1, "When loading tuning from '%s': %s\n", optarg, ret);+ }}|]+ } ] -- We detect the special case of writing a constant and turn it into a@@ -240,10 +259,15 @@ staticOpenCLArray :: GC.StaticArray OpenCL () staticOpenCLArray name "device" t vs = do let ct = GC.primTypeToCType t- vs' = [[C.cinit|$exp:v|] | v <- map GC.compilePrimValue vs]- num_elems = length vs name_realtype <- newVName $ baseString name ++ "_realtype"- GC.libDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:num_elems] = {$inits:vs'};|]+ num_elems <- case vs of+ ArrayValues vs' -> do+ let vs'' = [[C.cinit|$exp:v|] | v <- map GC.compilePrimValue vs']+ GC.libDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:(length vs'')] = {$inits:vs''};|]+ return $ length vs''+ ArrayZeros n -> do+ GC.libDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:n];|]+ return n -- Fake a memory block. GC.contextField (pretty name) [C.cty|struct memblock_device|] Nothing -- During startup, copy the data to where we need it.
src/Futhark/CodeGen/Backends/COpenCL/Boilerplate.hs view
@@ -61,6 +61,8 @@ ([C.cedecl|struct $id:s;|], [C.cedecl|struct $id:s { struct opencl_config opencl; size_t sizes[$int:num_sizes];+ int num_build_opts;+ const char **build_opts; };|]) let size_value_inits = map (\i -> [C.cstm|cfg->sizes[$int:i] = 0;|]) [0..M.size sizes-1]@@ -72,6 +74,9 @@ return NULL; } + cfg->num_build_opts = 0;+ cfg->build_opts = malloc(sizeof(const char*));+ cfg->build_opts[0] = NULL; $stms:size_value_inits opencl_config_init(&cfg->opencl, $int:num_sizes, size_names, size_vars,@@ -82,9 +87,19 @@ GC.publicDef_ "context_config_free" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg);|], [C.cedecl|void $id:s(struct $id:cfg* cfg) {+ free(cfg->build_opts); free(cfg); }|]) + GC.publicDef_ "context_config_add_build_option" GC.InitDecl $ \s ->+ ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *opt);|],+ [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *opt) {+ cfg->build_opts[cfg->num_build_opts] = opt;+ cfg->num_build_opts++;+ cfg->build_opts = realloc(cfg->build_opts, (cfg->num_build_opts+1) * sizeof(const char*));+ cfg->build_opts[cfg->num_build_opts] = NULL;+ }|])+ GC.publicDef_ "context_config_set_debugging" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|], [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) {@@ -170,6 +185,27 @@ return 0; } }++ if (strcmp(size_name, "default_group_size") == 0) {+ cfg->opencl.default_group_size = size_value;+ return 0;+ }++ if (strcmp(size_name, "default_num_groups") == 0) {+ cfg->opencl.default_num_groups = size_value;+ return 0;+ }++ if (strcmp(size_name, "default_threshold") == 0) {+ cfg->opencl.default_threshold = size_value;+ return 0;+ }++ if (strcmp(size_name, "default_tile_size") == 0) {+ cfg->opencl.default_tile_size = size_value;+ return 0;+ }+ return 1; }|]) @@ -231,7 +267,7 @@ $stms:set_required_types init_context_early(cfg, ctx);- typename cl_program prog = setup_opencl(&ctx->opencl, opencl_program, required_types);+ typename cl_program prog = setup_opencl(&ctx->opencl, opencl_program, required_types, cfg->build_opts); init_context_late(cfg, ctx, prog); return ctx; }|])@@ -248,7 +284,7 @@ $stms:set_required_types init_context_early(cfg, ctx);- typename cl_program prog = setup_opencl_with_command_queue(&ctx->opencl, queue, opencl_program, required_types);+ typename cl_program prog = setup_opencl_with_command_queue(&ctx->opencl, queue, opencl_program, required_types, cfg->build_opts); init_context_late(cfg, ctx, prog); return ctx; }|])@@ -403,6 +439,7 @@ NumGroups -> [C.cexp|ctx->cfg.default_num_groups|] GroupSize -> [C.cexp|ctx->cfg.default_group_size|] TileSize -> [C.cexp|ctx->cfg.default_tile_size|]+ Threshold -> [C.cexp|ctx->cfg.default_threshold|] get_size = case what of HeuristicConst x ->
src/Futhark/CodeGen/Backends/CSOpenCL.hs view
@@ -337,11 +337,17 @@ -- Create host-side C# array with intended values. tmp_arr <- newVName' "tmpArr" let t' = CS.compilePrimTypeToAST t- CS.staticMemDecl $ AssignTyped (Composite $ ArrayT t') (Var tmp_arr) (Just $ CreateArray t' $ map CS.compilePrimValue vs)+ CS.staticMemDecl $ AssignTyped (Composite $ ArrayT t') (Var tmp_arr) $ Just $+ case vs of Imp.ArrayValues vs' ->+ CreateArray (CS.compilePrimTypeToAST t) $ Right $ map CS.compilePrimValue vs'+ Imp.ArrayZeros n ->+ CreateArray (CS.compilePrimTypeToAST t) $ Left n -- Create memory block on the device. ptr <- newVName' "ptr"- let size = Integer $ genericLength vs * Imp.primByteSize t+ let num_elems = case vs of Imp.ArrayValues vs' -> length vs'+ Imp.ArrayZeros n -> n+ size = Integer $ toInteger num_elems * Imp.primByteSize t CS.staticMemAlloc $ Reassign (Var name') (CS.simpleCall "EmptyMemblock" [Var "Ctx.EMPTY_MEM_HANDLE"]) errcode <- CS.compileName <$> newVName "errCode"@@ -375,7 +381,7 @@ let createTuple = "createTuple_"++ pretty bt' return $ CS.simpleCall createTuple [ memblockFromMem mem, Var "Ctx.OpenCL.Queue", nbytes- , CreateArray (Primitive $ CSInt Int64T) dims']+ , CreateArray (Primitive $ CSInt Int64T) $ Right dims'] where dims' = map CS.compileDim dims packArrayOutput _ sid _ _ _ =
src/Futhark/CodeGen/Backends/CSOpenCL/Boilerplate.hs view
@@ -271,6 +271,7 @@ NumGroups -> Var "Ctx.OpenCL.Cfg.DefaultNumGroups" GroupSize -> Var "Ctx.OpenCL.Cfg.DefaultGroupSize" TileSize -> Var "Ctx.OpenCL.Cfg.DefaultTileSize"+ Threshold -> Var "Ctx.OpenCL.Cfg.DefaultThreshold" get_size = case what of HeuristicConst x ->
src/Futhark/CodeGen/Backends/GenericC.hs view
@@ -161,7 +161,7 @@ type Deallocate op s = C.Exp -> C.Exp -> SpaceId -> CompilerM op s () -- | Create a static array of values - initialised at load time.-type StaticArray op s = VName -> SpaceId -> PrimType -> [PrimValue] -> CompilerM op s ()+type StaticArray op s = VName -> SpaceId -> PrimType -> ArrayContents -> CompilerM op s () -- | Copy from one memory block to another. type Copy op s = C.Exp -> C.Exp -> Space ->@@ -1221,12 +1221,12 @@ benchmarkOptions = [ Option { optionLongName = "write-runtime-to" , optionShortName = Just 't'- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "FILE" , optionAction = set_runtime_file } , Option { optionLongName = "runs" , optionShortName = Just 'r'- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "INT" , optionAction = set_num_runs } , Option { optionLongName = "debugging"@@ -1241,8 +1241,8 @@ } , Option { optionLongName = "entry-point" , optionShortName = Just 'e'- , optionArgument = RequiredArgument- , optionAction = [C.cstm|entry_point = optarg;|]+ , optionArgument = RequiredArgument "NAME"+ , optionAction = [C.cstm|if (entry_point != NULL) entry_point = optarg;|] } , Option { optionLongName = "binary-output" , optionShortName = Just 'b'@@ -1356,8 +1356,11 @@ static typename FILE *runtime_file; static int perform_warmup = 0; static int num_runs = 1;+// If the entry point is NULL, the program will terminate after doing initialisation and such. static const char *entry_point = "main"; +$esc:tuning_h+ $func:option_parser $edecls:cli_entry_point_decls@@ -1390,31 +1393,33 @@ struct futhark_context *ctx = futhark_context_new(cfg); assert (ctx != NULL); - int num_entry_points = sizeof(entry_points) / sizeof(entry_points[0]);- entry_point_fun *entry_point_fun = NULL;- for (int i = 0; i < num_entry_points; i++) {- if (strcmp(entry_points[i].name, entry_point) == 0) {- entry_point_fun = entry_points[i].fun;- break;+ if (entry_point != NULL) {+ int num_entry_points = sizeof(entry_points) / sizeof(entry_points[0]);+ entry_point_fun *entry_point_fun = NULL;+ for (int i = 0; i < num_entry_points; i++) {+ if (strcmp(entry_points[i].name, entry_point) == 0) {+ entry_point_fun = entry_points[i].fun;+ break;+ } }- } - if (entry_point_fun == NULL) {- fprintf(stderr, "No entry point '%s'. Select another with --entry-point. Options are:\n",- entry_point);- for (int i = 0; i < num_entry_points; i++) {- fprintf(stderr, "%s\n", entry_points[i].name);+ if (entry_point_fun == NULL) {+ fprintf(stderr, "No entry point '%s'. Select another with --entry-point. Options are:\n",+ entry_point);+ for (int i = 0; i < num_entry_points; i++) {+ fprintf(stderr, "%s\n", entry_points[i].name);+ }+ return 1; }- return 1;- } - entry_point_fun(ctx);+ entry_point_fun(ctx); - if (runtime_file != NULL) {- fclose(runtime_file);- }+ if (runtime_file != NULL) {+ fclose(runtime_file);+ } - futhark_debugging_report(ctx);+ futhark_debugging_report(ctx);+ } futhark_context_free(ctx); futhark_context_config_free(cfg);@@ -1489,6 +1494,7 @@ values_h = $(embedStringFile "rts/c/values.h") timing_h = $(embedStringFile "rts/c/timing.h") lock_h = $(embedStringFile "rts/c/lock.h")+ tuning_h = $(embedStringFile "rts/c/tuning.h") compileFun :: (Name, Function op) -> CompilerM op s (C.Definition, C.Func) compileFun (fname, Function _ outputs inputs body _ _) = do@@ -1805,10 +1811,14 @@ decl [C.cdecl|$ty:ct $id:name;|] compileCode (DeclareArray name DefaultSpace t vs) = do- let ct = primTypeToCType t- vs' = [[C.cinit|$exp:(compilePrimValue v)|] | v <- vs] name_realtype <- newVName $ baseString name ++ "_realtype"- libDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:(length vs)] = {$inits:vs'};|]+ let ct = primTypeToCType t+ case vs of+ ArrayValues vs' -> do+ let vs'' = [[C.cinit|$exp:(compilePrimValue v)|] | v <- vs']+ libDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:(length vs')] = {$inits:vs''};|]+ ArrayZeros n ->+ libDecl [C.cedecl|static $ty:ct $id:name_realtype[$int:n];|] -- Fake a memory block. contextField (pretty name) [C.cty|struct memblock|] $
src/Futhark/CodeGen/Backends/GenericC/Options.hs view
@@ -28,7 +28,8 @@ -- | Whether an option accepts an argument. data OptionArgument = NoArgument- | RequiredArgument+ | RequiredArgument String+ -- ^ The 'String' becomes part of the help text. | OptionalArgument -- | Generate an option parser as a function of the given name, that@@ -52,7 +53,8 @@ panic(-1, "Missing argument for option %s\n", argv[optind-1]); } if ($id:chosen_option == '?') {- panic(-1, "Unknown option %s\n", argv[optind-1]);+ fprintf(stderr, "Usage: %s: %s\n", fut_progname, $string:option_descriptions);+ panic(1, "Unknown option: %s\n", argv[optind-1]); } } return optind;@@ -62,15 +64,28 @@ option_string = ':' : optionString options option_applications = optionApplications chosen_option options option_fields = optionFields options+ option_descriptions = unwords $ map describeOption options +describeOption :: Option -> String+describeOption opt =+ concat [ "["+ , maybe "" (\c -> "-" ++ [c] ++ "/") $ optionShortName opt+ , "--" ++ optionLongName opt+ , case optionArgument opt of+ NoArgument -> ""+ RequiredArgument what -> " " ++ what+ OptionalArgument -> " [ARG]"+ , "]"+ ]+ optionFields :: [Option] -> [C.Initializer] optionFields = zipWith field [(1::Int)..] where field i option = [C.cinit| { $string:(optionLongName option), $id:arg, NULL, $int:i } |] where arg = case optionArgument option of- NoArgument -> "no_argument"- RequiredArgument -> "required_argument"- OptionalArgument -> "optional_argument"+ NoArgument -> "no_argument"+ RequiredArgument _ -> "required_argument"+ OptionalArgument -> "optional_argument" optionApplications :: String -> [Option] -> [C.Stm] optionApplications chosen_option = zipWith check [(1::Int)..]@@ -86,6 +101,6 @@ short <- optionShortName option return $ short : case optionArgument option of- NoArgument -> ""- RequiredArgument -> ":"- OptionalArgument -> "::"+ NoArgument -> ""+ RequiredArgument _ -> ":"+ OptionalArgument -> "::"
src/Futhark/CodeGen/Backends/GenericCSharp.hs view
@@ -114,7 +114,7 @@ CompilerM op s () -- | Create a static array of values - initialised at load time.-type StaticArray op s = VName -> Imp.SpaceId -> PrimType -> [PrimValue] -> CompilerM op s ()+type StaticArray op s = VName -> Imp.SpaceId -> PrimType -> Imp.ArrayContents -> CompilerM op s () -- | Construct the C# array being returned from an entry point. type EntryOutput op s = VName -> Imp.SpaceId ->@@ -381,6 +381,11 @@ , optionAction = [ Reassign (Var "EntryPoint") $ Var "optarg" ] }+ , Option { optionLongName = "tuning"+ , optionShortName = Nothing+ , optionArgument = RequiredArgument+ , optionAction = [Escape "FutharkConfigLoadTuning(ref Cfg, optarg);"]+ } ] -- | The class generated by the code generator must have a@@ -644,7 +649,7 @@ entryPointOutput (Imp.TransparentValue (Imp.ArrayValue mem _ Imp.DefaultSpace bt ept dims)) = do let src = Var $ compileName mem let createTuple = "createTuple_" ++ compilePrimTypeExt bt ept- return $ simpleCall createTuple [src, CreateArray (Primitive $ CSInt Int64T) $ map compileDim dims]+ return $ simpleCall createTuple [src, CreateArray (Primitive $ CSInt Int64T) $ Right $ map compileDim dims] entryPointOutput (Imp.TransparentValue (Imp.ArrayValue mem _ (Imp.Space sid) bt ept dims)) = do unRefMem mem (Imp.Space sid)@@ -804,7 +809,7 @@ printStm (Imp.ArrayValue mem memsize space bt ept (outer:shape)) ind e = do ptr <- newVName "shapePtr" first <- newVName "printFirst"- let size = callMethod (CreateArray (Primitive $ CSInt Int32T) $ map compileDim $ outer:shape)+ let size = callMethod (CreateArray (Primitive $ CSInt Int32T) $ Right $ map compileDim $ outer:shape) "Aggregate" [ Integer 1 , Lambda (Tuple [Var "acc", Var "val"]) [Exp $ BinOp "*" (Var "acc") (Var "val")]@@ -1257,13 +1262,15 @@ stms [Assign (Var $ "init_"++name') $ simpleCall "unwrapArray" [- CreateArray (compilePrimTypeToAST t) (map compilePrimValue vs)+ case vs of Imp.ArrayValues vs' ->+ CreateArray (compilePrimTypeToAST t) $ Right $ map compilePrimValue vs'+ Imp.ArrayZeros n ->+ CreateArray (compilePrimTypeToAST t) $ Left n , simpleCall "sizeof" [Var $ compilePrimType t] ] , Assign (Var name') $ Var ("init_"++name') ] where name' = compileName name- compileCode (Imp.DeclareArray name (Space space) t vs) = join $ asks envStaticArray <*>
src/Futhark/CodeGen/Backends/GenericCSharp/AST.hs view
@@ -143,7 +143,7 @@ | Call CSExp [CSArg] | CallMethod CSExp CSExp [CSArg] | CreateObject CSExp [CSArg]- | CreateArray CSType [CSExp]+ | CreateArray CSType (Either Int [CSExp]) | CreateSystemTuple [CSExp] | AllocArray CSType CSExp | Cast CSType CSExp@@ -182,7 +182,8 @@ ppr (Call fun args) = ppr fun <> parens(commasep $ map ppr args) ppr (CallMethod obj method args) = ppr obj <> dot <> ppr method <> parens(commasep $ map ppr args) ppr (CreateObject className args) = text "new" <+> ppr className <> parens(commasep $ map ppr args)- ppr (CreateArray t vs) = text "new" <+> ppr t <> text "[]" <+> braces(commasep $ map ppr vs)+ ppr (CreateArray t (Left n)) = text "new" <+> ppr t <> brackets (ppr n)+ ppr (CreateArray t (Right vs)) = text "new" <+> ppr t <> text "[]" <+> braces(commasep $ map ppr vs) ppr (CreateSystemTuple exps) = text "Tuple.Create" <> parens(commasep $ map ppr exps) ppr (Tuple exps) = parens(commasep $ map ppr exps) ppr (Array exps) = braces(commasep $ map ppr exps) -- uhoh is this right?
src/Futhark/CodeGen/Backends/GenericPython.hs view
@@ -91,7 +91,7 @@ CompilerM op s () -- | Create a static array of values - initialised at load time.-type StaticArray op s = VName -> Imp.SpaceId -> PrimType -> [PrimValue] -> CompilerM op s ()+type StaticArray op s = VName -> Imp.SpaceId -> PrimType -> Imp.ArrayContents -> CompilerM op s () -- | Construct the Python array being returned from an entry point. type EntryOutput op s = VName -> Imp.SpaceId ->@@ -249,7 +249,7 @@ standardOptions = [ Option { optionLongName = "write-runtime-to" , optionShortName = Just 't'- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "str" , optionAction = [ If (Var "runtime_file")@@ -260,7 +260,7 @@ }, Option { optionLongName = "runs" , optionShortName = Just 'r'- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "str" , optionAction = [ Assign (Var "num_runs") $ Var "optarg" , Assign (Var "do_warmup_run") $ Bool True@@ -268,7 +268,7 @@ }, Option { optionLongName = "entry-point" , optionShortName = Just 'e'- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "str" , optionAction = [ Assign (Var "entry_point") $ Var "optarg" ] },@@ -277,6 +277,11 @@ , optionShortName = Just 'b' , optionArgument = NoArgument , optionAction = [Pass]+ },+ Option { optionLongName = "tuning"+ , optionShortName = Nothing+ , optionArgument = RequiredArgument "open"+ , optionAction = [Exp $ simpleCall "read_tuning_file" [Var "sizes", Var "optarg"]] } ] @@ -312,7 +317,9 @@ Just _ -> "" return $ maybe_shebang ++ pretty (PyProg $ imports ++- [Import "argparse" Nothing] +++ [ Import "argparse" Nothing+ , Assign (Var "sizes") $ Dict []+ ] ++ defines ++ [Escape pyUtility] ++ prog')@@ -879,9 +886,15 @@ -- to prevent it from going "out-of-scope" before calling -- unwrapArray (which internally uses the .ctype method); see -- https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.ctypes.html- atInit $ Assign (Field (Var "self") arr_name) $ Call (Var "np.array")- [Arg $ List $ map compilePrimValue vs,- ArgKeyword "dtype" $ Var $ compilePrimToNp t]+ atInit $ Assign (Field (Var "self") arr_name) $ case vs of+ Imp.ArrayValues vs' ->+ Call (Var "np.array")+ [Arg $ List $ map compilePrimValue vs',+ ArgKeyword "dtype" $ Var $ compilePrimToNp t]+ Imp.ArrayZeros n ->+ Call (Var "np.zeros")+ [Arg $ Integer $ fromIntegral n,+ ArgKeyword "dtype" $ Var $ compilePrimToNp t] atInit $ Assign (Field (Var "self") name') $ simpleCall "unwrapArray" [Field (Var "self") arr_name]
src/Futhark/CodeGen/Backends/GenericPython/Definitions.hs view
@@ -4,6 +4,7 @@ , pyUtility , pyValues , pyPanic+ , pyTuning ) where import Data.FileEmbed@@ -19,3 +20,6 @@ pyPanic :: String pyPanic = $(embedStringFile "rts/python/panic.py")++pyTuning :: String+pyTuning = $(embedStringFile "rts/python/tuning.py")
src/Futhark/CodeGen/Backends/GenericPython/Options.hs view
@@ -24,7 +24,7 @@ -- | Whether an option accepts an argument. data OptionArgument = NoArgument- | RequiredArgument+ | RequiredArgument String | OptionalArgument -- | Generate option parsing code that accepts the given command line options. Will read from @sys.argv@.@@ -43,13 +43,15 @@ map executeOption options where parseOption option = Exp $ Call (Var "parser.add_argument") $- map (Arg . String) name_args ++ argument_args+ map (Arg . String) name_args +++ argument_args where name_args = maybe id ((:) . ('-':) . (:[])) (optionShortName option) ["--" ++ optionLongName option] argument_args = case optionArgument option of- RequiredArgument ->+ RequiredArgument t -> [ArgKeyword "action" (String "append"),- ArgKeyword "default" $ List []]+ ArgKeyword "default" $ List [],+ ArgKeyword "type" $ Var t] NoArgument -> [ArgKeyword "action" (String "append_const"),
src/Futhark/CodeGen/Backends/PyOpenCL.hs view
@@ -34,10 +34,15 @@ [Assign (Var "synchronous") $ Bool False, Assign (Var "preferred_platform") None, Assign (Var "preferred_device") None,+ Assign (Var "default_threshold") None,+ Assign (Var "default_group_size") None,+ Assign (Var "default_num_groups") None,+ Assign (Var "default_tile_size") None, Assign (Var "fut_opencl_src") $ RawStringLiteral $ opencl_prelude ++ opencl_code, Escape pyValues, Escape pyFunctions,- Escape pyPanic]+ Escape pyPanic,+ Escape pyTuning] let imports = [Import "sys" Nothing, Import "numpy" $ Just "np", Import "ctypes" $ Just "ct",@@ -50,23 +55,59 @@ , "interactive=False" , "platform_pref=preferred_platform" , "device_pref=preferred_device"- , "default_group_size=None"- , "default_num_groups=None"- , "default_tile_size=None"- , "sizes={}"]+ , "default_group_size=default_group_size"+ , "default_num_groups=default_num_groups"+ , "default_tile_size=default_tile_size"+ , "default_threshold=default_threshold"+ , "sizes=sizes"] [Escape $ openClInit types assign sizes] options = [ Option { optionLongName = "platform" , optionShortName = Just 'p'- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "str" , optionAction = [ Assign (Var "preferred_platform") $ Var "optarg" ] } , Option { optionLongName = "device" , optionShortName = Just 'd'- , optionArgument = RequiredArgument+ , optionArgument = RequiredArgument "str" , optionAction = [ Assign (Var "preferred_device") $ Var "optarg" ]- }]+ }+ , Option { optionLongName = "default-threshold"+ , optionShortName = Nothing+ , optionArgument = RequiredArgument "int"+ , optionAction =+ [ Assign (Var "default_threshold") $ Var "optarg" ]+ }+ , Option { optionLongName = "default-group-size"+ , optionShortName = Nothing+ , optionArgument = RequiredArgument "int"+ , optionAction =+ [ Assign (Var "default_group_size") $ Var "optarg" ]+ }+ , Option { optionLongName = "default-num-groups"+ , optionShortName = Nothing+ , optionArgument = RequiredArgument "int"+ , optionAction =+ [ Assign (Var "default_num_groups") $ Var "optarg" ]+ }+ , Option { optionLongName = "default-tile-size"+ , optionShortName = Nothing+ , optionArgument = RequiredArgument "int"+ , optionAction =+ [ Assign (Var "default_tile_size") $ Var "optarg" ]+ }+ , Option { optionLongName = "size"+ , optionShortName = Nothing+ , optionArgument = RequiredArgument "size_assignment"+ , optionAction =+ [Assign (Index (Var "sizes")+ (IdxExp (Index (Var "optarg")+ (IdxExp (Integer 0)))))+ (Index (Var "optarg") (IdxExp (Integer 1)))+ ]+ }+ ] Right <$> Py.compileProg module_name constructor imports defines operations () [Exp $ Py.simpleCall "self.queue.finish" []] options prog'@@ -216,14 +257,22 @@ staticOpenCLArray name "device" t vs = do mapM_ Py.atInit <=< Py.collect $ do -- Create host-side Numpy array with intended values.- Py.stm $ Assign (Var name') $- Call (Var "np.array")- [Arg $ List $ map Py.compilePrimValue vs,- ArgKeyword "dtype" $ Var $ Py.compilePrimToNp t]+ Py.stm $ Assign (Var name') $ case vs of+ Imp.ArrayValues vs' ->+ Call (Var "np.array")+ [Arg $ List $ map Py.compilePrimValue vs',+ ArgKeyword "dtype" $ Var $ Py.compilePrimToNp t]+ Imp.ArrayZeros n ->+ Call (Var "np.zeros")+ [Arg $ Integer $ fromIntegral n,+ ArgKeyword "dtype" $ Var $ Py.compilePrimToNp t] + let num_elems = case vs of Imp.ArrayValues vs' -> length vs'+ Imp.ArrayZeros n -> n+ -- Create memory block on the device. static_mem <- newVName "static_mem"- let size = Integer $ fromIntegral (length vs) * Imp.primByteSize t+ let size = Integer $ toInteger num_elems * Imp.primByteSize t allocateOpenCLBuffer static_mem size "device" -- Copy Numpy array to the device memory block.
src/Futhark/CodeGen/Backends/PyOpenCL/Boilerplate.hs view
@@ -35,6 +35,7 @@ default_group_size=default_group_size, default_num_groups=default_num_groups, default_tile_size=default_tile_size,+ default_threshold=default_threshold, size_heuristics=size_heuristics, required_types=$types', user_sizes=sizes,@@ -68,6 +69,7 @@ NumGroups -> String "num_groups" GroupSize -> String "group_size" TileSize -> String "tile_size"+ Threshold -> String "threshold" what' = case what of HeuristicConst x -> Integer $ toInteger x
src/Futhark/CodeGen/Backends/SequentialPython.hs view
@@ -27,7 +27,7 @@ Import "numpy" $ Just "np", Import "ctypes" $ Just "ct", Import "time" Nothing]- defines = [Escape pyValues, Escape pyFunctions, Escape pyPanic]+ defines = [Escape pyValues, Escape pyFunctions, Escape pyPanic, Escape pyTuning] operations :: GenericPython.Operations Imp.Sequential () operations = GenericPython.defaultOperations { GenericPython.opsCompiler = const $ return ()
src/Futhark/CodeGen/ImpCode.hs view
@@ -34,6 +34,7 @@ , index , ErrorMsg(..) , ErrorMsgPart(..)+ , ArrayContents(..) -- * Typed enumerations , Count (..)@@ -137,13 +138,22 @@ -- | Type alias for namespace control. type Function = FunctionT +-- | The contents of a statically declared constant array. Such+-- arrays are always unidimensional, and reshaped if necessary in the+-- code that uses them.+data ArrayContents = ArrayValues [PrimValue]+ -- ^ Precisely these values.+ | ArrayZeros Int+ -- ^ This many zeroes.+ deriving (Show)+ data Code a = Skip | Code a :>>: Code a | For VName IntType Exp (Code a) | While Exp (Code a) | DeclareMem VName Space | DeclareScalar VName PrimType- | DeclareArray VName Space PrimType [PrimValue]+ | DeclareArray VName Space PrimType ArrayContents -- ^ Create an array containing the given values. The -- lifetime of the array will be the entire application. -- This is mostly used for constant arrays, but also for@@ -295,6 +305,10 @@ ppr (ConstSize x) = ppr x ppr (VarSize v) = ppr v +instance Pretty ArrayContents where+ ppr (ArrayValues vs) = braces (commasep $ map ppr vs)+ ppr (ArrayZeros n) = braces (text "0") <+> text "*" <+> ppr n+ instance Pretty op => Pretty (Code op) where ppr (Op op) = ppr op ppr Skip = text "skip"@@ -313,7 +327,7 @@ text "var" <+> ppr name <> text ":" <+> ppr t ppr (DeclareArray name space t vs) = text "array" <+> ppr name <> text "@" <> ppr space <+> text ":" <+> ppr t <+>- equals <+> braces (commasep $ map ppr vs)+ equals <+> ppr vs ppr (Allocate name e space) = ppr name <+> text "<-" <+> text "malloc" <> parens (ppr e) <> ppr space ppr (Free name space) =
src/Futhark/CodeGen/ImpCode/Kernels.hs view
@@ -103,11 +103,11 @@ instance Pretty KernelUse where ppr (ScalarUse name t) =- text "scalar_copy" <> parens (commasep [ppr name, ppr t])+ oneLine $ text "scalar_copy" <> parens (commasep [ppr name, ppr t]) ppr (MemoryUse name) =- text "mem_copy" <> parens (commasep [ppr name])+ oneLine $ text "mem_copy" <> parens (commasep [ppr name]) ppr (ConstUse name e) =- text "const" <> parens (commasep [ppr name, ppr e])+ oneLine $ text "const" <> parens (commasep [ppr name, ppr e]) instance Pretty HostOp where ppr (GetSize dest key size_class) =
src/Futhark/CodeGen/ImpGen.hs view
@@ -78,9 +78,9 @@ , sComment , sIf, sWhen, sUnless , sOp- , sAlloc+ , sDeclareMem, sAlloc, sAlloc_ , sArray, sAllocArray, sStaticArray- , sWrite+ , sWrite, sUpdate , (<--) ) where@@ -675,11 +675,8 @@ defCompileBasicOp _ Index{} = return () -defCompileBasicOp (Pattern _ [pe]) (Update _ slice se) = do- MemLocation mem shape ixfun <- entryArrayLocation <$> lookupArray (patElemName pe)- let memdest = sliceArray (MemLocation mem shape ixfun) $- map (fmap (compileSubExpOfType int32)) slice- copyDWIMDest (ArrayDestination $ Just memdest) [] se []+defCompileBasicOp (Pattern _ [pe]) (Update _ slice se) =+ sUpdate (patElemName pe) (map (fmap (compileSubExpOfType int32)) slice) se defCompileBasicOp (Pattern _ [pe]) (Replicate (Shape ds) se) = do ds' <- mapM compileSubExp ds@@ -736,7 +733,7 @@ dest_space <- entryMemSpace <$> lookupMemory (memLocationName dest_mem) let t = primValueType v static_array <- newVName "static_array"- emit $ Imp.DeclareArray static_array dest_space t vs+ emit $ Imp.DeclareArray static_array dest_space t $ Imp.ArrayValues vs let static_src = MemLocation static_array [Imp.ConstSize $ fromIntegral $ length es] $ IxFun.iota [fromIntegral $ length es] num_bytes = Imp.ConstSize $ fromIntegral (length es) * primByteSize t@@ -1237,19 +1234,33 @@ sOp :: op -> ImpM lore op () sOp = emit . Imp.Op -sAlloc :: String -> Count Bytes -> Space -> ImpM lore op VName-sAlloc name size space = do+dSize :: Imp.Count u -> ImpM lore op Imp.Size+dSize size =+ case Imp.innerExp size of+ Imp.LeafExp (Imp.ScalarVar size') _ -> return $ Imp.VarSize size'+ Imp.ValueExp (IntValue (Int64Value v)) -> return $ Imp.ConstSize v+ _ -> do size_var <- dPrim "local_buf_size" int32+ size_var <-- Imp.innerExp size+ return $ Imp.VarSize size_var++sDeclareMem :: String -> Count Bytes -> Space -> ImpM lore op (VName, Imp.MemSize)+sDeclareMem name size space = do+ name' <- newVName name- size' <- case Imp.innerExp size of- Imp.LeafExp (Imp.ScalarVar size') _ -> return $ Imp.VarSize size'- Imp.ValueExp (IntValue (Int64Value v)) -> return $ Imp.ConstSize v- _ -> do size_var <- dPrim "local_buf_size" int32- size_var <-- Imp.innerExp size- return $ Imp.VarSize size_var+ size' <- dSize size emit $ Imp.DeclareMem name' space- fake <- asks $ elem space . envFakeMemory- unless fake $ emit $ Imp.Allocate name' size space addVar name' $ MemVar Nothing $ MemEntry size' space+ return (name', size')++sAlloc_ :: VName -> Imp.MemSize -> Space -> ImpM lore op ()+sAlloc_ name' size' space = do+ fake <- asks $ elem space . envFakeMemory+ unless fake $ emit $ Imp.Allocate name' (Imp.memSizeToExp size') space++sAlloc :: String -> Count Bytes -> Space -> ImpM lore op VName+sAlloc name size space = do+ (name', size') <- sDeclareMem name size space+ sAlloc_ name' size' space return name' sArray :: String -> PrimType -> ShapeBase SubExp -> MemBind -> ImpM lore op VName@@ -1268,21 +1279,28 @@ ArrayIn mem $ IxFun.iota $ map (primExpFromSubExp int32) $ shapeDims shape -- | Uses linear/iota index function.-sStaticArray :: String -> Space -> PrimType -> [PrimValue] -> ImpM lore op VName+sStaticArray :: String -> Space -> PrimType -> Imp.ArrayContents -> ImpM lore op VName sStaticArray name space pt vs = do- let shape = Shape [constant $ length vs]- size = Imp.ConstSize $ fromIntegral (length vs) * primByteSize pt+ let num_elems = case vs of Imp.ArrayValues vs' -> genericLength vs'+ Imp.ArrayZeros n -> fromIntegral n+ shape = Shape [constant num_elems]+ mem_size = Imp.ConstSize $ num_elems * primByteSize pt mem <- newVName $ name ++ "_mem" emit $ Imp.DeclareArray mem space pt vs- addVar mem $ MemVar Nothing $ MemEntry size space- sArray name pt shape $- ArrayIn mem $ IxFun.iota $ map (primExpFromSubExp int32) $ shapeDims shape+ addVar mem $ MemVar Nothing $ MemEntry mem_size space+ sArray name pt shape $ ArrayIn mem $ IxFun.iota [fromIntegral num_elems] sWrite :: VName -> [Imp.Exp] -> PrimExp Imp.ExpLeaf -> ImpM lore op () sWrite arr is v = do (mem, space, offset) <- fullyIndexArray arr is vol <- asks envVolatility emit $ Imp.Write mem offset (primExpType v) space vol v++sUpdate :: VName -> Slice Imp.Exp -> SubExp -> ImpM lore op ()+sUpdate arr slice v = do+ MemLocation mem shape ixfun <- entryArrayLocation <$> lookupArray arr+ let memdest = sliceArray (MemLocation mem shape ixfun) slice+ copyDWIMDest (ArrayDestination $ Just memdest) [] v [] -- | ASsignment. (<--) :: VName -> Imp.Exp -> ImpM lore op ()
src/Futhark/CodeGen/ImpGen/Kernels.hs view
@@ -23,9 +23,9 @@ import qualified Futhark.CodeGen.ImpGen as ImpGen import Futhark.CodeGen.ImpGen.Kernels.Base import Futhark.CodeGen.ImpGen.Kernels.SegRed+import Futhark.CodeGen.ImpGen.Kernels.SegGenRed import Futhark.CodeGen.ImpGen (sFor, sWhen,- sOp,- dPrim, dPrim_, dPrimV)+ sOp) import Futhark.CodeGen.ImpGen.Kernels.Transpose import qualified Futhark.Representation.ExplicitMemory.IndexFunction as IxFun import Futhark.CodeGen.SetDefaultSpace@@ -108,14 +108,12 @@ show (baseTag $ kernelGlobalThreadIdVar constants) } --- First handle the simple case of a non-segmented reduction. Our--- strategy is the conventional approach of generating two kernels:--- one where each group is given a chunk of the total input and--- produces a partial result per group, and then a final kernel that--- combines the per-group partial results. kernelCompiler pat (SegRed space comm red_op nes _ body) = compileSegRed pat space comm red_op nes body +kernelCompiler pat (SegGenRed space ops _ body) =+ compileSegGenRed pat space ops body+ kernelCompiler pat e = compilerBugS $ "ImpGen.kernelCompiler: Invalid pattern\n " ++ pretty pat ++ "\nfor expression\n " ++ pretty e@@ -127,33 +125,11 @@ n' <- ImpGen.compileSubExp n x' <- ImpGen.compileSubExp x s' <- ImpGen.compileSubExp s- destloc <- ImpGen.entryArrayLocation <$> ImpGen.lookupArray (patElemName pe)- (constants, set_constants) <- simpleKernelConstants n' "iota" - sKernel constants "iota" $ do- set_constants- let gtid = kernelGlobalThreadId constants- sWhen (kernelThreadActive constants) $ do- (destmem, destspace, destidx) <-- ImpGen.fullyIndexArray' destloc [gtid] (IntType et)-- ImpGen.emit $- Imp.Write destmem destidx (IntType et) destspace Imp.Nonvolatile $- Imp.ConvOpExp (SExt Int32 et) gtid * s' + x'--expCompiler (Pattern _ [pe]) (BasicOp (Replicate (Shape ds) se)) = do- t <- subExpType se-- dims <- mapM ImpGen.compileSubExp $ ds ++ arrayDims t- (constants, set_constants) <-- simpleKernelConstants (product dims) "replicate"-- let is' = unflattenIndex dims $ kernelGlobalThreadId constants+ sIota (patElemName pe) n' x' s' et - sKernel constants "replicate" $ do- set_constants- sWhen (kernelThreadActive constants) $- ImpGen.copyDWIM (patElemName pe) is' se $ drop (length ds) is'+expCompiler (Pattern _ [pe]) (BasicOp (Replicate shape se)) =+ sReplicate (patElemName pe) shape se -- Allocation in the "local" space is just a placeholder. expCompiler _ (Op (Alloc _ (Space "local"))) =@@ -195,29 +171,7 @@ srcmem (bytes srcoffset) srcspace $ (n * row_size) `Imp.withElemType` bt - | otherwise = do-- -- Note that the shape of the destination and the source are- -- necessarily the same.- let shape = map Imp.sizeToExp srcshape- shape_se = map (Imp.innerExp . ImpGen.dimSizeToExp) srcshape- kernel_size = Imp.innerExp n * product (drop 1 shape)-- (constants, set_constants) <- simpleKernelConstants kernel_size "copy"-- sKernel constants "copy" $ do- set_constants-- let gtid = kernelGlobalThreadId constants- dest_is = unflattenIndex shape_se gtid- src_is = dest_is-- (_, destspace, destidx) <- ImpGen.fullyIndexArray' destloc dest_is bt- (_, srcspace, srcidx) <- ImpGen.fullyIndexArray' srcloc src_is bt-- sWhen (gtid .<. kernel_size) $ ImpGen.emit $- Imp.Write destmem destidx bt destspace Imp.Nonvolatile $- Imp.index srcmem srcidx bt srcspace Imp.Nonvolatile+ | otherwise = sCopy bt destloc srcloc n mapTransposeForType :: PrimType -> ImpGen.ImpM ExplicitMemory Imp.HostOp Name mapTransposeForType bt = do@@ -372,39 +326,6 @@ let (mapped, notmapped) = splitAt r1 shape (pretrans, posttrans) = f $ splitAt r2 notmapped in (product mapped, product pretrans, product posttrans)--simpleKernelConstants :: Imp.Exp -> String- -> CallKernelGen (KernelConstants, ImpGen.ImpM InKernel Imp.KernelOp ())-simpleKernelConstants kernel_size desc = do- thread_gtid <- newVName $ desc ++ "_gtid"- thread_ltid <- newVName $ desc ++ "_ltid"- group_id <- newVName $ desc ++ "_gid"- (group_size, num_groups) <- computeMapKernelGroups kernel_size- let set_constants = do- dPrim_ thread_gtid int32- dPrim_ thread_ltid int32- dPrim_ group_id int32- sOp (Imp.GetGlobalId thread_gtid 0)- sOp (Imp.GetLocalId thread_ltid 0)- sOp (Imp.GetGroupId group_id 0)-- return (KernelConstants- (Imp.var thread_gtid int32) (Imp.var thread_ltid int32) (Imp.var group_id int32)- thread_gtid thread_ltid group_id- group_size num_groups (group_size*num_groups) 0- [] (Imp.var thread_gtid int32 .<. kernel_size) mempty,-- set_constants)--computeMapKernelGroups :: Imp.Exp -> CallKernelGen (Imp.Exp, Imp.Exp)-computeMapKernelGroups kernel_size = do- group_size <- dPrim "group_size" int32- fname <- asks ImpGen.envFunction- let group_size_var = Imp.var group_size int32- group_size_key = keyWithEntryPoint fname $ nameFromString $ pretty group_size- sOp $ Imp.GetSize group_size group_size_key Imp.SizeGroup- num_groups <- dPrimV "num_groups" $ kernel_size `quotRoundingUp` Imp.ConvOpExp (SExt Int32 Int32) group_size_var- return (Imp.var group_size int32, Imp.var num_groups int32) compileKernelBody :: Pattern InKernel -> KernelConstants
src/Futhark/CodeGen/ImpGen/Kernels/Base.hs view
@@ -19,6 +19,14 @@ , groupScan , isActive , sKernel+ , sReplicate+ , sIota+ , sCopy++ , atomicUpdate+ , atomicUpdateLocking+ , Locking(..)+ , AtomicUpdate ) where @@ -30,7 +38,7 @@ import qualified Data.Set as S import Data.List -import Prelude hiding (quot)+import Prelude hiding (quot, rem) import Futhark.Error import Futhark.MonadFreshNames@@ -45,7 +53,7 @@ dPrim, dPrim_, dPrimV) import Futhark.Tools (partitionChunkedKernelLambdaParameters) import Futhark.Util.IntegralExp (quotRoundingUp, quot, rem, IntegralExp)-import Futhark.Util (splitAt3, maybeNth)+import Futhark.Util (splitAt3, maybeNth, takeLast) type CallKernelGen = ImpGen.ImpM ExplicitMemory Imp.HostOp type InKernelGen = ImpGen.ImpM InKernel Imp.KernelOp@@ -271,12 +279,17 @@ -- Check if bucket is in-bounds bucket' <- mapM ImpGen.compileSubExp bucket w' <- mapM ImpGen.compileSubExp w- sWhen (indexInBounds bucket' w') $- atomicUpdate arrs bucket op values locking+ num_locks <- ImpGen.compileSubExpOfType int32 . arraySize 0 <$> lookupType locks+ let locking = Locking locks 0 1 0 $ (`rem` num_locks) . sum+ values_params = takeLast (length values) $ lambdaParams op++ sWhen (indexInBounds bucket' w') $ do+ forM_ (zip values_params values) $ \(p, v) ->+ ImpGen.copyDWIM (paramName p) [] v []+ atomicUpdate arrs bucket' op locking where indexInBounds inds bounds = foldl1 (.&&.) $ zipWith checkBound inds bounds where checkBound ind bound = 0 .<=. ind .&&. ind .<. bound- locking = Locking locks 0 1 0 compileKernelExp _ dest e = compilerBugS $ unlines ["Invalid target", " " ++ show dest,@@ -290,35 +303,57 @@ ImpGen.emit =<< ImpGen.subImpM_ (inKernelOperations constants') m -- | Locking strategy used for an atomic update.-data Locking = Locking { lockingArray :: VName -- ^ Array containing the lock.- , lockingIsUnlocked :: Imp.Exp -- ^ Value for us to consider the lock free.- , lockingToLock :: Imp.Exp -- ^ What to write when we lock it.- , lockingToUnlock :: Imp.Exp -- ^ What to write when we unlock it.- }+data Locking =+ Locking { lockingArray :: VName+ -- ^ Array containing the lock.+ , lockingIsUnlocked :: Imp.Exp+ -- ^ Value for us to consider the lock free.+ , lockingToLock :: Imp.Exp+ -- ^ What to write when we lock it.+ , lockingToUnlock :: Imp.Exp+ -- ^ What to write when we unlock it.+ , lockingMapping :: [Imp.Exp] -> Imp.Exp+ -- ^ A transformation from the logical lock index to the+ -- physical position in the array. This can also be used+ -- to make the lock array smaller.+ } +-- | A function for generating code for an atomic update. Assumes+-- that the bucket is in-bounds.+type AtomicUpdate lore =+ [VName] -> [Imp.Exp] -> ImpGen.ImpM lore Imp.KernelOp ()+ atomicUpdate :: ExplicitMemorish lore =>- [VName] -> [SubExp] -> Lambda lore -> [SubExp] -> Locking+ [VName] -> [Imp.Exp] -> Lambda lore -> Locking -> ImpGen.ImpM lore Imp.KernelOp ()+atomicUpdate arrs bucket lam locking =+ case atomicUpdateLocking lam of+ Left f -> f arrs bucket+ Right f -> f locking arrs bucket -atomicUpdate arrs bucket lam values _+-- | 'atomicUpdate', but where it is explicitly visible whether a+-- locking strategy is necessary.+atomicUpdateLocking :: ExplicitMemorish lore =>+ Lambda lore+ -> Either (AtomicUpdate lore) (Locking -> AtomicUpdate lore)++atomicUpdateLocking lam | Just ops_and_ts <- splitOp lam,- all ((==32) . primBitSize . snd) ops_and_ts =+ all (\(_, t, _) -> primBitSize t == 32) ops_and_ts = Left $ \arrs bucket -> -- If the operator is a vectorised binary operator on 32-bit values, -- we can use a particularly efficient implementation. If the -- operator has an atomic implementation we use that, otherwise it -- is still a binary operator which can be implemented by atomic -- compare-and-swap if 32 bits.- forM_ (zip3 arrs ops_and_ts values) $ \(a, (op, t), val) -> do+ forM_ (zip arrs ops_and_ts) $ \(a, (op, t, val)) -> do -- Common variables. old <- dPrim "old" t- bucket' <- mapM ImpGen.compileSubExp bucket - (arr', _a_space, bucket_offset) <- ImpGen.fullyIndexArray a bucket'+ (arr', _a_space, bucket_offset) <- ImpGen.fullyIndexArray a bucket - val' <- ImpGen.compileSubExp val case opHasAtomicSupport old arr' bucket_offset op of- Just f -> sOp $ f val'+ Just f -> sOp $ f val Nothing -> do -- Code generation target:@@ -331,7 +366,7 @@ -- } while(assumed != old); assumed <- dPrim "assumed" t run_loop <- dPrimV "run_loop" 1- ImpGen.copyDWIM old [] (Var a) bucket'+ ImpGen.copyDWIM old [] (Var a) bucket -- Critical section x <- dPrim "x" t@@ -344,7 +379,7 @@ _ -> (id, id) sWhile (Imp.var run_loop int32) $ do assumed <-- Imp.var old t- x <-- val'+ x <-- val y <-- Imp.var assumed t x <-- Imp.BinOpExp op (Imp.var x t) (Imp.var y t) old_bits <- dPrim "old_bits" int32@@ -359,69 +394,69 @@ let atomic f = Imp.Atomic . f old arr' bucket' atomic <$> Imp.atomicBinOp bop -atomicUpdate arrs bucket op values locking = do+atomicUpdateLocking op = Right $ \locking arrs bucket -> do old <- dPrim "old" int32 continue <- dPrimV "continue" true - -- Check if bucket is in-bounds- bucket' <- mapM ImpGen.compileSubExp bucket- -- Correctly index into locks. (locks', _locks_space, locks_offset) <-- ImpGen.fullyIndexArray (lockingArray locking) bucket'-- -- Preparing parameters- let (acc_params, arr_params) =- splitAt (length values) $ lambdaParams op+ ImpGen.fullyIndexArray (lockingArray locking) [lockingMapping locking bucket] -- Critical section let try_acquire_lock = sOp $ Imp.Atomic $ Imp.AtomicCmpXchg old locks' locks_offset (lockingIsUnlocked locking) (lockingToLock locking) lock_acquired = Imp.var old int32 .==. lockingIsUnlocked locking- release_lock = ImpGen.everythingVolatile $- ImpGen.sWrite (lockingArray locking) bucket' $ lockingToUnlock locking+ -- Even the releasing is done with an atomic rather than a+ -- simple write, for memory coherency reasons.+ release_lock =+ sOp $ Imp.Atomic $+ Imp.AtomicCmpXchg old locks' locks_offset (lockingToLock locking) (lockingToUnlock locking) break_loop = continue <-- false - -- We copy the current value and the new value to the parameters.- -- It is important that the right-hand-side is bound first for the- -- (rare) case when we are dealing with arrays.- let bind_acc_params =+ -- Preparing parameters. It is assumed that the caller has already+ -- filled the arr_params. We copy the current value to the+ -- accumulator parameters.+ --+ -- Note the use of 'everythingVolatile' when reading and writing the+ -- buckets. This was necessary to ensure correct execution on a+ -- newer NVIDIA GPU (RTX 2080). The 'volatile' modifiers likely+ -- make the writes pass through the (SM-local) L1 cache, which is+ -- necessary here, because we are really doing device-wide+ -- synchronisation without atomics (naughty!).+ let (acc_params, _arr_params) = splitAt (length arrs) $ lambdaParams op+ bind_acc_params =+ ImpGen.everythingVolatile $ ImpGen.sComment "bind lhs" $ forM_ (zip acc_params arrs) $ \(acc_p, arr) ->- ImpGen.copyDWIM (paramName acc_p) [] (Var arr) bucket'-- let bind_arr_params =- ImpGen.sComment "bind rhs" $- forM_ (zip arr_params values) $ \(arr_p, val) ->- ImpGen.copyDWIM (paramName arr_p) [] val []+ ImpGen.copyDWIM (paramName acc_p) [] (Var arr) bucket let op_body = ImpGen.sComment "execute operation" $ ImpGen.compileBody' acc_params $ lambdaBody op - do_gen_reduce = ImpGen.sComment "update global result" $- zipWithM_ (writeArray bucket') arrs $ map (Var . paramName) acc_params+ do_gen_reduce =+ ImpGen.everythingVolatile $+ ImpGen.sComment "update global result" $+ zipWithM_ (writeArray bucket) arrs $ map (Var . paramName) acc_params -- While-loop: Try to insert your value sWhile (Imp.var continue Bool) $ do try_acquire_lock sWhen lock_acquired $ do- ImpGen.dLParams $ lambdaParams op- bind_arr_params+ ImpGen.dLParams acc_params bind_acc_params op_body do_gen_reduce+ sOp Imp.MemFence release_lock break_loop sOp Imp.MemFence- where writeArray bucket' arr val =- ImpGen.copyDWIM arr bucket' val []+ where writeArray bucket arr val = ImpGen.copyDWIM arr bucket val [] -- | Horizontally fission a lambda that models a binary operator.-splitOp :: Attributes lore => Lambda lore -> Maybe [(BinOp, PrimType)]+splitOp :: Attributes lore => Lambda lore -> Maybe [(BinOp, PrimType, Imp.Exp)] splitOp lam = mapM splitStm $ bodyResult $ lambdaBody lam where n = length $ lambdaReturnType lam- splitStm :: SubExp -> Maybe (BinOp, PrimType) splitStm (Var res) = do Let (Pattern [] [pe]) _ (BasicOp (BinOp op (Var x) (Var y))) <- find (([res]==) . patternNames . stmPattern) $@@ -432,7 +467,7 @@ guard $ paramName xp == x guard $ paramName yp == y Prim t <- Just $ patElemType pe- return (op, t)+ return (op, t, Imp.var (paramName yp) t) splitStm _ = Nothing computeKernelUses :: FreeIn a =>@@ -494,10 +529,10 @@ hasExp (ImpGen.ScalarVar e _) = e hasExp (ImpGen.MemVar e _) = e --- | Only some constant expressions quality as *static* expressions,+-- | Only some constant expressions qualify as *static* expressions, -- which we can use for static memory allocation. This is a bit of a -- hack, as it is primarly motivated by what you can put as the size--- when daring an array in C.+-- when declaring an array in C. isStaticExp :: Imp.KernelConstExp -> Bool isStaticExp LeafExp{} = True isStaticExp ValueExp{} = True@@ -853,6 +888,8 @@ sComment "restore correct values for first block" $ sWhen is_first_block write_final_result + sOp Imp.LocalBarrier+ inBlockScan :: Maybe (Imp.Exp -> Imp.Exp -> Imp.Exp) -> Imp.Exp -> Imp.Exp@@ -944,6 +981,39 @@ collapse l@((g,_):_) = (g, map snd l) +computeMapKernelGroups :: Imp.Exp -> CallKernelGen (Imp.Exp, Imp.Exp)+computeMapKernelGroups kernel_size = do+ group_size <- dPrim "group_size" int32+ fname <- asks ImpGen.envFunction+ let group_size_var = Imp.var group_size int32+ group_size_key = keyWithEntryPoint fname $ nameFromString $ pretty group_size+ sOp $ Imp.GetSize group_size group_size_key Imp.SizeGroup+ num_groups <- dPrimV "num_groups" $ kernel_size `quotRoundingUp` Imp.ConvOpExp (SExt Int32 Int32) group_size_var+ return (Imp.var group_size int32, Imp.var num_groups int32)++simpleKernelConstants :: Imp.Exp -> String+ -> CallKernelGen (KernelConstants, ImpGen.ImpM InKernel Imp.KernelOp ())+simpleKernelConstants kernel_size desc = do+ thread_gtid <- newVName $ desc ++ "_gtid"+ thread_ltid <- newVName $ desc ++ "_ltid"+ group_id <- newVName $ desc ++ "_gid"+ (group_size, num_groups) <- computeMapKernelGroups kernel_size+ let set_constants = do+ dPrim_ thread_gtid int32+ dPrim_ thread_ltid int32+ dPrim_ group_id int32+ sOp (Imp.GetGlobalId thread_gtid 0)+ sOp (Imp.GetLocalId thread_ltid 0)+ sOp (Imp.GetGroupId group_id 0)++ return (KernelConstants+ (Imp.var thread_gtid int32) (Imp.var thread_ltid int32) (Imp.var group_id int32)+ thread_gtid thread_ltid group_id+ group_size num_groups (group_size*num_groups) 0+ [] (Imp.var thread_gtid int32 .<. kernel_size) mempty,++ set_constants)+ sKernel :: KernelConstants -> String -> ImpGen.ImpM InKernel Imp.KernelOp a -> CallKernelGen () sKernel constants name m = do body <- makeAllMemoryGlobal $@@ -958,3 +1028,69 @@ , Imp.kernelName = nameFromString $ name ++ "_" ++ show (baseTag $ kernelGlobalThreadIdVar constants) }++-- | Perform a Replicate with a kernel.+sReplicate :: VName -> Shape -> SubExp+ -> CallKernelGen ()+sReplicate arr (Shape ds) se = do+ t <- subExpType se++ dims <- mapM ImpGen.compileSubExp $ ds ++ arrayDims t+ (constants, set_constants) <-+ simpleKernelConstants (product dims) "replicate"++ let is' = unflattenIndex dims $ kernelGlobalThreadId constants++ sKernel constants "replicate" $ do+ set_constants+ sWhen (kernelThreadActive constants) $+ ImpGen.copyDWIM arr is' se $ drop (length ds) is'++-- | Perform an Iota with a kernel.+sIota :: VName -> Imp.Exp -> Imp.Exp -> Imp.Exp -> IntType+ -> CallKernelGen ()+sIota arr n x s et = do+ destloc <- ImpGen.entryArrayLocation <$> ImpGen.lookupArray arr+ (constants, set_constants) <- simpleKernelConstants n "iota"++ sKernel constants "iota" $ do+ set_constants+ let gtid = kernelGlobalThreadId constants+ sWhen (kernelThreadActive constants) $ do+ (destmem, destspace, destidx) <-+ ImpGen.fullyIndexArray' destloc [gtid] (IntType et)++ ImpGen.emit $+ Imp.Write destmem destidx (IntType et) destspace Imp.Nonvolatile $+ Imp.ConvOpExp (SExt Int32 et) gtid * s + x++sCopy :: PrimType+ -> ImpGen.MemLocation+ -> ImpGen.MemLocation+ -> Imp.Count Imp.Elements+ -> CallKernelGen ()+sCopy bt+ destloc@(ImpGen.MemLocation destmem _ _)+ srcloc@(ImpGen.MemLocation srcmem srcshape _)+ n = do+ -- Note that the shape of the destination and the source are+ -- necessarily the same.+ let shape = map Imp.sizeToExp srcshape+ shape_se = map (Imp.innerExp . ImpGen.dimSizeToExp) srcshape+ kernel_size = Imp.innerExp n * product (drop 1 shape)++ (constants, set_constants) <- simpleKernelConstants kernel_size "copy"++ sKernel constants "copy" $ do+ set_constants++ let gtid = kernelGlobalThreadId constants+ dest_is = unflattenIndex shape_se gtid+ src_is = dest_is++ (_, destspace, destidx) <- ImpGen.fullyIndexArray' destloc dest_is bt+ (_, srcspace, srcidx) <- ImpGen.fullyIndexArray' srcloc src_is bt++ sWhen (gtid .<. kernel_size) $ ImpGen.emit $+ Imp.Write destmem destidx bt destspace Imp.Nonvolatile $+ Imp.index srcmem srcidx bt srcspace Imp.Nonvolatile
+ src/Futhark/CodeGen/ImpGen/Kernels/SegGenRed.hs view
@@ -0,0 +1,275 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleContexts #-}+-- | Our compilation strategy for 'SegGenRed' is based around avoiding+-- bin conflicts. We do this by splitting the input into chunks, and+-- for each chunk computing a single subhistogram. Then we combine+-- the subhistograms using an ordinary segmented reduction ('SegRed').+--+-- There are some branches around to efficiently handle the case where+-- we use only a single subhistogram (because it's large), so that we+-- respect the asymptotics, and do not copy the destination array.+module Futhark.CodeGen.ImpGen.Kernels.SegGenRed+ ( compileSegGenRed )+ where++import Control.Monad.Except+import Data.Maybe+import Data.List++import Prelude hiding (quot, rem)++import Futhark.MonadFreshNames+import Futhark.Representation.ExplicitMemory+import qualified Futhark.Representation.ExplicitMemory.IndexFunction as IxFun+import Futhark.Pass.ExplicitAllocations()+import qualified Futhark.CodeGen.ImpCode.Kernels as Imp+import qualified Futhark.CodeGen.ImpGen as ImpGen+import Futhark.CodeGen.ImpGen ((<--),+ sFor, sComment, sIf, sWhen, sArray,+ dPrim_, dPrimV)+import Futhark.CodeGen.ImpGen.Kernels.SegRed (compileSegRed')+import Futhark.CodeGen.ImpGen.Kernels.Base+import Futhark.Util.IntegralExp (quotRoundingUp, quot, rem)+import Futhark.Util (chunks, mapAccumLM, splitFromEnd, takeLast)+import Futhark.Construct (fullSliceNum)++prepareAtomicUpdate :: Maybe Locking -> [VName] -> Lambda InKernel+ -> CallKernelGen (Maybe Locking,+ [Imp.Exp] -> ImpGen.ImpM InKernel Imp.KernelOp ())+prepareAtomicUpdate l dests lam =+ -- We need a separate lock array if the opterators are not all of a+ -- particularly simple form that permits pure atomic operations.+ case (l, atomicUpdateLocking lam) of+ (_, Left f) -> return (l, f dests)+ (Just l', Right f) -> return (l, f l' dests)+ (Nothing, Right f) -> do+ -- The number of locks used here is too low, but since we are+ -- currently forced to inline a huge list, I'm keeping it down+ -- for now. Some quick experiments suggested that it has little+ -- impact anyway (maybe the locking case is just too slow).+ --+ -- A fun solution would also be to use a simple hashing+ -- algorithm to ensure good distribution of locks.+ let num_locks = 10000+ locks <-+ ImpGen.sStaticArray "genred_locks" (Space "device") int32 $+ Imp.ArrayZeros num_locks+ let l' = Locking locks 0 1 0 ((`rem` fromIntegral num_locks) . sum)+ return (Just l', f l' dests)++prepareIntermediateArrays :: [SubExp] -> Imp.Exp -> [GenReduceOp InKernel]+ -> CallKernelGen+ [(VName,+ [VName],+ [Imp.Exp] -> ImpGen.ImpM InKernel Imp.KernelOp ())]+prepareIntermediateArrays segment_dims num_threads = fmap snd . mapAccumLM onOp Nothing+ where+ onOp l op = do+ -- Determining the degree of cooperation (heuristic):+ -- coop_lvl := size of histogram (Cooperation level)+ -- num_histos := (threads / coop_lvl) (Number of histograms)+ -- threads := min(physical_threads, segment_size)+ --+ -- Careful to avoid division by zero when genReduceWidth==0.+ num_histos <- dPrimV "num_histos" $ num_threads `quotRoundingUp`+ BinOpExp (SMax Int32) 1 (ImpGen.compileSubExpOfType int32 (genReduceWidth op))++ ImpGen.emit $ Imp.DebugPrint "num_histograms" int32 $ Imp.var num_histos int32++ -- Initialise sub-histograms.+ --+ -- If num_histos is 1, then we just reuse the original+ -- destination. The idea is to avoid a copy if we are writing a+ -- small number of values into a very large prior histogram.++ dests <- forM (zip (genReduceDest op) (genReduceNeutral op)) $ \(dest, ne) -> do+ dest_t <- lookupType dest+ dest_mem <- ImpGen.entryArrayLocation <$> ImpGen.lookupArray dest+ let num_elems = foldl' (*) (Imp.var num_histos int32) $+ map (ImpGen.compileSubExpOfType int32) $+ arrayDims dest_t+ let size = Imp.elements num_elems `Imp.withElemType` int32++ (sub_mem, size') <-+ ImpGen.sDeclareMem "subhistogram_mem" size $ Space "device"++ let num_segments = length segment_dims+ sub_shape = Shape (segment_dims++[Var num_histos]) <>+ stripDims num_segments (arrayShape dest_t)+ sub_membind = ArrayIn sub_mem $ IxFun.iota $+ map (primExpFromSubExp int32) $ shapeDims sub_shape+ subhisto <- sArray "genred_dest" (elemType dest_t) sub_shape sub_membind++ let unitHistoCase =+ ImpGen.emit $+ Imp.SetMem sub_mem (ImpGen.memLocationName dest_mem) $+ Space "device"++ multiHistoCase = do+ ImpGen.sAlloc_ sub_mem size' $ Space "device"+ sReplicate subhisto (Shape $ segment_dims ++ [Var num_histos, genReduceWidth op]) ne+ subhisto_t <- lookupType subhisto+ let slice = fullSliceNum (map (ImpGen.compileSubExpOfType int32) $ arrayDims subhisto_t) $+ map (unitSlice 0 . ImpGen.compileSubExpOfType int32) segment_dims +++ [DimFix 0]+ ImpGen.sUpdate subhisto slice $ Var dest++ sIf (Imp.var num_histos int32 .==. 1) unitHistoCase multiHistoCase++ return subhisto++ (l', do_op) <- prepareAtomicUpdate l dests $ genReduceOp op++ return (l', (num_histos, dests, do_op))++genRedKernel :: [PatElem ExplicitMemory]+ -> KernelSpace+ -> [GenReduceOp InKernel]+ -> Body InKernel+ -> CallKernelGen [(VName, [VName])]+genRedKernel map_pes space ops body = do+ (base_constants, init_constants) <- kernelInitialisationSetSpace space $ return ()+ let constants = base_constants { kernelThreadActive = true }+ (space_is, space_sizes) = unzip $ spaceDimensions space+ i32_to_i64 = ConvOpExp (SExt Int32 Int64)+ space_sizes_64 = map (i32_to_i64 . ImpGen.compileSubExpOfType int32) space_sizes+ total_w_64 = product space_sizes_64++ histograms <- prepareIntermediateArrays (init space_sizes) (kernelNumThreads constants) ops++ elems_per_thread_64 <- dPrimV "elems_per_thread_64" $+ total_w_64 `quotRoundingUp`+ ConvOpExp (SExt Int32 Int64) (kernelNumThreads constants)++ sKernel constants "seggenred" $ allThreads constants $ do+ init_constants++ i <- newVName "i"++ -- Compute subhistogram index for each thread, per histogram.+ subhisto_inds <- forM histograms $ \(num_histograms, _, _) ->+ dPrimV "subhisto_ind" $+ kernelGlobalThreadId constants `quot`+ (kernelNumThreads constants `quotRoundingUp` Imp.var num_histograms int32)++ sFor i Int64 (Imp.var elems_per_thread_64 int64) $ do+ -- Compute the offset into the input and output. To this a+ -- thread can add its local ID to figure out which element it is+ -- responsible for. The calculation is done with 64-bit+ -- integers to avoid overflow, but the final segment indexes are+ -- 32 bit.+ offset <- dPrimV "offset" $+ (i32_to_i64 (kernelGroupId constants) *+ (Imp.var elems_per_thread_64 int64 *+ i32_to_i64 (kernelGroupSize constants)))+ + (Imp.var i int64 * i32_to_i64 (kernelGroupSize constants))++ j <- dPrimV "j" $ Imp.var offset int64 + i32_to_i64 (kernelLocalThreadId constants)++ -- Construct segment indices.+ let setIndex v e = do dPrim_ v int32+ v <-- e+ zipWithM_ setIndex space_is $+ map (ConvOpExp (SExt Int64 Int32)) . unflattenIndex space_sizes_64 $ Imp.var j int64++ -- We execute the bucket function once and update each histogram serially.+ -- We apply the bucket function if j=offset+ltid is less than+ -- num_elements. This also involves writing to the mapout+ -- arrays.+ let input_in_bounds = Imp.var j int32 .<. total_w_64++ sWhen input_in_bounds $ ImpGen.compileStms mempty (stmsToList $ bodyStms body) $ do+ let (red_res, map_res) = splitFromEnd (length map_pes) $ bodyResult body++ sComment "save map-out results" $+ forM_ (zip map_pes map_res) $ \(pe, se) ->+ ImpGen.copyDWIM (patElemName pe)+ (map ((`Imp.var` int32) . fst) $ kernelDimensions constants) se []++ let (buckets, vs) = splitAt (length ops) red_res+ perOp = chunks $ map (length . genReduceDest) ops++ sComment "perform atomic updates" $+ forM_ (zip5 ops histograms buckets (perOp vs) subhisto_inds) $+ \(GenReduceOp dest_w _ _ shape lam,+ (_, _, do_op), bucket, vs', subhisto_ind) -> do++ let bucket' = ImpGen.compileSubExpOfType int32 bucket+ dest_w' = ImpGen.compileSubExpOfType int32 dest_w+ bucket_in_bounds = 0 .<=. bucket' .&&. bucket' .<. dest_w'+ bucket_is = map (`Imp.var` int32) (init space_is) +++ [Imp.var subhisto_ind int32, bucket']+ vs_params = takeLast (length vs') $ lambdaParams lam++ sWhen bucket_in_bounds $ do+ ImpGen.dLParams vs_params+ vectorLoops [] (shapeDims shape) $ \is -> do+ forM_ (zip vs_params vs') $ \(p, v) ->+ ImpGen.copyDWIM (paramName p) [] v is+ do_op (bucket_is ++ is)++ let histogramInfo (num_histos, dests, _) = (num_histos, dests)+ return $ map histogramInfo histograms++vectorLoops :: [Imp.Exp] -> [SubExp]+ -> ([Imp.Exp] -> ImpGen.ImpM lore op ())+ -> ImpGen.ImpM lore op ()+vectorLoops is [] f = f $ reverse is+vectorLoops is (d:ds) f = do+ i <- newVName "i"+ d' <- ImpGen.compileSubExp d+ ImpGen.sFor i Int32 d' $ vectorLoops (Imp.var i int32:is) ds f++compileSegGenRed :: Pattern ExplicitMemory+ -> KernelSpace+ -> [GenReduceOp InKernel]+ -> Body InKernel+ -> CallKernelGen ()+compileSegGenRed (Pattern _ pes) genred_space ops body = do+ let num_red_res = length ops + sum (map (length . genReduceNeutral) ops)+ (all_red_pes, map_pes) = splitAt num_red_res pes++ infos <- genRedKernel map_pes genred_space ops body+ let pes_per_op = chunks (map (length . genReduceDest) ops) all_red_pes++ forM_ (zip3 infos pes_per_op ops) $ \((num_histos, subhistos), red_pes, op) -> do+ let unitHistoCase =+ -- This is OK because the memory blocks are at least as+ -- large as the ones we are supposed to use for the result.+ forM_ (zip red_pes subhistos) $ \(pe, subhisto) -> do+ pe_mem <- ImpGen.memLocationName . ImpGen.entryArrayLocation <$>+ ImpGen.lookupArray (patElemName pe)+ subhisto_mem <- ImpGen.memLocationName . ImpGen.entryArrayLocation <$>+ ImpGen.lookupArray subhisto+ ImpGen.emit $ Imp.SetMem pe_mem subhisto_mem $ Space "device"++ sIf (Imp.var num_histos int32 .==. 1) unitHistoCase $ do+ -- For the segmented reduction, we keep the segment dimensions+ -- unchanged. To this, we add two dimensions: one over the number+ -- of buckets, and one over the number of subhistograms. This+ -- inner dimension is the one that is collapsed in the reduction.+ let segment_dims = init $ spaceDimensions genred_space+ num_buckets = genReduceWidth op++ bucket_id <- newVName "bucket_id"+ subhistogram_id <- newVName "subhistogram_id"+ vector_ids <- mapM (const $ newVName "vector_id") $+ shapeDims $ genReduceShape op+ gtid <- newVName $ baseString $ spaceGlobalId genred_space+ let lam = genReduceOp op+ segred_space =+ genred_space+ { spaceStructure =+ FlatThreadSpace $+ segment_dims +++ [(bucket_id, num_buckets)] +++ zip vector_ids (shapeDims $ genReduceShape op) +++ [(subhistogram_id, Var num_histos)]+ , spaceGlobalId = gtid+ }++ compileSegRed' (Pattern [] red_pes) segred_space+ Commutative lam (genReduceNeutral op) $ \red_dests _ ->+ forM_ (zip red_dests subhistos) $ \((d, is), subhisto) ->+ ImpGen.copyDWIM d is (Var subhisto) $ map (`Imp.var` int32) $+ map fst segment_dims ++ [subhistogram_id, bucket_id] ++ vector_ids
src/Futhark/CodeGen/ImpGen/Kernels/SegRed.hs view
@@ -43,12 +43,12 @@ -- have any element to read, which becomes highly inefficient. module Futhark.CodeGen.ImpGen.Kernels.SegRed ( compileSegRed+ , compileSegRed' ) where import Control.Monad.Except import Data.Maybe-import qualified Data.Set as S import Data.List import Prelude hiding (quot, rem)@@ -83,14 +83,29 @@ i <- newVName "i" sFor i Int32 iterations $ m $ group_id + Imp.var i int32 * kernelNumGroups constants --- Compile 'SegRed' instance to host-level code with calls to various--- kernels.+-- | Compile 'SegRed' instance to host-level code with calls to+-- various kernels. compileSegRed :: Pattern ExplicitMemory -> KernelSpace -> Commutativity -> Lambda InKernel -> [SubExp] -> Body InKernel -> CallKernelGen ()-compileSegRed pat space comm red_op nes body+compileSegRed pat space comm red_op nes body =+ compileSegRed' pat space comm red_op nes $ \red_dests map_dests ->+ ImpGen.compileStms mempty (stmsToList $ bodyStms body) $ do+ let (red_res, map_res) = splitAt (length nes) $ bodyResult body+ sComment "save results to be reduced" $+ forM_ (zip red_dests red_res) $ \((d,is), se) -> ImpGen.copyDWIM d is se []+ sComment "save map-out results" $+ forM_ (zip map_dests map_res) $ \((d,is), se) -> ImpGen.copyDWIM d is se []++-- | Like 'compileSegRed', but where the body is a monadic action.+compileSegRed' :: Pattern ExplicitMemory+ -> KernelSpace+ -> Commutativity -> Lambda InKernel -> [SubExp]+ -> ([(VName, [Imp.Exp])] -> [(VName, [Imp.Exp])] -> InKernelGen ())+ -> CallKernelGen ()+compileSegRed' pat space comm red_op nes body | [(_, Constant (IntValue (Int32Value 1))), _] <- spaceDimensions space = nonsegmentedReduction pat space comm red_op nes body | otherwise = do@@ -98,52 +113,14 @@ ImpGen.compileSubExp $ last $ map snd $ spaceDimensions space group_size <- ImpGen.compileSubExp $ spaceGroupSize space let use_small_segments = segment_size * 2 .<. group_size- sIf (segment_size .==. 1)- (unitSegmentsReduction pat space nes body) $- sIf use_small_segments+ sIf use_small_segments (smallSegmentsReduction pat space red_op nes body) (largeSegmentsReduction pat space comm red_op nes body) --- Handle degenerate case where segments are of size 1, meaning--- that it is really just a 'map' in disguise.-unitSegmentsReduction :: Pattern ExplicitMemory- -> KernelSpace- -> [SubExp]- -> Body InKernel- -> CallKernelGen ()-unitSegmentsReduction (Pattern _ segred_pes) space nes body = do- (constants, init_constants) <- kernelInitialisationSetSpace space $ return ()-- let (gtids, dims) = unzip $ spaceDimensions space- (redout_pes, mapout_pes) = splitAt (length nes) segred_pes-- dims' <- mapM ImpGen.compileSubExp dims-- let num_segments = product $ init dims'- required_groups = num_segments `quotRoundingUp` kernelGroupSize constants-- ImpGen.emit $ Imp.DebugPrint "num_segments" int32 num_segments- ImpGen.emit $ Imp.DebugPrint "required_groups" int32 required_groups-- sKernel constants "segred_mapseg" $ do- init_constants- virtualiseGroups constants required_groups $ \group_id -> do- setSpaceIndices (group_id * kernelGroupSize constants + kernelLocalThreadId constants) space- ImpGen.compileStms mempty (stmsToList $ bodyStms body) $- sWhen (kernelThreadActive constants) $ do- let (redout_ses, mapout_ses) = splitAt (length nes) $ bodyResult body- forM_ (zip redout_pes redout_ses) $ \(pe, se) ->- ImpGen.copyDWIM (patElemName pe)- (map (`Imp.var` int32) (init gtids)) se []-- forM_ (zip mapout_pes mapout_ses) $ \(pe, se) ->- ImpGen.copyDWIM (patElemName pe)- (map (`Imp.var` int32) gtids) se []- nonsegmentedReduction :: Pattern ExplicitMemory -> KernelSpace -> Commutativity -> Lambda InKernel -> [SubExp]- -> Body InKernel+ -> ([(VName, [Imp.Exp])] -> [(VName, [Imp.Exp])] -> InKernelGen ()) -> CallKernelGen () nonsegmentedReduction segred_pat space comm red_op nes body = do (base_constants, init_constants) <- kernelInitialisationSetSpace space $ return ()@@ -166,7 +143,7 @@ counter <- ImpGen.sStaticArray "counter" (Space "device") int32 $- replicate 1 $ IntValue $ Int32Value 0+ Imp.ArrayValues $ replicate 1 $ IntValue $ Int32Value 0 group_res_arrs <- forM (lambdaReturnType red_op) $ \t -> do let pt = elemType t@@ -198,14 +175,10 @@ (kernelNumGroups constants) group_result_params red_acc_params red_op_renamed nes 1 counter sync_arr group_res_arrs red_arrs -hasMemoryAccesses :: Body InKernel -> ImpGen.ImpM InKernel Imp.KernelOp Bool-hasMemoryAccesses body = or <$> mapM isArray (S.toList $ freeInBody body)- where isArray = fmap (not . primType) . lookupType- smallSegmentsReduction :: Pattern ExplicitMemory -> KernelSpace -> Lambda InKernel -> [SubExp]- -> Body InKernel+ -> ([(VName, [Imp.Exp])] -> [(VName, [Imp.Exp])] -> InKernelGen ()) -> CallKernelGen () smallSegmentsReduction (Pattern _ segred_pes) space red_op nes body = do (base_constants, init_constants) <- kernelInitialisationSetSpace space $ return ()@@ -215,8 +188,12 @@ dims' <- mapM ImpGen.compileSubExp dims let segment_size = last dims'+ -- Careful to avoid division by zero now.+ segment_size_nonzero_v <- dPrimV "segment_size_nonzero" $+ BinOpExp (SMax Int32) 1 segment_size+ let segment_size_nonzero = Imp.var segment_size_nonzero_v int32 num_segments = product $ init dims'- segments_per_group = kernelGroupSize constants `quot` segment_size+ segments_per_group = kernelGroupSize constants `quot` segment_size_nonzero required_groups = num_segments `quotRoundingUp` segments_per_group let red_op_params = lambdaParams red_op@@ -248,44 +225,37 @@ -- the segment ID, and are computed from the group id. The inner -- is computed from the local thread id, and may be out-of-bounds. let ltid = kernelLocalThreadId constants- segment_index = (ltid `quot` segment_size) + (group_id' * segments_per_group)+ segment_index = (ltid `quot` segment_size_nonzero) + (group_id' * segments_per_group) index_within_segment = ltid `rem` segment_size zipWithM_ (<--) (init gtids) $ unflattenIndex (init dims') segment_index last gtids <-- index_within_segment let toLocalMemory ses =- forM_ (zip red_arrs ses) $ \(arr, se) -> do- se_t <- subExpType se- when (primType se_t) $- ImpGen.copyDWIM arr [ltid] se []+ forM_ (zip red_arrs ses) $ \(arr, se) ->+ ImpGen.copyDWIM arr [ltid] se [] in_bounds =- ImpGen.compileStms mempty (stmsToList $ bodyStms body) $ do- let (red_res, map_res) = splitAt (length nes) $ bodyResult body-- sComment "save results to be reduced" $- toLocalMemory red_res-- sComment "save map-out results" $- forM_ (zip (drop (length nes) segred_pes) map_res) $ \(pe, se) ->- ImpGen.copyDWIM (patElemName pe) (map (`Imp.var` int32) gtids) se []+ body (zip red_arrs $ repeat [ltid])+ (zip (map patElemName $ drop (length nes) segred_pes) $+ repeat $ map (`Imp.var` int32) gtids) sComment "apply map function if in bounds" $- sIf (isActive (init $ zip gtids dims) .&&.+ sIf (segment_size .>. 0 .&&.+ isActive (init $ zip gtids dims) .&&. ltid .<. segment_size * segments_per_group) in_bounds (toLocalMemory nes) sOp Imp.LocalBarrier index_i <- newVName "index_i" index_j <- newVName "index_j"- let crossesSegment from to =- (to-from) .>. (to `rem` segment_size)+ let crossesSegment from to = (to-from) .>. (to `rem` segment_size) red_op' = red_op { lambdaParams = Param index_i (MemPrim int32) : Param index_j (MemPrim int32) : lambdaParams red_op } - sComment "perform segmented scan to imitate reduction" $+ sWhen (segment_size .>. 0) $+ sComment "perform segmented scan to imitate reduction" $ groupScan constants (Just crossesSegment) (segment_size*segments_per_group) red_op' red_arrs sOp Imp.LocalBarrier@@ -298,12 +268,16 @@ let flat_segment_index = group_id' * segments_per_group + ltid gtids' = unflattenIndex (init dims') flat_segment_index ImpGen.copyDWIM (patElemName pe) gtids'- (Var arr) [(ltid+1) * segment_size - 1]+ (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.LocalBarrier+ largeSegmentsReduction :: Pattern ExplicitMemory -> KernelSpace -> Commutativity -> Lambda InKernel -> [SubExp]- -> Body InKernel+ -> ([(VName, [Imp.Exp])] -> [(VName, [Imp.Exp])] -> InKernelGen ()) -> CallKernelGen () largeSegmentsReduction segred_pat space comm red_op nes body = do (base_constants, init_constants) <- kernelInitialisationSetSpace space $ return ()@@ -368,7 +342,7 @@ let num_counters = 1024 counter <- ImpGen.sStaticArray "counter" (Space "device") int32 $- replicate num_counters $ IntValue $ Int32Value 0+ Imp.ArrayZeros num_counters sync_arr <- ImpGen.sAllocArray "sync_arr" Bool (Shape [intConst Int32 1]) $ Space "local" @@ -408,11 +382,12 @@ sIf (groups_per_segment .==. 1) one_group_per_segment multiple_groups_per_segment +-- Careful to avoid division by zero here. groupsPerSegmentAndElementsPerThread :: Imp.Exp -> Imp.Exp -> Imp.Exp -> Imp.Exp -> (Imp.Exp, Imp.Count Imp.Elements) groupsPerSegmentAndElementsPerThread segment_size num_segments num_groups_hint group_size = let groups_per_segment =- num_groups_hint `quotRoundingUp` num_segments+ num_groups_hint `quotRoundingUp` BinOpExp (SMax Int32) 1 num_segments elements_per_thread = segment_size `quotRoundingUp` (group_size * groups_per_segment) in (groups_per_segment, Imp.elements elements_per_thread)@@ -427,7 +402,7 @@ -> LambdaT InKernel -> [SubExp] -> [VName]- -> Body InKernel+ -> ([(VName, [Imp.Exp])] -> [(VName, [Imp.Exp])] -> InKernelGen ()) -> InKernelGen ([LParam InKernel], Lambda InKernel) reductionStageOne constants (Pattern _ segred_pes) num_elements global_tid elems_per_thread threads_per_segment comm red_op nes red_arrs body = do @@ -436,13 +411,11 @@ (gtids, _dims) = unzip $ kernelDimensions constants gtid = last gtids local_tid = kernelLocalThreadId constants- index_in_segment = global_tid `quot` kernelGroupSize constants -- Figure out how many elements this thread should process. chunk_size <- dPrim "chunk_size" int32 let ordering = case comm of Commutative -> SplitStrided $ Var threads_per_segment Noncommutative -> SplitContiguous- accesses_memory <- hasMemoryAccesses body computeThreadChunkSize ordering global_tid elems_per_thread num_elements chunk_size ImpGen.dScope Nothing $ scopeOfLParams $ lambdaParams red_op@@ -462,6 +435,8 @@ groupReduce constants (kernelGroupSize constants) red_op_renamed red_arrs + sOp Imp.LocalBarrier+ i <- newVName "i" -- If this is a non-commutative reduction, each thread must run the -- loop the same number of iterations, because we will be performing@@ -478,31 +453,24 @@ Commutative -> global_tid + Imp.var threads_per_segment int32 * Imp.var i int32- Noncommutative | accesses_memory ->- local_tid +- (index_in_segment * Imp.innerExp elems_per_thread + Imp.var i int32) *- kernelGroupSize constants Noncommutative ->- Imp.var i int32 +- global_tid * Imp.innerExp elems_per_thread-- check_bounds $ sComment "apply map function" $- ImpGen.compileStms mempty (stmsToList $ bodyStms body) $ do- let (red_res, map_res) = splitAt (length nes) $ bodyResult body+ let index_in_segment = global_tid `quot` kernelGroupSize constants+ in local_tid ++ (index_in_segment * Imp.innerExp elems_per_thread + Imp.var i int32) *+ kernelGroupSize constants - sComment "save results to be reduced" $- forM_ (zip red_next_params red_res) $ \(p, se) ->- ImpGen.copyDWIM (paramName p) [] se []+ let red_dests = zip (map paramName red_next_params) $ repeat []+ map_dests = zip (map patElemName $ drop (length nes) segred_pes) $+ repeat $ map (`Imp.var` int32) gtids - sComment "save map-out results" $- forM_ (zip (drop (length nes) segred_pes) map_res) $ \(pe, se) ->- ImpGen.copyDWIM (patElemName pe) (map (`Imp.var` int32) gtids) se []+ check_bounds $ sComment "apply map function" $ do+ body red_dests map_dests - sComment "apply reduction operator" $- ImpGen.compileBody' red_acc_params $ lambdaBody red_op+ sComment "apply reduction operator" $+ ImpGen.compileBody' red_acc_params $ lambdaBody red_op case comm of- Noncommutative | accesses_memory -> do+ Noncommutative -> do doTheReduction sComment "first thread takes carry-out; others neutral element" $ do let carry_out =@@ -512,17 +480,12 @@ forM_ (zip red_acc_params nes) $ \(p, ne) -> ImpGen.copyDWIM (paramName p) [] ne [] sIf (local_tid .==. 0) carry_out reset_to_neutral- _ ->- return ()-- group_result_params <- case comm of- Noncommutative | accesses_memory ->- return red_acc_params-- _ -> do- doTheReduction+ _ -> return () - return $ lambdaParams red_op_renamed+ group_result_params <-+ case comm of Noncommutative -> return red_acc_params+ _ -> do doTheReduction+ return $ lambdaParams red_op_renamed return (group_result_params, red_op_renamed) @@ -590,6 +553,8 @@ load_group_result load_neutral_element when (primType $ paramType p) $ ImpGen.copyDWIM arr [local_tid] (Var $ paramName p) []++ sOp Imp.LocalBarrier sComment "reduce the per-group results" $ do groupReduce constants group_size red_op_renamed red_arrs
src/Futhark/CodeGen/ImpGen/Kernels/ToOpenCL.hs view
@@ -29,7 +29,7 @@ import qualified Futhark.CodeGen.ImpCode.OpenCL as ImpOpenCL import Futhark.MonadFreshNames import Futhark.Util (zEncodeString)-import Futhark.Util.Pretty (pretty, prettyOneLine)+import Futhark.Util.Pretty (pretty) kernelsToCUDA, kernelsToOpenCL :: ImpKernels.Program -> Either InternalError ImpOpenCL.Program@@ -65,17 +65,15 @@ type UsedFunctions = [(String,C.Func)] -- The ordering is important! -data OpenClRequirements =- OpenClRequirements { kernelUsedTypes :: S.Set PrimType- , _kernelConstants :: [(VName, KernelConstExp)]- }+newtype OpenClRequirements =+ OpenClRequirements { kernelUsedTypes :: S.Set PrimType } instance Semigroup OpenClRequirements where- OpenClRequirements ts1 consts1 <> OpenClRequirements ts2 consts2 =- OpenClRequirements (ts1 <> ts2) (consts1 <> consts2)+ OpenClRequirements ts1 <> OpenClRequirements ts2 =+ OpenClRequirements (ts1 <> ts2) instance Monoid OpenClRequirements where- mempty = OpenClRequirements mempty mempty+ mempty = OpenClRequirements mempty data ToOpenCL = ToOpenCL { clExtraFuns :: M.Map Name ImpOpenCL.Function , clKernels :: M.Map KernelName C.Func@@ -140,16 +138,17 @@ params = perm_params ++ catMaybes local_memory_params ++ use_params + const_defs = mapMaybe constDef $ kernelUses kernel+ tell mempty { clExtraFuns = mempty , clKernels = M.singleton name [C.cfun|__kernel void $id:name ($params:params) {+ $items:const_defs $items:block_dim_init $items:local_memory_init $items:kernel_body }|]- , clRequirements = OpenClRequirements- (typesInKernel kernel)- (mapMaybe useAsConst $ kernelUses kernel)+ , clRequirements = OpenClRequirements (typesInKernel kernel) } return $ LaunchKernel name (kernelArgs kernel) num_groups group_size@@ -186,9 +185,11 @@ useAsParam ConstUse{} = Nothing -useAsConst :: KernelUse -> Maybe (VName, KernelConstExp)-useAsConst (ConstUse v e) = Just (v,e)-useAsConst _ = Nothing+constDef :: KernelUse -> Maybe C.BlockItem+constDef (ConstUse v e) = Just [C.citem|const $ty:t $id:v = $exp:e';|]+ where t = GenericC.primTypeToCType $ primExpType e+ e' = compilePrimExp e+constDef _ = Nothing openClCode :: [C.Func] -> String openClCode kernels =@@ -198,9 +199,13 @@ kernel_func <- kernels ] genOpenClPrelude :: OpenClRequirements -> [C.Definition]-genOpenClPrelude (OpenClRequirements ts consts) =+genOpenClPrelude (OpenClRequirements ts) = -- Clang-based OpenCL implementations need this for 'static' to work.- [C.cedecl|$esc:("#pragma OPENCL EXTENSION cl_clang_storage_class_specifiers : enable")|] :+ [ [C.cedecl|$esc:("#ifdef cl_clang_storage_class_specifiers")|]+ , [C.cedecl|$esc:("#pragma OPENCL EXTENSION cl_clang_storage_class_specifiers : enable")|]+ , [C.cedecl|$esc:("#endif")|]+ , [C.cedecl|$esc:("#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : enable")|]]+ ++ [[C.cedecl|$esc:("#pragma OPENCL EXTENSION cl_khr_fp64 : enable")|] | uses_float64] ++ [C.cunit| /* Some OpenCL programs dislike empty progams, or programs with no kernels.@@ -222,10 +227,24 @@ typedef ulong uint64_t; $esc:("#define ALIGNED_LOCAL_MEMORY(m,size) __local unsigned char m[size] __attribute__ ((align))")++// NVIDIAs OpenCL does not create device-wide memory fences (see #734), so we+// use inline assembly if we detect we are on an NVIDIA GPU.+$esc:("#ifdef cl_nv_pragma_unroll")+static inline void mem_fence_global() {+ asm("membar.gl;");+}+$esc:("#else")+static inline void mem_fence_global() {+ mem_fence(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE);+}+$esc:("#endif")+static inline void mem_fence_local() {+ mem_fence(CLK_LOCAL_MEM_FENCE);+} |] ++ cIntOps ++ cFloat32Ops ++ cFloat32Funs ++- (if uses_float64 then cFloat64Ops ++ cFloat64Funs ++ cFloatConvOps else []) ++- [ [C.cedecl|$esc:def|] | def <- map constToDefine consts ]+ (if uses_float64 then cFloat64Ops ++ cFloat64Funs ++ cFloatConvOps else []) where uses_float64 = FloatType Float64 `S.member` ts @@ -254,11 +273,10 @@ }|] | t <- types] genCUDAPrelude :: OpenClRequirements -> [C.Definition]-genCUDAPrelude (OpenClRequirements _ consts) =- cudafy ++ cudaAtomicOps ++ defs ++ ops+genCUDAPrelude (OpenClRequirements _) =+ cudafy ++ cudaAtomicOps ++ ops where ops = cIntOps ++ cFloat32Ops ++ cFloat32Funs ++ cFloat64Ops ++ cFloat64Funs ++ cFloatConvOps- defs = [ [C.cedecl|$esc:def|] | def <- map constToDefine consts ] cudafy = [CUDAC.cunit| typedef char int8_t; typedef short int16_t;@@ -349,25 +367,17 @@ { __syncthreads(); }-static inline void mem_fence(int x)-{- if (x == CLK_LOCAL_MEM_FENCE) {- __threadfence_block();- } else {- __threadfence();- }+static inline void mem_fence_local() {+ __threadfence_block(); }+static inline void mem_fence_global() {+ __threadfence();+} $esc:("#define NAN (0.0/0.0)") $esc:("#define INFINITY (1.0/0.0)") extern volatile __shared__ char shared_mem[]; |] -constToDefine :: (VName, KernelConstExp) -> String-constToDefine (name, e) =- let e' = compilePrimExp e- in unwords ["#define", zEncodeString (pretty name), "("++prettyOneLine e'++")"]-- compilePrimExp :: PrimExp KernelConst -> C.Exp compilePrimExp e = runIdentity $ GenericC.compilePrimExp compileKernelConst e where compileKernelConst (SizeConst key) =@@ -413,7 +423,7 @@ kernelOps GlobalBarrier = GenericC.stm [C.cstm|barrier(CLK_GLOBAL_MEM_FENCE);|] kernelOps MemFence =- GenericC.stm [C.cstm|mem_fence(CLK_GLOBAL_MEM_FENCE);|]+ GenericC.stm [C.cstm|mem_fence_global();|] kernelOps (Atomic aop) = atomicOps aop atomicOps (AtomicAdd old arr ind val) = do
src/Futhark/CodeGen/OpenCL/Kernels.hs view
@@ -28,7 +28,7 @@ | HeuristicDeviceInfo String -- | A size that can be assigned a default.-data WhichSize = LockstepWidth | NumGroups | GroupSize | TileSize+data WhichSize = LockstepWidth | NumGroups | GroupSize | TileSize | Threshold -- | A heuristic for setting the default value for something. data SizeHeuristic =@@ -47,9 +47,11 @@ , SizeHeuristic "" DeviceGPU NumGroups $ HeuristicConst 256 , SizeHeuristic "" DeviceGPU GroupSize $ HeuristicConst 256 , SizeHeuristic "" DeviceGPU TileSize $ HeuristicConst 32+ , SizeHeuristic "" DeviceGPU Threshold $ HeuristicConst $ 32*1024 , SizeHeuristic "" DeviceCPU LockstepWidth $ HeuristicConst 1 , SizeHeuristic "" DeviceCPU NumGroups $ HeuristicDeviceInfo "MAX_COMPUTE_UNITS" , SizeHeuristic "" DeviceCPU GroupSize $ HeuristicConst 32 , SizeHeuristic "" DeviceCPU TileSize $ HeuristicConst 4+ , SizeHeuristic "" DeviceCPU Threshold $ HeuristicDeviceInfo "MAX_COMPUTE_UNITS" ]
src/Futhark/Construct.hs view
@@ -30,6 +30,7 @@ , eSliceArray , eSplitArray + , eOutOfBounds , eWriteArray , asIntZ, asIntS@@ -285,16 +286,13 @@ offset' <- letSubExp "offset" $ BasicOp $ BinOp (Add Int32) offset size return (offset', offset) --- | Write to an index of the array, if within bounds. Otherwise,--- nothing. Produces the updated array.-eWriteArray :: (MonadBinder m, BranchType (Lore m) ~ ExtType) =>- VName -> [m (Exp (Lore m))] -> m (Exp (Lore m))- -> m (Exp (Lore m))-eWriteArray arr is v = do+-- | Are these indexes out-of-bounds for the array?+eOutOfBounds :: MonadBinder m =>+ VName -> [m (Exp (Lore m))] -> m (Exp (Lore m))+eOutOfBounds arr is = do arr_t <- lookupType arr let ws = arrayDims arr_t is' <- mapM (letSubExp "write_i") =<< sequence is- v' <- letSubExp "write_v" =<< v let checkDim w i = do less_than_zero <- letSubExp "less_than_zero" $ BasicOp $ CmpOp (CmpSlt Int32) i (constant (0::Int32))@@ -302,11 +300,19 @@ BasicOp $ CmpOp (CmpSle Int32) w i letSubExp "outside_bounds_dim" $ BasicOp $ BinOp LogOr less_than_zero greater_than_size+ foldBinOp LogOr (constant False) =<< zipWithM checkDim ws is' - outside_bounds <-- letSubExp "outside_bounds" =<<- foldBinOp LogOr (constant False) =<<- zipWithM checkDim ws is'+-- | Write to an index of the array, if within bounds. Otherwise,+-- nothing. Produces the updated array.+eWriteArray :: (MonadBinder m, BranchType (Lore m) ~ ExtType) =>+ VName -> [m (Exp (Lore m))] -> m (Exp (Lore m))+ -> m (Exp (Lore m))+eWriteArray arr is v = do+ arr_t <- lookupType arr+ is' <- mapM (letSubExp "write_i") =<< sequence is+ v' <- letSubExp "write_v" =<< v++ outside_bounds <- letSubExp "outside_bounds" =<< eOutOfBounds arr is outside_bounds_branch <- insertStmsM $ resultBodyM [Var arr]
src/Futhark/Doc/Generator.hs view
@@ -715,10 +715,9 @@ describeDec _ ImportDec{} = Nothing valBindWhat :: ValBind -> IndexWhat-valBindWhat vb =- if null (valBindParams vb) && orderZero (unInfo (valBindRetType vb))- then IndexValue- else IndexFunction+valBindWhat vb | null (valBindParams vb),+ orderZero (unInfo (valBindRetType vb)) = IndexValue+ | otherwise = IndexFunction describeSpecs :: [Spec] -> DocM Html describeSpecs specs =
src/Futhark/Internalise.hs view
@@ -459,11 +459,11 @@ mergeinit_ts' <- mapM subExpType mergeinit' - let ctxinit = argShapes- (map I.paramName shapepat)- (map I.paramType mergepat')- mergeinit_ts'- ctxmerge = zip shapepat ctxinit+ ctxinit <- argShapes+ (map I.paramName shapepat)+ (map I.paramType mergepat')+ mergeinit_ts'+ let ctxmerge = zip shapepat ctxinit valmerge = zip mergepat' mergeinit' merge = ctxmerge ++ valmerge dropCond = case form of E.While{} -> drop 1@@ -487,10 +487,10 @@ inScopeOf form' $ internaliseBodyStms loopbody $ \ses -> do sets <- mapM subExpType ses let mergepat' = concat nested_mergepat- shapeargs = argShapes- (map I.paramName shapepat)- (map I.paramType mergepat')- sets+ shapeargs <- argShapes+ (map I.paramName shapepat)+ (map I.paramType mergepat')+ sets return (resultBody $ shapeargs ++ ses, (form', shapepat,@@ -536,10 +536,10 @@ -- next iteration?). This is safe, as the type rules for -- the external language guarantees that 'cond' does not -- consume anything.- let shapeinit = argShapes- (map I.paramName shapepat)- (map I.paramType mergepat')- mergeinit_ts+ shapeinit <- argShapes+ (map I.paramName shapepat)+ (map I.paramType mergepat')+ mergeinit_ts (loop_initial_cond, init_loop_cond_bnds) <- collectStms $ do forM_ (zip shapepat shapeinit) $ \(p, se) ->@@ -555,10 +555,10 @@ internaliseBodyStms loopbody $ \ses -> do sets <- mapM subExpType ses loop_while <- newParam "loop_while" $ I.Prim I.Bool- let shapeargs = argShapes- (map I.paramName shapepat)- (map I.paramType mergepat')- sets+ shapeargs <- argShapes+ (map I.paramName shapepat)+ (map I.paramType mergepat')+ sets -- Careful not to clobber anything. loop_end_cond_body <- renameBody <=< insertStmsM $ do@@ -618,9 +618,6 @@ return $ bef ++ src'' ++ aft replace _ _ ve' _ = return ve' -internaliseExp desc (E.Unzip e _ _) =- internaliseExp desc e- internaliseExp desc (E.Unsafe e _) = local (\env -> env { envDoBoundsChecks = False }) $ internaliseExp desc e@@ -636,33 +633,6 @@ letBindNames_ [v'] $ I.BasicOp $ I.SubExp v return $ I.Var v' -internaliseExp _ (E.Zip _ e es _ loc) = do- e' <- internaliseExpToVars "zip_arg" $ TupLit (e:es) loc- case e' of- e_key:es_unchecked -> do- -- We will reshape all of es_unchecked' to have the same outer- -- size as ts. We will not change any of the inner dimensions.- -- This will cause a runtime error if the outer sizes do not match,- -- thus preserving the semantics of zip().- w <- arraySize 0 <$> lookupType e_key- let reshapeToOuter e_unchecked' = do- unchecked_t <- lookupType e_unchecked'- case I.arrayDims unchecked_t of- outer:inner | w /= outer -> do- cmp <- letSubExp "zip_cmp" $ I.BasicOp $- I.CmpOp (I.CmpEq I.int32) w outer- c <- assertingOne $- letExp "zip_assert" $ I.BasicOp $- I.Assert cmp "arrays differ in length" (loc, mempty)- certifying c $ letExp (postfix e_unchecked' "_zip_res") $- shapeCoerce (w:inner) e_unchecked'- _ -> return e_unchecked'- es' <- mapM reshapeToOuter es_unchecked- return $ map I.Var $ e_key : es'- [] -> return []-- where postfix i s = baseString i ++ s- internaliseExp desc (E.Map lam arr _ _) = do arr' <- internaliseExpToVars "map_arr" arr lam' <- internaliseMapLambda internaliseLambda lam $ map I.Var arr'@@ -1420,48 +1390,9 @@ ressize <- foldM sumdims outer_size =<< mapM (fmap (arraysSize 0) . mapM lookupType) [ys] - let conc xarr yarr = do- -- All dimensions except the outermost must match. An- -- empty array matches anything.- xt <- lookupType xarr- yt <- lookupType yarr- let matches n m =- letSubExp "match" $- I.BasicOp $ I.CmpOp (I.CmpEq I.int32) n m-- emptyRow arr_t =- letSubExp "empty_row" =<<- foldBinOp I.LogOr (constant False) =<<- mapM (matches (intConst Int32 0)) (arrayDims $ rowType arr_t)-- all_match <- letSubExp "all_match" =<<- foldBinOp I.LogAnd (constant True) =<<- zipWithM matches- (arrayDims (rowType xt)) (arrayDims (rowType yt))- xarr_empty <- emptyRow xt- yarr_empty <- emptyRow yt- either_empty <- letSubExp "either_empty" $- I.BasicOp $ I.BinOp I.LogOr xarr_empty yarr_empty- matchcs <- assertingOne $ letExp "concat_ok" =<<- eAssert (pure $ I.BasicOp $ I.BinOp I.LogOr either_empty all_match)- "row sizes do not match when concatenating" loc-- let updims (j, xd, yd)- | j == 0 =- return (xd, yd)- | otherwise = do- d <- letSubExp "dim" $ I.BasicOp $ I.BinOp (SMax Int32) xd yd- return (d, d)-- (xdims, ydims) <- unzip <$>- mapM updims (zip3 [(0::Int)..] (I.arrayDims xt) (I.arrayDims yt))-- xarr' <- certifying matchcs $ letExp "concat_x_reshaped" $- shapeCoerce xdims xarr- yarr' <- certifying matchcs $ letExp "concat_y_reshaped" $- shapeCoerce ydims yarr- return $ I.BasicOp $ I.Concat 0 xarr' [yarr'] ressize- letSubExps desc =<< zipWithM conc xs ys+ let conc xarr yarr =+ I.BasicOp $ I.Concat 0 xarr [yarr] ressize+ letSubExps desc $ zipWith conc xs ys handle [TupLit [offset, e] _] "rotate" = Just $ \desc -> do offset' <- internaliseExp1 "rotation_offset" offset@@ -1635,8 +1566,8 @@ (constargs, const_ds, _) <- unzip3 <$> constFunctionArgs loc constparams argts <- mapM subExpType args closure_ts <- mapM lookupType closure- let shapeargs = argShapes shapes value_paramts argts- diets = const_ds ++ replicate (length closure + length shapeargs) I.Observe +++ shapeargs <- argShapes shapes value_paramts argts+ let diets = const_ds ++ replicate (length closure + length shapeargs) I.Observe ++ map I.diet value_paramts constOrShape = const $ I.Prim int32 paramts = map constOrShape constargs ++ closure_ts ++
src/Futhark/Internalise/AccurateSizes.hs view
@@ -15,6 +15,7 @@ import Control.Monad import Data.Loc import qualified Data.Map.Strict as M+import qualified Data.Set as S import Futhark.Construct import Futhark.Representation.AST@@ -26,7 +27,7 @@ runBodyBinder $ do ses <- bodyBind body sets <- mapM subExpType ses- return $ resultBody $ argShapes shapenames ts sets+ resultBody <$> argShapes shapenames ts sets annotateArrayShape :: ArrayShape shape => TypeBase shape u -> [Int] -> TypeBase Shape u@@ -34,13 +35,25 @@ t `setArrayShape` Shape (take (arrayRank t) $ map (intConst Int32 . toInteger) $ newshape ++ repeat 0) -argShapes :: [VName] -> [TypeBase Shape u0] -> [TypeBase Shape u1] -> [SubExp]+-- Some trickery is needed here to predict sensible values for+-- dimensions that are used exclusively as the inner dimension of an+-- array. The issue is that the dimension may be inside an empty+-- array. In this case, the dimension inside the empty array should+-- not count, as it will be zero. The solution we use is to take the+-- maximum of such sizes; this will effectively disregard the zeroes.+argShapes :: MonadBinder m =>+ [VName] -> [TypeBase Shape u0] -> [TypeBase Shape u1] -> m [SubExp] argShapes shapes valts valargts =- map addShape shapes+ mapM addShape shapes where mapping = shapeMapping valts valargts- addShape name- | Just se <- M.lookup name mapping = se- | otherwise = intConst Int32 0+ outer_dims = map (arraySize 0) valts+ addShape name =+ case M.lookup name mapping of+ Just s | x:xs <- S.toList s ->+ if Var name `elem` outer_dims+ then return x+ else letSubExp "size" =<< foldBinOp (SMax Int32) x xs+ _ -> return $ intConst Int32 0 ensureResultShape :: MonadBinder m => (m Certificates -> m Certificates)
src/Futhark/Internalise/Defunctionalise.hs view
@@ -363,15 +363,6 @@ arr' <- defuncExp' arr return (Stream form' lam' arr' loc, Dynamic $ typeOf e) -defuncExp e@(Zip i e1 es t loc) = do- e1' <- defuncExp' e1- es' <- mapM defuncExp' es- return (Zip i e1' es' t loc, Dynamic $ typeOf e)--defuncExp e@(Unzip e0 tps loc) = do- e0' <- defuncExp' e0- return (Unzip e0' tps loc, Dynamic $ typeOf e)- defuncExp (Unsafe e1 loc) = do (e1', sv) <- defuncExp e1 return (Unsafe e1' loc, sv)@@ -497,67 +488,42 @@ let closure_pat = buildEnvPattern closure_env pat' = updatePattern pat sv2 - -- Inline certain trivial lifted functions immediately. This is- -- purely an optimisation to avoid having the rest of the- -- compiler spend a lot of time processing them (they will end- -- up being inlined later anyway). We also try to simplify away- -- some let-bindings, to make the generated code look slightly- -- more comprehensible.- --- -- If you are debugging the defunctionaliser, you may want to- -- turn this off to see the original structure of the generated- -- code instead.- let letPat (RecordPattern [] _) _ pbody = pbody- letPat (PatternParens pp _) pe pbody = letPat pp pe pbody- letPat (Id v1 _ _) pe (RecordLit [RecordFieldExplicit f (Var v2 _ _) floc] rloc)- | v1 == qualLeaf v2 =- RecordLit [RecordFieldExplicit f pe floc] rloc- letPat (RecordPattern [(pf,p)] _) (RecordLit [RecordFieldExplicit f pe _] _) pbody- | pf == f =- letPat p pe pbody- letPat pp pe pbody = LetPat [] pp pe pbody noLoc-- inline RecordLit{} = True- inline TupLit{} = True- inline (Apply x y _ _ _) = inline x && inline y- inline (BinOp _ _ (x, _) (y, _) _ _) = inline x && inline y- inline Var{} = True- inline Literal{} = True- inline (LetPat _ _ x y _) = inline x && inline y- inline Negate{} = True- inline _ = False- if inline e0' && null dims- then return (letPat closure_pat e1' $ letPat pat' e2' e0', sv)- else do- -- Lift lambda to top-level function definition.- let params = [closure_pat, pat']- rettype = buildRetType closure_env params e0_t $ typeOf e0'+ -- Lift lambda to top-level function definition. We put in+ -- a lot of effort to try to infer the uniqueness attributes+ -- of the lifted function, but this is ultimately all a sham+ -- and a hack. There is some piece we're missing.+ let params = [closure_pat, pat']+ params_for_rettype = params ++ svParams sv1 ++ svParams sv2+ svParams (LambdaSV _ sv_pat _ _ _) = [sv_pat]+ svParams _ = []+ rettype = buildRetType closure_env params_for_rettype e0_t $ typeOf e0' - -- Embed some information about the original function- -- into the name of the lifted function, to make the- -- result slightly more human-readable.- liftedName i (Var f _ _) =- "lifted_" ++ show i ++ "_" ++ baseString (qualLeaf f)- liftedName i (Apply f _ _ _ _) =- liftedName (i+1) f- liftedName _ _ = "lifted"- fname <- newNameFromString $ liftedName (0::Int) e1- liftValDec fname rettype dims params e0'+ -- Embed some information about the original function+ -- into the name of the lifted function, to make the+ -- result slightly more human-readable.+ liftedName i (Var f _ _) =+ "lifted_" ++ show i ++ "_" ++ baseString (qualLeaf f)+ liftedName i (Apply f _ _ _ _) =+ liftedName (i+1) f+ liftedName _ _ = "lifted"+ fname <- newNameFromString $ liftedName (0::Int) e1+ liftValDec fname rettype dims params e0' - let t1 = vacuousShapeAnnotations . toStruct $ typeOf e1'- t2 = vacuousShapeAnnotations . toStruct $ typeOf e2'- fname' = qualName fname- return (Parens (Apply (Apply (Var fname' (Info (Arrow mempty Nothing (fromStruct t1) $- Arrow mempty Nothing (fromStruct t2) rettype)) loc)- e1' (Info Observe) (Info $ Arrow mempty Nothing (fromStruct t2) rettype) loc)- e2' d (Info rettype) loc) noLoc, sv)+ let t1 = vacuousShapeAnnotations . toStruct $ typeOf e1'+ t2 = vacuousShapeAnnotations . toStruct $ typeOf e2'+ fname' = qualName fname+ return (Parens (Apply (Apply (Var fname' (Info (Arrow mempty Nothing (fromStruct t1) $+ Arrow mempty Nothing (fromStruct t2) rettype)) loc)+ e1' (Info Observe) (Info $ Arrow mempty Nothing (fromStruct t2) rettype) loc)+ e2' d (Info rettype) loc) noLoc, sv) -- If e1 is a dynamic function, we just leave the application in place, -- but we update the types since it may be partially applied or return -- a higher-order term. DynamicFun _ sv -> let (argtypes', rettype) = dynamicFunType sv argtypes- apply_e = Apply e1' e2' d (Info $ foldFunType argtypes' rettype) loc+ apply_e = Apply e1' e2' d (Info $ foldFunType argtypes' rettype+ `setAliases` aliases ret) loc in return (apply_e, sv) -- Propagate the 'IntrinsicsSV' until we reach the outermost application,@@ -681,7 +647,7 @@ comb (Record fs_annot) (Record fs_got) = Record $ M.intersectionWith comb fs_annot fs_got comb Arrow{} t = vacuousShapeAnnotations $ descend t- comb got _ = fromStruct got+ comb got et = descend $ fromStruct got `setUniqueness` uniqueness et `setAliases` aliases et descend t@Array{} | any (problematic . aliasVar) (aliases t) = t `setUniqueness` Nonunique@@ -865,8 +831,6 @@ where freeInForm (RedLike _ _ e) = freeVars e freeInForm _ = mempty - Zip _ e es _ _ -> freeVars e <> foldMap freeVars es- Unzip e _ _ -> freeVars e Unsafe e _ -> freeVars e Assert e1 e2 _ _ -> freeVars e1 <> freeVars e2 VConstr0{} -> mempty
src/Futhark/Internalise/Monomorphise.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE FlexibleContexts #-} -- | This monomorphization module converts a well-typed, polymorphic, -- module-free Futhark program into an equivalent monomorphic program. --@@ -29,15 +30,19 @@ import Control.Monad.RWS import Control.Monad.State+import Control.Monad.Writer+import Data.Bitraversable+import Data.Bifunctor import Data.Loc import qualified Data.Map.Strict as M+import qualified Data.Set as S import qualified Data.Sequence as Seq import Data.Foldable import Futhark.MonadFreshNames import Language.Futhark import Language.Futhark.Traversals-import Language.Futhark.TypeChecker.Monad (TypeBinding(..))+import Language.Futhark.Semantic (TypeBinding(..)) import Language.Futhark.TypeChecker.Types -- | The monomorphization monad reads 'PolyBinding's and writes 'ValBinding's.@@ -135,11 +140,24 @@ (Nothing, Nothing) -> return fname -- A polymorphic function. (Nothing, Just funbind) -> do- (fname', funbind') <- monomorphizeBinding funbind t+ (fname', funbind') <- monomorphizeBinding False funbind t tell $ Seq.singleton (fname, funbind') addLifted fname t fname' return fname' +-- | This carries out record replacements in the alias information of a type.+transformType :: TypeBase dim Aliasing -> MonoM (TypeBase dim Aliasing)+transformType t = do+ rrs <- asks envRecordReplacements+ let replace (AliasBound v) | Just d <- M.lookup v rrs =+ S.fromList $ map (AliasBound . fst) $ M.elems d+ replace x = S.singleton x+ -- As an attempt at an optimisation, only transform the aliases if+ -- they refer to a variable we have record-replaced.+ return $ if any ((`M.member` rrs) . aliasVar) $ aliases t+ then bimap id (mconcat . map replace . S.toList) t+ else t+ -- | Monomorphization of expressions. transformExp :: Exp -> MonoM Exp transformExp e@Literal{} = return e@@ -159,9 +177,10 @@ RecordLit <$> mapM transformField fs <*> pure loc where transformField (RecordFieldExplicit name e loc') = RecordFieldExplicit name <$> transformExp e <*> pure loc'- transformField (RecordFieldImplicit v t _) =+ transformField (RecordFieldImplicit v t _) = do+ t' <- traverse transformType t transformField $ RecordFieldExplicit (baseName v)- (Var (qualName v) (vacuousShapeAnnotations <$> t) loc) loc+ (Var (qualName v) (vacuousShapeAnnotations <$> t') loc) loc transformExp (ArrayLit es tp loc) = ArrayLit <$> mapM transformExp es <*> pure tp <*> pure loc@@ -176,13 +195,15 @@ maybe_fs <- lookupRecordReplacement fname case maybe_fs of Just fs -> do- let toField (f, (f_v, f_t)) =- let f_v' = Var (qualName f_v) (Info $ vacuousShapeAnnotations f_t) loc- in RecordFieldExplicit f f_v' loc- return $ RecordLit (map toField $ M.toList fs) loc+ let toField (f, (f_v, f_t)) = do+ f_t' <- transformType f_t+ let f_v' = Var (qualName f_v) (Info $ vacuousShapeAnnotations f_t') loc+ return $ RecordFieldExplicit f f_v' loc+ RecordLit <$> mapM toField (M.toList fs) <*> pure loc Nothing -> do fname' <- transformFName fname (toStructural t)- return $ Var (QualName qs fname') (Info t) loc+ t' <- transformType t+ return $ Var (QualName qs fname') (Info t') loc transformExp (Ascript e tp loc) = Ascript <$> transformExp e <*> pure tp <*> pure loc@@ -213,7 +234,8 @@ e1' <- transformExp e1 e2' <- transformExp e2 e3' <- transformExp e3- return $ If e1' e2' e3' tp loc+ tp' <- traverse transformType tp+ return $ If e1' e2' e3' tp' loc transformExp (Apply e1 e2 d tp loc) = -- We handle on an ad-hoc basis certain polymorphic higher-order@@ -254,7 +276,8 @@ _ -> do e1' <- transformExp e1 e2' <- transformExp e2- return $ Apply e1' e2' d tp loc+ tp' <- traverse transformType tp+ return $ Apply e1' e2' d tp' loc where intrinsic s (QualName _ v) = baseTag v <= maxIntrinsicTag && baseName v == nameFromString s @@ -364,14 +387,6 @@ <*> transformExp e5 -- input image <*> pure loc -transformExp (Zip i e1 es t loc) = do- e1' <- transformExp e1- es' <- mapM transformExp es- return $ Zip i e1' es' t loc--transformExp (Unzip e0 tps loc) =- Unzip <$> transformExp e0 <*> pure tps <*> pure loc- transformExp (Unsafe e1 loc) = Unsafe <$> transformExp e1 <*> pure loc @@ -380,7 +395,7 @@ transformExp e@VConstr0{} = return e transformExp (Match e cs t loc) =- Match <$> transformExp e <*> mapM transformCase cs <*> pure t <*> pure loc+ Match <$> transformExp e <*> mapM transformCase cs <*> traverse transformType t <*> pure loc transformCase :: Case -> MonoM Case transformCase (CasePat p e loc) = do@@ -447,7 +462,7 @@ expandRecordPattern (Id v (Info (Record fs)) loc) = do let fs' = M.toList fs (fs_ks, fs_ts) <- fmap unzip $ forM fs' $ \(f, ft) ->- (,) <$> newVName (nameToString f) <*> pure ft+ (,) <$> newVName (nameToString f) <*> transformType ft return (RecordPattern (zip (map fst fs') (zipWith3 Id fs_ks (map Info fs_ts) $ repeat loc)) loc,@@ -463,7 +478,9 @@ expandRecordPattern (PatternParens pat loc) = do (pat', rr) <- expandRecordPattern pat return (PatternParens pat' loc, rr)-expandRecordPattern (Wildcard t loc) = return (Wildcard t loc, mempty)+expandRecordPattern (Wildcard t loc) = do+ t' <- traverse transformType t+ return (Wildcard t' loc, mempty) expandRecordPattern (PatternAscription pat td loc) = do (pat', rr) <- expandRecordPattern pat return (PatternAscription pat' td loc, rr)@@ -472,24 +489,27 @@ -- | Monomorphize a polymorphic function at the types given in the instance -- list. Monomorphizes the body of the function as well. Returns the fresh name -- of the generated monomorphic function and its 'ValBind' representation.-monomorphizeBinding :: PolyBinding -> TypeBase () () -> MonoM (VName, ValBind)-monomorphizeBinding (PolyBinding rr (name, tparams, params, retdecl, rettype, body, loc)) t =+monomorphizeBinding :: Bool -> PolyBinding -> TypeBase () () -> MonoM (VName, ValBind)+monomorphizeBinding entry (PolyBinding rr (name, tparams, params, retdecl, rettype, body, loc)) t = replaceRecordReplacements rr $ do t' <- removeTypeVariablesInType t let bind_t = foldFunType (map (toStructural . patternType) params) $ toStructural rettype- substs = M.map Subst $ typeSubsts bind_t t'- rettype' = applySubst (`M.lookup` substs) rettype- params' = map (substPattern $ applySubst (`M.lookup` substs)) params+ (substs, t_shape_params) <- typeSubstsM loc bind_t t'+ let substs' = M.map Subst substs+ rettype' = substTypesAny (`M.lookup` substs') rettype+ substPatternType =+ substTypesAny (fmap (fmap fromStruct) . (`M.lookup` substs'))+ params' = map (substPattern entry substPatternType) params (params'', rrs) <- unzip <$> mapM expandRecordPattern params' mapM_ noticeDims $ rettype : map patternStructType params'' - body' <- updateExpTypes (`M.lookup` substs) body+ body' <- updateExpTypes (`M.lookup` M.map (fmap toStructural) substs') body body'' <- withRecordReplacements (mconcat rrs) $ transformExp body' name' <- if null tparams then return name else newName name- return (name', toValBinding name' params'' rettype' body'')+ return (name', toValBinding t_shape_params name' params'' rettype' body'') where shape_params = filter (not . isTypeParam) tparams @@ -503,45 +523,69 @@ , mapOnPatternType = pure . applySubst substs } - toValBinding name' params'' rettype' body'' =+ toValBinding t_shape_params name' params'' rettype' body'' = ValBind { valBindEntryPoint = False , valBindName = name' , valBindRetDecl = retdecl , valBindRetType = Info rettype'- , valBindTypeParams = shape_params+ , valBindTypeParams = shape_params ++ t_shape_params , valBindParams = params'' , valBindBody = body'' , valBindDoc = Nothing , valBindLocation = loc } -typeSubsts :: TypeBase () () -> TypeBase () ()- -> M.Map VName (TypeBase () ())-typeSubsts (Record fields1) (Record fields2) =- mconcat $ zipWith typeSubsts- (map snd $ sortFields fields1) (map snd $ sortFields fields2)-typeSubsts (TypeVar _ _ v _) t =- M.singleton (typeLeaf v) t-typeSubsts Prim{} Prim{} = mempty-typeSubsts (Arrow _ _ t1a t1b) (Arrow _ _ t2a t2b) =- typeSubsts t1a t2a <> typeSubsts t1b t2b-typeSubsts t1@Array{} t2@Array{}- | Just t1' <- peelArray (arrayRank t1) t1,- Just t2' <- peelArray (arrayRank t1) t2 =- typeSubsts t1' t2'-typeSubsts Enum{} Enum{} = mempty-typeSubsts t1 t2 = error $ unlines ["typeSubsts: mismatched types:", pretty t1, pretty t2]+-- Careful not to introduce size parameters for non-positive positions+-- (i.e. function parameters).+typeSubstsM :: MonadFreshNames m =>+ SrcLoc -> TypeBase () () -> TypeBase () ()+ -> m (M.Map VName StructType, [TypeParam])+typeSubstsM loc orig_t1 orig_t2 =+ let (t1_pts, t1_rt) = unfoldFunType orig_t1+ (t2_pts, t2_rt) = unfoldFunType orig_t2+ m = do zipWithM_ (sub True) t1_pts t2_pts+ sub False t1_rt t2_rt+ in runWriterT $ execStateT m mempty + where sub pos (TypeVar _ _ v _) t = addSubst pos v t+ sub pos (Record fields1) (Record fields2) =+ zipWithM_ (sub pos)+ (map snd $ sortFields fields1) (map snd $ sortFields fields2)+ sub _ Prim{} Prim{} = return ()+ sub _ Enum{} Enum{} = return ()+ sub pos t1@Array{} t2@Array{}+ | Just t1' <- peelArray (arrayRank t1) t1,+ Just t2' <- peelArray (arrayRank t1) t2 =+ sub pos t1' t2'+ sub pos (Arrow _ _ t1a t1b) (Arrow _ _ t2a t2b) = do+ sub False t1a t2a+ sub pos t1b t2b++ sub _ t1 t2 = error $ unlines ["typeSubstsM: mismatched types:", pretty t1, pretty t2]++ addSubst pos (TypeName _ v) t = do+ exists <- gets $ M.member v+ unless exists $ do+ t' <- if pos+ then bitraverse onDim pure t+ else pure $ vacuousShapeAnnotations t+ modify $ M.insert v t'++ onDim () = do d <- lift $ lift $ newVName "d"+ tell [TypeParamDim d loc]+ return $ NamedDim $ qualName d+ -- | Perform a given substitution on the types in a pattern.-substPattern :: (PatternType -> PatternType) -> Pattern -> Pattern-substPattern f pat = case pat of- TuplePattern pats loc -> TuplePattern (map (substPattern f) pats) loc+substPattern :: Bool -> (PatternType -> PatternType) -> Pattern -> Pattern+substPattern entry f pat = case pat of+ TuplePattern pats loc -> TuplePattern (map (substPattern entry f) pats) loc RecordPattern fs loc -> RecordPattern (map substField fs) loc- where substField (n, p) = (n, substPattern f p)- PatternParens p loc -> PatternParens (substPattern f p) loc+ where substField (n, p) = (n, substPattern entry f p)+ PatternParens p loc -> PatternParens (substPattern entry f p) loc Id vn (Info tp) loc -> Id vn (Info $ f tp) loc Wildcard (Info tp) loc -> Wildcard (Info $ f tp) loc- PatternAscription p td loc -> PatternAscription (substPattern f p) td loc+ PatternAscription p td loc | entry -> PatternAscription (substPattern False f p) td loc+ | otherwise -> substPattern False f p PatternLit e (Info tp) loc -> PatternLit e (Info $ f tp) loc toPolyBinding :: ValBind -> PolyBinding@@ -549,27 +593,26 @@ PolyBinding mempty (name, tparams, params, retdecl, rettype, body, loc) -- | Remove all type variables and type abbreviations from a value binding.-removeTypeVariables :: ValBind -> MonoM ValBind-removeTypeVariables valbind@(ValBind _ _ _ (Info rettype) _ pats body _ _) = do+removeTypeVariables :: Bool -> ValBind -> MonoM ValBind+removeTypeVariables entry valbind@(ValBind _ _ _ (Info rettype) _ pats body _ _) = do subs <- asks $ M.map TypeSub . envTypeBindings- let substPatternType = fromStruct . substituteTypes subs . toStruct- mapper = ASTMapper {+ let mapper = ASTMapper { mapOnExp = astMap mapper , mapOnName = pure , mapOnQualName = pure , mapOnType = pure . removeShapeAnnotations . substituteTypes subs . vacuousShapeAnnotations- , mapOnCompType = pure . fromStruct . removeShapeAnnotations .+ , mapOnCompType = pure . removeShapeAnnotations . substituteTypes subs .- vacuousShapeAnnotations . toStruct+ vacuousShapeAnnotations , mapOnStructType = pure . substituteTypes subs- , mapOnPatternType = pure . substPatternType+ , mapOnPatternType = pure . substituteTypes subs } body' <- astMap mapper body return valbind { valBindRetType = Info $ substituteTypes subs rettype- , valBindParams = map (substPattern substPatternType) pats+ , valBindParams = map (substPattern entry $ substituteTypes subs) pats , valBindBody = body' } @@ -580,12 +623,12 @@ transformValBind :: ValBind -> MonoM Env transformValBind valbind = do- valbind' <- toPolyBinding <$> removeTypeVariables valbind+ valbind' <- toPolyBinding <$> removeTypeVariables (valBindEntryPoint valbind) valbind when (valBindEntryPoint valbind) $ do t <- removeTypeVariablesInType $ removeShapeAnnotations $ foldFunType (map patternStructType (valBindParams valbind)) $ unInfo $ valBindRetType valbind- (name, valbind'') <- monomorphizeBinding valbind' t+ (name, valbind'') <- monomorphizeBinding True valbind' t tell $ Seq.singleton (name, valbind'' { valBindEntryPoint = True}) addLifted (valBindName valbind) t name return mempty { envPolyBindings = M.singleton (valBindName valbind) valbind' }
src/Futhark/Optimise/CSE.hs view
@@ -47,8 +47,9 @@ import qualified Futhark.Representation.ExplicitMemory as ExplicitMemory import Futhark.Transform.Substitute import Futhark.Pass+import Futhark.Util (takeLast) --- | Perform CSE on every functioon in a program.+-- | Perform CSE on every function in a program. performCSE :: (Attributes lore, CanBeAliased (Op lore), CSEInOp (OpWithAliases (Op lore))) => Bool -> Pass lore lore@@ -61,22 +62,27 @@ Bool -> FunDef lore -> FunDef lore cseInFunDef cse_arrays fundec = fundec { funDefBody =- runReader (cseInBody $ funDefBody fundec) $ newCSEState cse_arrays+ runReader (cseInBody ds $ funDefBody fundec) $ newCSEState cse_arrays }+ where ds = map (diet . declExtTypeOf) $ funDefRetType fundec type CSEM lore = Reader (CSEState lore) cseInBody :: (Attributes lore, Aliased lore, CSEInOp (Op lore)) =>- Body lore -> CSEM lore (Body lore)-cseInBody (Body bodyattr bnds res) =- cseInStms (consumedInStms bnds res) (stmsToList bnds) $ do+ [Diet] -> Body lore -> CSEM lore (Body lore)+cseInBody ds (Body bodyattr bnds res) =+ cseInStms (res_cons <> consumedInStms bnds res) (stmsToList bnds) $ do CSEState (_, nsubsts) _ <- ask return $ Body bodyattr mempty $ substituteNames nsubsts res+ where res_cons = mconcat $ zipWith consumeResult ds $+ takeLast (length ds) res+ consumeResult Consume se = freeIn se+ consumeResult _ _ = mempty cseInLambda :: (Attributes lore, Aliased lore, CSEInOp (Op lore)) => Lambda lore -> CSEM lore (Lambda lore) cseInLambda lam = do- body' <- cseInBody $ lambdaBody lam+ body' <- cseInBody (map (const Observe) $ lambdaReturnType lam) $ lambdaBody lam return lam { lambdaBody = body' } cseInStms :: (Attributes lore, Aliased lore, CSEInOp (Op lore)) =>@@ -90,12 +96,17 @@ bnd'' <- mapM nestedCSE bnd' return $ Body bodyattr (stmsFromList bnd''<>bnds') es where nestedCSE bnd' = do- e <- mapExpM cse $ stmExp bnd'+ let ds = map patElemDiet $ patternValueElements $ stmPattern bnd'+ e <- mapExpM (cse ds) $ stmExp bnd' return bnd' { stmExp = e }- cse = identityMapper { mapOnBody = const cseInBody- , mapOnOp = cseInOp- } + cse ds = identityMapper { mapOnBody = const $ cseInBody ds+ , mapOnOp = cseInOp+ }++ patElemDiet pe | patElemName pe `S.member` consumed = Consume+ | otherwise = Observe+ cseInStm :: Attributes lore => Names -> Stm lore -> ([Stm lore] -> CSEM lore a)@@ -169,29 +180,34 @@ instance (Attributes lore, Aliased lore, CSEInOp (Op lore)) => CSEInOp (Kernel.Kernel lore) where cseInOp = subCSE . Kernel.mapKernelM- (Kernel.KernelMapper return cseInLambda cseInBody+ (Kernel.KernelMapper return cseInLambda+ (\b -> cseInBody (map (const Observe) $ bodyResult b) b) return return cseInKernelBody) cseInKernelBody :: (Attributes lore, Aliased lore, CSEInOp (Op lore)) => Kernel.KernelBody lore -> CSEM lore (Kernel.KernelBody lore) cseInKernelBody (Kernel.KernelBody bodyattr bnds res) = do- Body _ bnds' _ <- cseInBody $ Body bodyattr bnds []+ Body _ bnds' _ <- cseInBody (map (const Observe) res) $ Body bodyattr bnds [] return $ Kernel.KernelBody bodyattr bnds' res instance (Attributes lore, Aliased lore, CSEInOp (Op lore)) => CSEInOp (KernelExp.KernelExp lore) where cseInOp (KernelExp.Combine cspace ts active body) =- subCSE $ KernelExp.Combine cspace ts active <$> cseInBody body+ subCSE $ KernelExp.Combine cspace ts active <$>+ cseInBody (map (const Observe) ts) body cseInOp (KernelExp.GroupReduce w lam input) =- subCSE $ KernelExp.GroupReduce w <$> cseInLambda lam <*> pure input+ subCSE $ KernelExp.GroupReduce w <$>+ cseInLambda lam <*> pure input cseInOp (KernelExp.GroupStream w max_chunk lam nes arrs) =- subCSE $ KernelExp.GroupStream w max_chunk <$> cseInGroupStreamLambda lam <*> pure nes <*> pure arrs+ subCSE $ KernelExp.GroupStream w max_chunk <$>+ cseInGroupStreamLambda lam <*> pure nes <*> pure arrs cseInOp op = return op cseInGroupStreamLambda :: (Attributes lore, Aliased lore, CSEInOp (Op lore)) => KernelExp.GroupStreamLambda lore -> CSEM lore (KernelExp.GroupStreamLambda lore) cseInGroupStreamLambda lam = do- body' <- cseInBody $ KernelExp.groupStreamLambdaBody lam+ body' <- cseInBody (map (const Observe) $ KernelExp.groupStreamAccParams lam) $+ KernelExp.groupStreamLambdaBody lam return lam { KernelExp.groupStreamLambdaBody = body' }
src/Futhark/Optimise/InliningDeadFun.hs view
@@ -63,13 +63,14 @@ inlineInBody inlcallees (Body attr stms res) = Body attr stms' res where stms' = stmsFromList (concatMap inline $ stmsToList stms) - inline (Let pat _ (Apply fname args _ (safety,loc,locs)))+ inline (Let pat aux (Apply fname args _ (safety,loc,locs))) | fun:_ <- filter ((== fname) . funDefName) inlcallees = let param_stms = zipWith reshapeIfNecessary (map paramIdent $ funDefParams fun) (map fst args) body_stms = stmsToList $ addLocations safety (filter notNoLoc (loc:locs)) $ bodyStms $ funDefBody fun- res_stms = zipWith reshapeIfNecessary (patternIdents pat)- (bodyResult $ funDefBody fun)+ res_stms = map (certify $ stmAuxCerts aux) $+ zipWith reshapeIfNecessary (patternIdents pat) $+ bodyResult $ funDefBody fun in param_stms ++ body_stms ++ res_stms inline stm = [inlineInStm inlcallees stm]
− src/Futhark/Optimise/MemoryBlockMerging.hs
@@ -1,28 +0,0 @@--- | Merge memory blocks.-module Futhark.Optimise.MemoryBlockMerging- ( memoryBlockMergingCoalescing- , memoryBlockMergingReuse- ) where--import Futhark.Pass-import Futhark.Representation.ExplicitMemory (ExplicitMemory)--import Futhark.Optimise.MemoryBlockMerging.Coalescing (coalesceInProg)-import Futhark.Optimise.MemoryBlockMerging.Reuse (reuseInProg)----- | Apply the coalescing part of the memory block merging optimisation.-memoryBlockMergingCoalescing :: Pass ExplicitMemory ExplicitMemory-memoryBlockMergingCoalescing =- Pass- "Memory block merging (coalescing)"- "Coalesce the memory blocks of arrays"- coalesceInProg---- | Apply the reuse part of the memory block merging optimisation.-memoryBlockMergingReuse :: Pass ExplicitMemory ExplicitMemory-memoryBlockMergingReuse =- Pass- "Memory block merging (reuse)"- "Reuse the memory blocks of arrays"- reuseInProg
− src/Futhark/Optimise/MemoryBlockMerging/ActualVariables.hs
@@ -1,358 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}--- | Find the actual variables that need updating when a variable attribute--- needs updating. This is different than variable aliasing: Variable aliasing--- is a theoretical concept, while this module has the practical purpose of--- finding any extra variables that also need a change when a variable has a--- change of memory block.------ If and DoLoop statements have special requirements, as do some aliasing--- expressions. We don't want to (just) use the obvious statement variable;--- sometimes updating the memory block of one variable actually means updating--- the memory block of other variables as well.--module Futhark.Optimise.MemoryBlockMerging.ActualVariables- ( findActualVariables- ) where--import qualified Data.Set as S-import qualified Data.Map.Strict as M-import qualified Data.List as L-import Data.Maybe (fromMaybe, mapMaybe, catMaybes)-import Control.Monad-import Control.Monad.RWS--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (- ExplicitMemorish, ExplicitMemory, InKernel)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous-import Futhark.Optimise.MemoryBlockMerging.Types-import Futhark.Optimise.MemoryBlockMerging.AllExpVars---data Context = Context- { ctxVarToMem :: VarMemMappings MemorySrc- , ctxFirstUses :: FirstUses- }- deriving (Show)--newtype FindM lore a = FindM { unFindM :: RWS Context () ActualVariables a }- deriving (Monad, Functor, Applicative,- MonadReader Context,- MonadState ActualVariables)--type LoreConstraints lore = (ExplicitMemorish lore,- FullWalk lore,- LookInKernelExp lore)--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM--recordActuals :: VName -> Names -> FindM lore ()-recordActuals stmt_var more_actuals = do- -- If S.empty has already been recorded, keep it at that. This is because the- -- ActualVariables system is currently also used for disabling memory block- -- optimisations -- if a variables resolves to the empty set, don't touch it.- -- This keeps some edge cases simple. FIXME at some point.- current_actuals <- M.lookup stmt_var <$> get- case S.null <$> current_actuals of- Just True -> return ()- _ -> modify (insertOrUpdateMany stmt_var more_actuals)---- Find all the actual variables in a function definition.-findActualVariables :: VarMemMappings MemorySrc -> FirstUses ->- FunDef ExplicitMemory -> ActualVariables-findActualVariables var_mem_mappings first_uses fundef =- let context = Context var_mem_mappings first_uses- m = unFindM $ lookInBody $ funDefBody fundef- actual_variables = fst $ execRWS m context M.empty- in actual_variables--lookInFParam :: FParam lore -> FindM lore ()-lookInFParam (Param v _) =- recordActuals v $ S.singleton v--lookInLParam :: LParam lore -> FindM lore ()-lookInLParam (Param v _) =- recordActuals v $ S.singleton v--lookInLambda :: LoreConstraints lore => Lambda lore -> FindM lore ()-lookInLambda (Lambda params body _) = do- forM_ params lookInLParam- lookInBody body--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm stm@(Let (Pattern patctxelems patvalelems) _ e) = do- case (patvalelems, e) of- ([PatElem var _], BasicOp (Update orig _ _)) -> do- let actuals = S.fromList [var, orig]- -- When coalescing an in-place update statement, also look at the original- -- array.- recordActuals var actuals- -- When reusing a previous memory block, make sure to also update related- -- in-place updates.- recordActuals orig actuals- _ -> return ()-- -- Ignore the existential memory blocks.- let bodyResult' = drop (length patctxelems) . bodyResult-- -- Special handling of loops, ifs, etc.- case e of- DoLoop _mergectxparams mergevalparams loopform body -> do- let body_vars0 = mapMaybe (subExpVar . snd) mergevalparams- body_vars1 = map (paramName . fst) mergevalparams- body_vars2 = S.toList $ findAllExpVars e- body_vars = body_vars0 ++ body_vars1 ++ body_vars2- forM_ patvalelems $ \(PatElem var membound) -> do- case membound of- ExpMem.MemArray _ _ _ (ExpMem.ArrayIn mem _) -> do- -- If mem is existential, we need to find the return memory that it- -- refers to. We cannot just look at its memory aliases, since it- -- likely aliases both the initial memory and the final memory.-- let zipped = zip patctxelems (bodyResult body)- mem_search = case L.find ((== mem) . patElemName . fst) zipped of- Just (_, Var res_mem) -> res_mem- _ -> mem- -- Find the ones using the same memory as the result of the loop- -- expression.- body_vars' <- filterM (lookupGivesMem mem_search) body_vars- -- Not only the result variable needs to change its memory block in- -- case of a future memory merging with it; also the variables- -- extracted above.- let actuals = var : body_vars'- forM_ actuals $ \a -> recordActuals a (S.fromList actuals)- -- Some of these can be changed later on to have an actual variable- -- set of S.empty, e.g. if one of the variables using the memory is- -- a rearrange operation. This is fine, and will occur in the walk- -- later on.-- -- If you extend this loop handling, make sure not to target existential- -- memory blocks. We want those to stay.- _ -> return ()-- -- It seems wrong to change the memory of merge variables, so we disable- -- it. If we were to accept it, we would need to record what other- -- variables to change as well. Seems hard.- recordActuals var S.empty-- case loopform of- ForLoop _ _ _ loop_vars ->- -- Link 'array' to 'lvar' in 'for lvar in array' loop expressions.- forM_ loop_vars $ \(Param lvar _, array) ->- aliasOpHandleVar array lvar- WhileLoop _ -> return ()-- If _se body_then body_else _types ->- -- We don't want to coalesce the existiential memory block of the if.- -- However, if a branch result has a memory block that is firstly used- -- inside the branch, it is okay to coalesce that in a future statement.- forM_ (zip3 patvalelems (bodyResult' body_then) (bodyResult' body_else))- $ \(PatElem var membound, res_then, res_else) -> do- let body_vars = S.toList $ findAllExpVars e- case membound of- ExpMem.MemArray _ _ _ (ExpMem.ArrayIn mem _) ->- if mem `L.elem` map patElemName patctxelems- then- -- If the memory block is existential, we say that the If result- -- refers to all results in the If.- recordActuals var- $ S.fromList (var : catMaybes [subExpVar res_then, subExpVar res_else])-- else do- -- If the memory block is not existential, we need to find all the- -- variables in any sub-bodies using the same memory block (like- -- with loops).- body_vars' <- filterM (lookupGivesMem mem) body_vars-- first_uses <- asks ctxFirstUses- case filter ((mem `S.member`) . (`lookupEmptyable` first_uses)) body_vars' of- [] ->- -- Not just the result variable needs to change its memory- -- block in case of a future memory block merging with it;- -- also the variables extracted above.- recordActuals var $ S.fromList (var : body_vars')- _ ->- -- If we come across a non-existential If which can be said to- -- create a new array *and* which has one or more bodies which- -- can also be said to create a new array *in the same memory*- -- (i.e. has first memory uses), then we disable it. This is- -- not at all an impossible case to handle, but such an If is- -- weird, since it would make more sense if it had existential- -- memory, so maybe something needs to be done somewhere else- -- in the compiler? If this is naively enabled, we can get an- -- error because the sub-body results are first uses while the- -- main result is not. This can be "fixed" by stating that- -- the If as a whole is also a first use of the memory, but- -- this seems too conservative. FIXME.- forM_ (var : body_vars') $ \v -> recordActuals v S.empty-- _ -> return ()-- BasicOp (Index orig _) -> do- let ielem = head patvalelems -- Should be okay.- var = patElemName ielem- case patElemAttr ielem of- ExpMem.MemArray{} ->- -- Disable merging for index expressions that return arrays. Maybe- -- too restrictive. Make sure the source also updates the memory of- -- the index when updated. The array might be an aliasing operation,- -- in which case we try to find the original array.- aliasOpHandleVar orig var- _ -> return ()-- -- Support reusing the memory of reshape operations by recording the origin- -- array that is being reshaped. Only partial support for reshape- -- operations: If the shape is more than one-dimensional, mark the statement- -- as disabled for memory merging operations.- BasicOp (Reshape shapechange_var orig) ->- forM_ (map patElemName patvalelems) $ \var -> do- orig' <- aliasOpRoot' orig- mem_orig <- M.lookup orig' <$> asks ctxVarToMem- case (shapechange_var, mem_orig) of- ([_], Just (MemorySrc _ _ (Shape [_]))) ->- recordActuals var $ S.fromList [var, orig]- -- Works, but only in limited cases where the reshape is not even- -- that useful to begin with; mostly cases where a reshape was- -- inserted by the compiler in an assert-like manner.- _ ->- recordActuals var S.empty- -- FIXME: The problem with these more complex cases with more than- -- one dimension is that a slice is relative to the shape of the- -- reshaped array, and not the original array. Disabled for now.- recordActuals orig' $ S.fromList [orig', var]-- -- For the other aliasing operations, disable their use for now. If the- -- source has a change of memory block, make sure to change this as well.- BasicOp (Rearrange _ orig) ->- aliasOpHandle orig patvalelems-- BasicOp (Rotate _ orig) ->- aliasOpHandle orig patvalelems-- BasicOp (Opaque (Var orig)) ->- aliasOpHandle orig patvalelems-- _ -> forM_ patvalelems $ \(PatElem var membound) -> do- let body_vars = S.toList $ findAllExpVars e- case membound of- ExpMem.MemArray _ _ _ (ExpMem.ArrayIn mem _) -> do- body_vars' <- filterM (lookupGivesMem mem) body_vars- recordActuals var $ S.fromList (var : body_vars')- _ -> return ()-- -- If we are inside a kernel, check for actual variables in the KernelExp of- -- the statement.- lookInKernelExp stm-- -- Recurse over any sub-bodies.- fullWalkExpM walker walker_kernel e- where walker = identityWalker- { walkOnBody = lookInBody- , walkOnFParam = lookInFParam- , walkOnLParam = lookInLParam- }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInLambda- , walkOnKernelLParam = lookInLParam- }---- If we have a rotate or similar, we want to find the original array and--- associate *that* with this aliasing array, so that changes to the original--- array will affect this one as well.-aliasOpHandle :: VName -> [PatElem lore] -> FindM lore ()-aliasOpHandle orig patvalelems =- forM_ (map patElemName patvalelems) $ aliasOpHandleVar orig--aliasOpHandleVar :: VName -> VName -> FindM lore ()-aliasOpHandleVar orig var = do- recordActuals var S.empty- orig' <- aliasOpRoot' orig- recordActuals orig' $ S.fromList [orig', var]--aliasOpRoot :: VName -> FindM lore (Maybe VName)-aliasOpRoot orig = do- current_actuals <- get- return $ case S.null <$> M.lookup orig current_actuals of- -- If the original array is itself an aliasing operation, find the *actual*- -- original array. There can be more than one reference. We just pick the- -- first one -- any one should do, since there is a transitive closure- -- calculation later on.- Just True -> case M.keys (M.filter (orig `S.member`) current_actuals) of- orig' : _ -> Just orig'- _ -> Nothing- -- Else, just return orig.- _ -> Just orig--aliasOpRoot' :: VName -> FindM lore VName-aliasOpRoot' orig =- fromJust ("at some point there will have been a proper statement: "- ++ pretty orig) <$> aliasOpRoot orig---- Is the memory block of 'v' the same as 'mem'?-lookupGivesMem :: MName -> VName -> FindM lore Bool-lookupGivesMem mem v = do- m <- M.lookup v <$> asks ctxVarToMem- return (Just mem == (memSrcName <$> m))--class LookInKernelExp lore where- -- Find actual vars in 'KernelExp's.- lookInKernelExp :: Stm lore -> FindM lore ()--instance LookInKernelExp ExplicitMemory where- lookInKernelExp (Let (Pattern _ patvalelems) _ e) = case e of- Op (ExpMem.Inner (Kernel _ _ _ (KernelBody _ _ ress))) ->- zipWithM_ (\(PatElem var _) res -> case res of- WriteReturn _ arr _ ->- recordActuals arr $ S.singleton var- _ -> return ()- ) patvalelems ress- _ -> return ()--instance LookInKernelExp InKernel where- lookInKernelExp (Let _ _ e) = case e of- Op (ExpMem.Inner ke) -> case ke of- ExpMem.GroupReduce _ _ input -> do- let arrs = map snd input- extendActualVarsInKernel e arrs- ExpMem.GroupScan _ _ input -> do- let arrs = map snd input- extendActualVarsInKernel e arrs- ExpMem.GroupStream _ _ _ _ arrs ->- extendActualVarsInKernel e arrs- _ -> return ()- _ -> return ()---- Record actual variables for input arrays to 'KernelExp's.-extendActualVarsInKernel :: Exp InKernel -> [VName] -> FindM InKernel ()-extendActualVarsInKernel e arrs = forM_ arrs $ \var -> do- -- The array might be an aliasing operation, in which case we try to find the- -- original array.- var' <- fromMaybe var <$> aliasOpRoot var- varmem <- M.lookup var <$> asks ctxVarToMem- case varmem of- Just mem -> do- let body_vars = findAllExpVars e- body_vars' <- filterSetM (lookupGivesMem $ memSrcName mem) body_vars- let actuals = S.insert var' body_vars'- recordActuals var' actuals- Nothing -> return ()
− src/Futhark/Optimise/MemoryBlockMerging/AllExpVars.hs
@@ -1,96 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE LambdaCase #-}--- | Find all variables in a statement.-module Futhark.Optimise.MemoryBlockMerging.AllExpVars- ( findAllExpVars- ) where--import qualified Data.Set as S-import Control.Monad-import Control.Monad.Writer--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (ExplicitMemorish)-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous---newtype FindM lore a = FindM { unFindM :: Writer Names a }- deriving (Monad, Functor, Applicative,- MonadWriter Names)--type LoreConstraints lore = (ExplicitMemorish lore,- FullWalk lore)--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM---- Find all the variables (both free and bound) that occur in a statement and--- any nested bodies. We use this to record which extra variables need to have--- their memory blocks updated when some variable needs updating. The result--- might be an empty set, but in the case of If, DoLoop, and kernels, the result--- might be nonempty. We cannot just find all variables in the program and look--- through them every time we need to, since a memory block can (at least in--- theory) be present in two different places (which also means by two different--- variable sets) in a program, so we should limit ourselves to looking in the--- statement declaring a new current use of the memory.-findAllExpVars :: LoreConstraints lore =>- Exp lore -> Names-findAllExpVars e =- let m = unFindM $ lookInExp e- in execWriter m--lookInExp :: LoreConstraints lore =>- Exp lore -> FindM lore ()-lookInExp = fullWalkExpM walker walker_kernel- where walker = identityWalker- { walkOnBody = lookInBody- , walkOnFParam = lookInFParam- , walkOnLParam = lookInLParam- }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInLambda- , walkOnKernelLParam = lookInLParam- }--lookInFParam :: FParam lore -> FindM lore ()-lookInFParam (Param x _) =- tell $ S.singleton x--lookInLParam :: LParam lore -> FindM lore ()-lookInLParam (Param x _) =- tell $ S.singleton x--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds res) = do- mapM_ lookInStm bnds- forM_ res $ \case- ThreadsReturn{} -> return ()- WriteReturn _ arr _ -> tell $ S.singleton arr- ConcatReturns{} -> return ()- KernelInPlaceReturn v -> tell $ S.singleton v--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm (Let (Pattern _ patvalelems) _ e) = do- forM_ patvalelems $ \(PatElem x _) ->- tell $ S.singleton x- lookInExp e--lookInLambda :: LoreConstraints lore =>- Lambda lore -> FindM lore ()-lookInLambda (Lambda params body _) = do- forM_ params lookInLParam- lookInBody body
− src/Futhark/Optimise/MemoryBlockMerging/AuxiliaryInfo.hs
@@ -1,63 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}--- | Helper information for the main optimisation passes.-module Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo- ( AuxiliaryInfo(..), getAuxiliaryInfo)-where--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (ExplicitMemory)--import Futhark.Optimise.MemoryBlockMerging.Types--import Futhark.Optimise.MemoryBlockMerging.VariableMemory-import Futhark.Optimise.MemoryBlockMerging.MemoryAliases-import Futhark.Optimise.MemoryBlockMerging.VariableAliases-import Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse-import Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse-import Futhark.Optimise.MemoryBlockMerging.Liveness.Interference-import Futhark.Optimise.MemoryBlockMerging.ActualVariables-import Futhark.Optimise.MemoryBlockMerging.Existentials---- Information needed by multiple transformations.-data AuxiliaryInfo = AuxiliaryInfo- { auxName :: Name -- For debugging.- , auxVarMemMappings :: VarMemMappings MemorySrc- , auxMemAliases :: MemAliases- , auxVarAliases :: VarAliases- , auxFirstUses :: FirstUses- , auxLastUses :: LastUses- , auxInterferences :: Interferences- , auxPotentialKernelDataRaceInterferences- :: PotentialKernelDataRaceInterferences- , auxActualVariables :: ActualVariables- , auxExistentials :: Names- }- deriving (Show)--getAuxiliaryInfo :: FunDef ExplicitMemory -> AuxiliaryInfo-getAuxiliaryInfo fundef =- let name = funDefName fundef- var_to_mem = findVarMemMappings fundef- mem_aliases = findMemAliases fundef var_to_mem- var_aliases = findVarAliases fundef- first_uses = findFirstUses var_to_mem mem_aliases fundef- last_uses = findLastUses var_to_mem mem_aliases first_uses existentials- fundef- (interferences, potential_kernel_interferences) =- findInterferences var_to_mem mem_aliases first_uses last_uses- existentials fundef- actual_variables = findActualVariables var_to_mem first_uses fundef- existentials = findExistentials fundef- in AuxiliaryInfo- { auxName = name- , auxVarMemMappings = var_to_mem- , auxMemAliases = mem_aliases- , auxVarAliases = var_aliases- , auxFirstUses = first_uses- , auxLastUses = last_uses- , auxInterferences = interferences- , auxPotentialKernelDataRaceInterferences = potential_kernel_interferences- , auxActualVariables = actual_variables- , auxExistentials = existentials- }
− src/Futhark/Optimise/MemoryBlockMerging/Coalescing.hs
@@ -1,31 +0,0 @@--- | Coalesce the memory blocks of arrays.------ Enable by setting the environment variable MEMORY_BLOCK_MERGING_COALESCING=1.-module Futhark.Optimise.MemoryBlockMerging.Coalescing- ( coalesceInProg- ) where--import Futhark.Pass--import Futhark.MonadFreshNames-import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (ExplicitMemory)--import Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo-import Futhark.Optimise.MemoryBlockMerging.Coalescing.AllocationMovingUp-import Futhark.Optimise.MemoryBlockMerging.Coalescing.Core---coalesceInProg :: Prog ExplicitMemory -> PassM (Prog ExplicitMemory)-coalesceInProg = intraproceduralTransformation coalesceInFunDef--coalesceInFunDef :: MonadFreshNames m- => FunDef ExplicitMemory- -> m (FunDef ExplicitMemory)-coalesceInFunDef fundef0 = do- let fundef1 = moveUpAllocsFunDef fundef0- aux1 = getAuxiliaryInfo fundef1- coreCoalesceFunDef fundef1- (auxVarMemMappings aux1) (auxMemAliases aux1)- (auxVarAliases aux1) (auxFirstUses aux1) (auxLastUses aux1)- (auxActualVariables aux1) (auxExistentials aux1)
− src/Futhark/Optimise/MemoryBlockMerging/Coalescing/AllocationMovingUp.hs
@@ -1,94 +0,0 @@--- | Move allocation statements upwards in the bodies of a program to enable--- more memory block coalescings.------ This should be run *before* the coalescing pass, as it enables more--- optimisations.-module Futhark.Optimise.MemoryBlockMerging.Coalescing.AllocationMovingUp- ( moveUpAllocsFunDef- ) where--import qualified Data.Set as S-import Data.Maybe (mapMaybe)--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (ExplicitMemory)-import qualified Futhark.Representation.ExplicitMemory as ExpMem--import Futhark.Optimise.MemoryBlockMerging.CrudeMovingUp---findAllocHoistees :: Body ExplicitMemory -> Maybe [FParam ExplicitMemory]- -> [VName]-findAllocHoistees body params =- let all_found = mapMaybe findThemStm stms- ++ maybe [] (mapMaybe findThemFParam) params- extras = concatMap snd all_found- allocs = map fst all_found- -- We must hoist the alloc expressions in the end. If we hoist an alloc- -- before we hoist one of its array creations (in case of in-place- -- updates), that array creation might in turn hoist something depending- -- on another memory block mem_y further up than the allocation of memory- -- block mem_x. This will become a problem if mem_y can get coalesced- -- into mem_x.- --- -- Maybe there is a nicer way to guarantee that this does not happen, but- -- this seems to work for now.- --- -- We reverse the non-alloc dependencies to ensure (sloppily) that they do- -- not change positions internally compared to the original program: For- -- example, if a statement x is located before a statement y, and both x- -- and y need to be hoisted, then we need to hoist x in the end, so that- -- it can be hoisted further than y, which might have been hoisted to- -- before x. A better solution is welcome!- in reverse extras ++ reverse allocs-- where stms :: [Stm ExplicitMemory]- stms = stmsToList $ bodyStms body-- findThemStm :: Stm ExplicitMemory -> Maybe (VName, [VName])- findThemStm (Let (Pattern _ [PatElem xmem _]) _ (Op ExpMem.Alloc{})) =- usedByCopyOrConcat xmem- findThemStm _ = Nothing-- -- A function paramater can be a unique memory block. While we cannot- -- hoist that, we may have to hoist an index in an in-place update that- -- uses the memory.- findThemFParam :: FParam ExplicitMemory -> Maybe (VName, [VName])- findThemFParam (Param xmem ExpMem.MemMem{}) = usedByCopyOrConcat xmem- findThemFParam _ = Nothing-- -- Is the allocated memory used by either Copy or Concat in the function- -- body? Those are the only kinds of memory we care about, since those- -- are the cases handled by coalescing. Also find the names used by- -- in-place updates, since those also need to be hoisted (as an example- -- of this, consider the 'copy/pos1.fut' test where the replicate- -- expression needs to be hoisted as well as its memory allocation).- usedByCopyOrConcat :: VName -> Maybe (VName, [VName])- usedByCopyOrConcat xmem_alloc =- let vs = mapMaybe checkStm stms- vs' = if null vs then Nothing else Just (xmem_alloc, concat vs)- in vs'-- where checkStm :: Stm ExplicitMemory -> Maybe [VName]- checkStm (Let- (Pattern _- [PatElem _ (ExpMem.MemArray _ _ _ (ExpMem.ArrayIn xmem_pat _))])- _- (BasicOp bop))- | xmem_pat == xmem_alloc =- case bop of- Update v slice _ ->- -- The source array must also be hoisted so that it- -- is initialized before it is used by the- -- coalesced party. Any index variables are also- -- hoisted.- Just $ v : S.toList (freeIn slice)- Copy{} -> Just []- Concat{} -> Just []- _ -> Nothing- checkStm _ = Nothing--moveUpAllocsFunDef :: FunDef ExplicitMemory- -> FunDef ExplicitMemory-moveUpAllocsFunDef fundef =- moveUpInFunDef fundef findAllocHoistees
− src/Futhark/Optimise/MemoryBlockMerging/Coalescing/Core.hs
@@ -1,624 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}-module Futhark.Optimise.MemoryBlockMerging.Coalescing.Core- ( coreCoalesceFunDef- ) where--import qualified Data.Set as S-import qualified Data.List as L-import qualified Data.Map.Strict as M-import Data.Maybe (maybe, fromMaybe, mapMaybe, isJust)-import Control.Monad-import Control.Monad.RWS--import Futhark.MonadFreshNames-import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (- ExplicitMemory, ExplicitMemorish)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel-import qualified Futhark.Representation.ExplicitMemory.IndexFunction as IxFun-import Futhark.Tools--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous-import Futhark.Optimise.MemoryBlockMerging.Types-import Futhark.Optimise.MemoryBlockMerging.MemoryUpdater--import Futhark.Optimise.MemoryBlockMerging.PrimExps (findPrimExpsFunDef)-import Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps-import Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2-import Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3-import Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5-import Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes----- Some of these attributes could be split into separate Coalescing helper--- modules if it becomes confusing. Their computations are fairly independent.-data Current = Current- { -- Coalescings state. Also save offsets and slices in the case that an- -- optimistic coalescing later becomes part of a chain of coalescings, where- -- it is offset yet again, and where it should maintain its old relative- -- offset. FIXME: This works, but is inefficient in the long run, as we- -- need to update it whenever we come across a coalescing that also affects- -- previous coalescings. The directions of the coalescings is inherently- -- bottom-up, but our algorithm is top-down. It should be possible to- -- rewrite it.- curCoalescedIntos :: CoalescedIntos- , curMemsCoalesced :: MemsCoalesced- }- deriving (Show)--type CoalescedIntos = M.Map VName (S.Set (VName, PrimExp VName,- [Slice (PrimExp VName)]))-type MemsCoalesced = M.Map VName MemoryLoc--emptyCurrent :: Current-emptyCurrent = Current- { curCoalescedIntos = M.empty- , curMemsCoalesced = M.empty- }--data Context = Context- { ctxFunDef :: FunDef ExplicitMemory- -- ^ Keep the entire function definition around for lookup purposes.- , ctxVarToMem :: VarMemMappings MemorySrc- -- ^ From the module VariableMemory.- , ctxMemAliases :: MemAliases- -- ^ From the module MemoryAliases.- , ctxVarAliases :: VarAliases- -- ^ From the module VariableAliases.- , ctxFirstUses :: FirstUses- -- ^ From the module FirstUses.- , ctxLastUses :: LastUses- -- ^ From the module LastUses.- , ctxActualVars :: M.Map VName Names- -- ^ From the module ActualVariables.- , ctxExistentials :: Names- -- ^ From the module Existentials.- , ctxVarPrimExps :: M.Map VName (PrimExp VName)- -- ^ From the module PrimExps.- , ctxVarExps :: M.Map VName Exp'- -- ^ Statement-name-to-expression mappins for the entire function.- , ctxAllocatedBlocksBeforeCreation :: M.Map VName MNames- -- ^ Safety condition 2.- , ctxVarsInUseBeforeMem :: M.Map MName Names- -- ^ Safety condition 5.- , ctxCurSnapshot :: Current- -- ^ Keep a snapshot (used in 'tryCoalesce' for Concat).- }- deriving (Show)--newtype FindM lore a = FindM { unFindM :: RWS Context () Current a }- deriving (Monad, Functor, Applicative,- MonadReader Context,- MonadState Current)--type LoreConstraints lore = (ExplicitMemorish lore,- FullWalk lore)--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM--modifyCurCoalescedIntos :: (CoalescedIntos -> CoalescedIntos) -> FindM lore ()-modifyCurCoalescedIntos f =- modify $ \c -> c { curCoalescedIntos = f $ curCoalescedIntos c }--modifyCurMemsCoalesced :: (MemsCoalesced -> MemsCoalesced) -> FindM lore ()-modifyCurMemsCoalesced f =- modify $ \c -> c { curMemsCoalesced = f $ curMemsCoalesced c }--ifExp :: MonadReader Context m =>- VName -> m (Maybe Exp')-ifExp var = do- var_exp <- M.lookup var <$> asks ctxVarExps- return $ case var_exp of- Just e@(Exp _ _ If{}) -> Just e- _ -> Nothing--isIfExp :: MonadReader Context m =>- VName -> m Bool-isIfExp var = isJust <$> ifExp var--isLoopExp :: MonadReader Context m =>- VName -> m Bool-isLoopExp var = do- var_exp <- M.lookup var <$> asks ctxVarExps- return $ case var_exp of- Just (Exp _ _ DoLoop{}) -> True- _ -> False--isReshapeExp :: MonadReader Context m =>- VName -> m Bool-isReshapeExp var = do- var_exp <- M.lookup var <$> asks ctxVarExps- return $ case var_exp of- Just (Exp _ _ (BasicOp Reshape{})) -> True- _ -> False---- Lookup the memory block statically associated with a variable.-lookupVarMem :: MonadReader Context m =>- VName -> m MemorySrc-lookupVarMem var =- -- This should always be called from a place where it is certain that 'var'- -- refers to a statement with an array expression.- fromJust ("lookup memory block from " ++ pretty var) . M.lookup var- <$> asks ctxVarToMem--lookupActualVars :: MonadReader Context m =>- VName -> m Names-lookupActualVars var = do- actual_vars <- asks ctxActualVars- -- Do this recursively.- let actual_vars' = expandWithAliases actual_vars actual_vars- return $ fromMaybe (S.singleton var) $ M.lookup var actual_vars'---- Lookup the memory block currenty associated with a variable. In most cases--- (maybe all) this could probably replace 'lookupVarMem', though it would not--- always be necessary.-lookupCurrentVarMem :: VName -> FindM lore (Maybe VName)-lookupCurrentVarMem var = do- -- Current result...- mem_cur <- M.lookup var . curMemsCoalesced <$> asks ctxCurSnapshot- -- ... or original result.- --- -- This is why we save the variables after creation, not the memory- -- blocks: Variables stay the same, but memory blocks may change, which- -- is relevant in the case of a chain of coalescings.- mem_orig <- M.lookup var <$> asks ctxVarToMem- return $ case (mem_cur, mem_orig) of- (Just m, _) -> Just (memLocName m) -- priority choice- (_, Just m) -> Just (memSrcName m)- _ -> Nothing--withMemAliases :: MonadReader Context m =>- VName -> m Names-withMemAliases mem =- -- The only memory blocks with memory aliases are the existiential ones, so- -- using a static ctxMemAliases should be okay, as they will not change during- -- the transformation in this module.- S.union (S.singleton mem) . lookupEmptyable mem- <$> asks ctxMemAliases--data Bindage = BindInPlace VName (Slice SubExp)- | BindVar--recordOptimisticCoalescing :: VName -> PrimExp VName- -> [Slice (PrimExp VName)]- -> VName -> MemoryLoc -> Bindage -> FindM lore ()-recordOptimisticCoalescing src offset ixfun_slices dst dst_memloc bindage = do- modifyCurCoalescedIntos $ insertOrUpdate dst (src, offset, ixfun_slices)-- -- If this is an in-place operation, we future-proof future coalescings by- -- recording that they also need to take a look at the original array, not- -- just the result of an in-place update into it.- case bindage of- BindVar -> return ()- BindInPlace orig _ ->- modifyCurCoalescedIntos $ insertOrUpdate dst (orig, zeroOffset, [])-- modifyCurMemsCoalesced $ M.insert src dst_memloc--coreCoalesceFunDef :: MonadFreshNames m =>- FunDef ExplicitMemory -> VarMemMappings MemorySrc- -> MemAliases -> VarAliases -> FirstUses -> LastUses- -> ActualVariables -> Names -> m (FunDef ExplicitMemory)-coreCoalesceFunDef fundef var_to_mem mem_aliases var_aliases first_uses- last_uses actual_vars existentials = do- let primexps = findPrimExpsFunDef fundef- exps = findExpsFunDef fundef- cond2 = findSafetyCondition2FunDef fundef- cond5 = findSafetyCondition5FunDef fundef first_uses- context = Context { ctxFunDef = fundef- , ctxVarToMem = var_to_mem- , ctxMemAliases = mem_aliases- , ctxVarAliases = var_aliases- , ctxFirstUses = first_uses- , ctxLastUses = last_uses- , ctxActualVars = actual_vars- , ctxExistentials = existentials- , ctxVarPrimExps = primexps- , ctxVarExps = exps- , ctxAllocatedBlocksBeforeCreation = cond2- , ctxVarsInUseBeforeMem = cond5- , ctxCurSnapshot = emptyCurrent- }- m = unFindM $ lookInBody $ funDefBody fundef- var_to_mem_res = curMemsCoalesced $ fst $ execRWS m context emptyCurrent- sizes = memBlockSizesFunDef fundef- transformFromVarMemMappings var_to_mem_res (M.map memSrcName var_to_mem) (M.map fst sizes) (M.map fst sizes) False fundef--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--zeroOffset :: PrimExp VName-zeroOffset = primExpFromSubExp (IntType Int32) (constant (0 :: Int32))--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm (Let (Pattern _patctxelems patvalelems) _ e) = do- -- COALESCING-SPECIFIC HANDLING for Copy and Concat.- case patvalelems of- [PatElem dst ExpMem.MemArray{}] -> do- -- We create a function and pass it around instead of just applying it to- -- the memory of the MemBound. We do this, since any source variables- -- might have more actual variables with different index functions that- -- also need to be fixed -- e.g. in the case of reshape, where both the- -- reshaped array and the original array need to get their index functions- -- updated.- --- -- We take a snapshot of the current state of the curCoalescedIntos state- -- field. We need this feature to avoid having fewer coalescings just- -- because of the placement of the sources. For example, for- --- -- let b = ...- -- let a = ...- -- let c = concat a b- --- -- the coalescing pass will first coalesce m_a into m_c, which will- -- succeed. Then it will to coalesce m_b into m_c, which will (naively)- -- fail because of safety condition 3 arguing that m_c is now in use after- -- the creation of 'b' and before its use, since 'a' now uses m_c.- --- -- (Alternatively, we could do some more general index function analysis- -- to check for things that will never overlap in merged memory, but this- -- seems easier.)- cur_snapshot <- get- var_to_mem <- asks ctxVarToMem- local (\ctx -> ctx { ctxCurSnapshot = cur_snapshot })- $ case e of- -- In-place update.- BasicOp (Update orig slice (Var src)) ->- case M.lookup src var_to_mem of- Just _ ->- let ixfun_slices =- let slice' = map (primExpFromSubExp (IntType Int32) <$>) slice- in [slice']- bindage = BindInPlace orig slice- in tryCoalesce dst ixfun_slices bindage src zeroOffset- Nothing ->- return ()-- -- Copy.- BasicOp (Copy src) ->- tryCoalesce dst [] BindVar src zeroOffset-- -- Concat.- BasicOp (Concat 0 src0 src0s _) -> do- let srcs = src0 : src0s- shapes <- mapM ((memSrcShape <$>) . lookupVarMem) srcs- let getOffsets offset_prev shape =- let se = head (shapeDims shape) -- Should work.- len = primExpFromSubExp (IntType Int32) se- offset_new = offset_prev + len- in offset_new- offsets = init (scanl getOffsets zeroOffset shapes)- zipWithM_ (tryCoalesce dst [] BindVar) srcs offsets- _ -> return ()- _ -> return ()--- -- RECURSIVE BODY WALK.- fullWalkExpM walker walker_kernel e- where walker = identityWalker- { walkOnBody = lookInBody }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInBody . lambdaBody- }--tryCoalesce :: VName -> [Slice (PrimExp VName)] -> Bindage ->- VName -> PrimExp VName -> FindM lore ()-tryCoalesce dst ixfun_slices bindage src offset = do- mem_dst <- lookupVarMem dst-- -- For ifs and loops and some aliasing expressions (e.g. reshape), this tells- -- us what non-existential source variables actually need to have assigned the- -- new memory block.- src's <- S.toList <$> lookupActualVars src-- -- From earlier optimistic coalescings. Remember to also get the coalescings- -- from the actual variables in e.g. loops.- coalesced_intos <- curCoalescedIntos <$> asks ctxCurSnapshot- let (src0s, offset0s, ixfun_slice0ss) =- unzip3 $ S.toList $ S.unions- $ map (`lookupEmptyable` coalesced_intos) (src : src's)-- var_to_pe <- asks ctxVarPrimExps-- let srcs = src's ++ src0s- -- The same number of base offsets as in src's.- offsets = replicate (length src's) offset- -- The offsets of any previously optimistically coalesced src0s must be- -- re-offset relative to the offset of the newest coalescing.- ++ map (\o0 -> if o0 == zeroOffset && offset == zeroOffset- -- This should not be necessary, and maybe it- -- is not (but there were some problems).- then zeroOffset- else offset + o0) offset0s- ixfun_slicess = replicate (length src's) ixfun_slices- -- Same as above, kind of.- ++ map (\slices0 -> ixfun_slices ++ slices0) ixfun_slice0ss-- let ixfuns' = zipWith (\offset_local islices ->- let ixfun0 = memSrcIxFun mem_dst- ixfun1 = foldl IxFun.slice ixfun0 islices-- -- 'ixfun_slices' contain the slices that are the- -- result of a new coalescing, contrary to the- -- slices in 'ixfun_slice0ss' which contain- -- previously registered slices.- -- 'offsetIndexDWIM' handles the case that we- -- want to offset a DimFix if it is the result of- -- a previous coalescing, and not the current- -- one. We do that by counting the number of- -- 'DimFix'es that originate in the new- -- coalescing, and then ignore those for our- -- heuristic. This is a hack.- initial_dimfixes = L.takeWhile (isJust . dimFix) (concat ixfun_slices)- ixfun2 = if offset_local == zeroOffset- then ixfun1 -- Should not be necessary,- -- but it makes the type- -- checker happy for now.- else IxFun.offsetIndexDWIM (length initial_dimfixes) ixfun1 offset_local- ixfun3 = expandIxFun var_to_pe ixfun2- in ixfun3- ) offsets ixfun_slicess-- -- Not everything supported yet. This dials back the optimisation on areas- -- where it fails.- existentials <- asks ctxExistentials- let currentlyDisabled src_local = do- -- This case covers the problem described in several programs in- -- tests/coalescing/wip/loop/ (for programs where it is overly- -- conservative) and tests/coalescing/loop/replicate-in-loop.fut (where- -- it is absolutely needed to keep the program correct). It is a- -- conservative requirement and could likely be loosened up.-- src_local_is_loop <- isLoopExp src_local-- -- if the source contains the result a loop expression, and that result- -- is an array with existential memory, don't coalesce. Since memory- -- can be allocated inside loops, coalescing with no further rules might- -- end up having the same arrays use memory allocated outside the loop,- -- which is not always okay.- let res = src_local_is_loop- && src_local `L.elem` existentials- return res-- safe0 <- not . or <$> mapM currentlyDisabled srcs-- -- Safety condition 1 is the same for all eventual previous arrays from srcs- -- that also need to be coalesced into dst, so we check it here instead of- -- checking it independently for every sub src. This also ensures that we- -- check that the destination memory is lastly used in *just* this statement,- -- not also in any previous statement that uses the same memory block, which- -- could very well fail.- mem_src_base <- lookupVarMem src- safe1 <- safetyCond1 dst mem_src_base-- when (safe0 && safe1) $ do- safes <- zipWithM (canBeCoalesced dst) srcs ixfuns'- when (and safes) $ do- -- Any previous src0s coalescings must be deleted.- modifyCurCoalescedIntos $ M.delete src- -- The rest will be overwritten below.-- -- We then need to record that, from what we currently know, src and any- -- nested src0s can all use the memory of dst with the new index functions.- forM_ (L.zip4 srcs offsets ixfun_slicess ixfuns')- $ \(src_local, offset_local, ixfun_slices_local, ixfun_local) -> do- denotes_existential <- S.member src_local <$> asks ctxExistentials- is_if <- isIfExp src_local- dst_memloc <-- if denotes_existential && not is_if- then do- -- Only use the new index function. Keep the existential memory- -- block. This means we have to make fewer changes to the program.- --- -- FIXME: However, if we are at an If expression with an existential- -- memory block, we ignore it. This is due to some special handling- -- of If in MemoryUpdater, which is again due to branches having- -- explicit return types. This might not be correct.- mem_src <- lookupVarMem src_local- return $ MemoryLoc (memSrcName mem_src) ixfun_local- else- -- Use both the new memory block and the new index function.- return $ MemoryLoc (memSrcName mem_dst) ixfun_local- recordOptimisticCoalescing- src_local offset_local ixfun_slices_local- dst dst_memloc bindage--canBeCoalesced :: VName -> VName -> ExpMem.IxFun -> FindM lore Bool-canBeCoalesced dst src ixfun = do- mem_dst <- lookupVarMem dst- mem_src <- lookupVarMem src-- safe2 <- safetyCond2 src mem_dst- safe3 <- safetyCond3 src dst mem_dst- safe4 <- safetyCond4 src- safe5 <- safetyCond5 mem_src ixfun-- safe_if <- safetyIf src dst-- let safe_all = safe2 && safe3 && safe4 && safe5 && safe_if- return safe_all---- Safety conditions for each statement with a Copy or Concat:------ 1. mem_src is not used beyond the statement. Handle by checking LastUses for--- the statement.------ 2. The allocation of mem_dst occurs before the creation of src, i.e. the--- first use of mem_src. Handle by checking--- ctxAllocatedBlocksBeforeCreation.------ 3. There is no use or creation of mem_dst after the creation of src and--- before the current statement. Handle by calling getVarUsesBetween and--- looking at both the original var-mem mappings *and* the new, temporary--- ones.------ 4. src (the variable, not the memory) does not alias anything. Handle by--- checking VarAliases.------ 5. The new index function of src only uses variables declared prior to the--- first use of mem_src. Handle by first using curVarPrimExps and--- ExpMem.substituteInIxFun to create a (possibly larger) index function that--- uses earlier variables. Then use ctxVarsInUseBeforeMem to check that all--- the variables in the new index function are available before the creation--- of mem_src.------ If an array src0 has been coalesced into mem_src, handle that by *also*--- checking src0 and mem_src0 where src and mem_src are checked. We choose to--- coalesce in a top-down fashion, even though that might exclude some potential--- coalescings -- however, doing it differently might exclude some other--- potentials, so we just make a choice.------ We only coalesce src into dst if all eventual src0 can also be coalesced into--- dst. It does not make sense to coalesce only part of them, since in that--- case both memory blocks and related allocations will still be around.--safetyCond1 :: MonadReader Context m =>- VName -> MemorySrc -> m Bool-safetyCond1 dst mem_src = do- last_uses <- lookupEmptyable (FromStm dst) <$> asks ctxLastUses- let res = S.member (memSrcName mem_src) last_uses- return res--safetyCond2 :: MonadReader Context m =>- VName -> MemorySrc -> m Bool-safetyCond2 src mem_dst = do- allocs_before_src <- lookupEmptyable src- <$> asks ctxAllocatedBlocksBeforeCreation- let res = S.member (memSrcName mem_dst) allocs_before_src- return res--safetyCond3 :: VName -> VName -> MemorySrc -> FindM lore Bool-safetyCond3 src dst mem_dst = do- fundef <- asks ctxFunDef- let uses_after_src_vars = S.toList $ getVarUsesBetween fundef src dst- uses_after_src <- mapM (maybe (return S.empty) withMemAliases- <=< lookupCurrentVarMem) uses_after_src_vars- return $ not $ S.member (memSrcName mem_dst) (S.unions uses_after_src)--safetyCond4 :: MonadReader Context m =>- VName -> m Bool-safetyCond4 src = do- -- Special If handling: An If can have aliases, but that can be okay and is- -- checked in safe If: It is okay for it to have one alias (one of the- -- branches), while two aliases are wrong.- if_handling <- isIfExp src-- -- Special Reshape handling: If a reshape has variables associated with it, it- -- is okay to use it.- src_actuals <- lookupEmptyable src <$> asks ctxActualVars- reshape_handling <- isReshapeExp src <&&> pure (not (S.null src_actuals))-- -- This needs to be extended if support for e.g. reshape coalescing is wanted:- -- Some operations can be aliasing, but still be okay to coalesce if you also- -- coalesce their aliased sources.- src_aliases <- lookupEmptyable src <$> asks ctxVarAliases- let res = if_handling || reshape_handling || S.null src_aliases- return res--safetyCond5 :: MonadReader Context m =>- MemorySrc -> ExpMem.IxFun -> m Bool-safetyCond5 mem_src ixfun = do- in_use_before_mem_src <- lookupEmptyable (memSrcName mem_src)- <$> asks ctxVarsInUseBeforeMem- let used_vars = freeIn ixfun- res = all (`S.member` in_use_before_mem_src) $ S.toList used_vars- return res--safetyIf :: VName -> VName -> FindM lore Bool-safetyIf src dst = do- -- Special handling: If src refers to an If expression, we need to check that- -- not just is mem_dst not used after src and before dst, but neither is any- -- other memory that will be merged after the coalescing. Normally this is- -- not an issue, since a coalescing means changing just one memory block --- -- but in the case of an If expression, each branch can have its own memory- -- block, and both of them will try to be coalesced. This extra test only- -- applies to the actual memory blocks in the branches, not any existential- -- memory block in the If, which in any case will be "used" in both branches.- --- -- See tests/coalescing/if/if-neg-3.fut for an example of where this should- -- fail.- mem_src <- lookupVarMem src- actual_srcs <- S.toList <$> lookupActualVars src- existentials <- asks ctxExistentials- var_to_mem <- asks ctxVarToMem- first_uses_all <- asks ctxFirstUses-- -- Find all variables that have 'src' as an actual var, and then check if one- -- of those is an If expression.- reverse_actual_srcs <-- S.toList . S.unions . M.elems . M.filter (src `S.member`)- <$> asks ctxActualVars- outer <- mapMaybeM ifExp reverse_actual_srcs- let (is_in_if,- if_branch_results_from_outer,- at_least_one_creation_inside) = case outer of- -- This is the if expression of which we are currently looking at one of- -- its branch results.- [Exp nctx nthpat (If _ body0 body1 _)] ->- let results_from_outer = S.fromList $ mapMaybe subExpVar- $ concatMap (drop nctx . bodyResult)- $ filter (null . bodyStms) [body0, body1]-- resultCreatedInside body se = fromMaybe False $ do- res <- subExpVar se- res_mem <- memSrcName <$> M.lookup res var_to_mem- let body_vars = concatMap (map patElemName . patternValueElements- . stmPattern) $ bodyStms body- body_first_uses = S.unions $ map (`lookupEmptyable` first_uses_all)- body_vars- return $ S.member res_mem body_first_uses-- at_least = resultCreatedInside body0 (bodyResult body0 !! (nctx + nthpat))- || resultCreatedInside body1 (bodyResult body1 !! (nctx + nthpat))- in (True, results_from_outer, at_least)- _ -> (False, S.empty, False)-- -- This success requirement is independent of whichever branch we are in right- -- now. We say that the results of an if-expression can be coalesced if the- -- branch-specific requirements hold *and* this general rule holds: Either the- -- If has no existentials (e.g. if it does in-place updates), or it has- -- existentials and at least one of the branches returns an array that was- -- created inside the branch.- let res_general = not is_in_if || (not (any (`S.member` existentials) actual_srcs)- || at_least_one_creation_inside)-- -- Check if the branch described by 'src' needs special handling.- let if_handling =- -- We are sure this is an if. This might not actually be necessary.- is_in_if- -- This does not refer to the result of a branch where the array is- -- created outside the if. It is a requirement that there is at most- -- one such branch. The extra safety here only relates to branches- -- whose result arrays are created inside.- && not (any (`S.member` if_branch_results_from_outer) actual_srcs)- -- Ignore existentials as well.- && not (src `S.member` existentials)-- -- This success requirement is part is specific to this branch.- res_current <-- if if_handling- then do- -- Get the memory used in the other branch. Use a reverse lookup.- mem_actual_srcs <- L.nub <$> mapM lookupVarMem reverse_actual_srcs- let mem_actual_srcs_cur = L.delete mem_src mem_actual_srcs- and <$> mapM (safetyCond3 src dst) mem_actual_srcs_cur- else return True-- -- The full result.- let res = res_general && res_current- return res
− src/Futhark/Optimise/MemoryBlockMerging/Coalescing/Exps.hs
@@ -1,70 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE ExistentialQuantification #-}--- | Get a mapping from statement patterns to statement expression for all--- statements.-module Futhark.Optimise.MemoryBlockMerging.Coalescing.Exps- ( Exp'(..)- , findExpsFunDef- ) where--import qualified Data.Map.Strict as M-import Control.Monad-import Control.Monad.Writer--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (ExplicitMemorish)-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous---- | Describes the nth pattern and the statement expression.-data Exp' = forall lore. Annotations lore => Exp Int Int (Exp lore)-instance Show Exp' where- show (Exp _nctxpatters _nthvalpattern e) = show e--type Exps = M.Map VName Exp'--newtype FindM lore a = FindM { unFindM :: Writer Exps a }- deriving (Monad, Functor, Applicative,- MonadWriter Exps)--type LoreConstraints lore = (ExplicitMemorish lore,- FullWalk lore)--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM--findExpsFunDef :: LoreConstraints lore =>- FunDef lore -> Exps-findExpsFunDef fundef =- let m = unFindM $ lookInBody $ funDefBody fundef- res = execWriter m- in res--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm (Let (Pattern patctxelems patvalelems) _ e) = do- forM_ (zip patvalelems [0..]) $ \(PatElem var _, i) ->- tell $ M.singleton var $ Exp (length patctxelems) i e-- -- Recursive body walk.- fullWalkExpM walker walker_kernel e- where walker = identityWalker- { walkOnBody = lookInBody }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- }
− src/Futhark/Optimise/MemoryBlockMerging/Coalescing/SafetyCondition2.hs
@@ -1,110 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}--- | Find safety condition 2 for all statements.-module Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition2- ( findSafetyCondition2FunDef- ) where--import qualified Data.Map.Strict as M-import qualified Data.Set as S-import Control.Monad-import Control.Monad.RWS--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (- ExplicitMemory, InKernel, ExplicitMemorish)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.Types-import Futhark.Optimise.MemoryBlockMerging.Miscellaneous---type CurrentAllocatedBlocks = MNames-type AllocatedBlocksBeforeCreation = M.Map VName MNames--newtype FindM lore a = FindM { unFindM :: RWS ()- AllocatedBlocksBeforeCreation CurrentAllocatedBlocks a }- deriving (Monad, Functor, Applicative,- MonadWriter AllocatedBlocksBeforeCreation,- MonadState CurrentAllocatedBlocks)--type LoreConstraints lore = (ExplicitMemorish lore,- IsAlloc lore,- FullWalk lore)--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM--findSafetyCondition2FunDef :: FunDef ExplicitMemory- -> AllocatedBlocksBeforeCreation-findSafetyCondition2FunDef fundef =- let m = unFindM $ do- forM_ (funDefParams fundef) lookInFParam- lookInBody $ funDefBody fundef- res = snd $ evalRWS m () S.empty- in res--lookInFParam :: FParam ExplicitMemory -> FindM lore ()-lookInFParam (Param _ membound) =- -- Unique array function parameters also count as "allocations" in which- -- memory can be coalesced.- case membound of- ExpMem.MemArray _ _ Unique (ExpMem.ArrayIn mem _) ->- modify $ S.insert mem- _ -> return ()--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm (Let (Pattern patctxelems patvalelems) _ e) = do- let new_decls0 = map patElemName (patctxelems ++ patvalelems)- new_decls1 = case e of- DoLoop _mergectxparams mergevalparams _loopform _body ->- -- Technically not a declaration for the current expression, but very- -- close, and hopefully okay to consider it as one.- map (paramName . fst) mergevalparams- _ -> []- new_decls = new_decls0 ++ new_decls1-- cur_allocated_blocks <- get- forM_ new_decls $ \x ->- tell $ M.singleton x cur_allocated_blocks-- case patvalelems of- [PatElem mem _] ->- when (isAlloc e) $ modify $ S.insert mem- _ -> return ()-- -- RECURSIVE BODY WALK.- fullWalkExpM walker walker_kernel e- where walker = identityWalker- { walkOnBody = lookInBody- , walkOnFParam = lookInFParam- }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInBody . lambdaBody- }--class IsAlloc lore where- isAlloc :: Exp lore -> Bool--instance IsAlloc ExplicitMemory where- isAlloc (Op ExpMem.Alloc{}) = True- isAlloc _ = False--instance IsAlloc InKernel where- isAlloc _ = False
− src/Futhark/Optimise/MemoryBlockMerging/Coalescing/SafetyCondition3.hs
@@ -1,136 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}--- | Safety condition 3 verification.-module Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition3- ( getVarUsesBetween- ) where--import qualified Data.Set as S-import qualified Data.List as L-import Control.Monad-import Control.Monad.RWS--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (- ExplicitMemory, ExplicitMemorish)-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous---data Context = Context- { ctxSource :: VName- , ctxDestination :: VName- }- deriving (Show)--data Current = Current- { curHasReachedSource :: Bool- , curHasReachedDestination :: Bool- , curVars :: Names- }- deriving (Show)--newtype FindM lore a = FindM { unFindM :: RWS Context () Current a }- deriving (Monad, Functor, Applicative,- MonadReader Context,- MonadState Current)--type LoreConstraints lore = (ExplicitMemorish lore,- FullWalk lore)--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM--modifyCurVars :: (Names -> Names) -> FindM lore ()-modifyCurVars f = modify $ \c -> c { curVars = f $ curVars c }---- Find all the variables present between the creations of two variables (not--- inclusive).-getVarUsesBetween :: FunDef ExplicitMemory- -> VName -> VName- -> Names-getVarUsesBetween fundef src dst =- let context = Context src dst- m = unFindM $ lookInBody $ funDefBody fundef- res = curVars $ fst $ execRWS m context (Current False False S.empty)- in res--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm stm@(Let _ _ e) = do- let new_decls = newDeclarationsStm stm-- dst <- asks ctxDestination- when (dst `L.elem` new_decls)- $ modify $ \c -> c { curHasReachedDestination = True }-- is_after_source <- gets curHasReachedSource- is_before_destination <- gets curHasReachedDestination-- unless is_before_destination $ do- let e_free_vars = freeInExp e- e_used_vars = S.union e_free_vars (S.fromList new_decls)-- -- If the source has been created, add the newly used variables.- --- -- Note that "used after creation" refers both to used in subsequent- -- statements AND any statements in any sub-bodies (if and loop).- when is_after_source- $ modifyCurVars $ S.union e_used_vars-- -- If the source is present in the declarations, state that it has been- -- created.- src <- asks ctxSource- when (src `L.elem` new_decls)- $ modify $ \c -> c { curHasReachedSource = True }-- -- RECURSIVE BODY WALK.- case e of- If _ body0 body1 _ -> do- -- This is not very If-specific, but rather specific to expressions with- -- multiple, independent bodies, where If is just the only such- -- expression.- --- -- We do not want the state (for safety condition 3) after traversing- -- the first branch to be present when traversing the second branch,- -- since they really will never both be run, so we compute them- -- independently and then merge them at the end.- before <- get- lookInBody body0- after0 <- get- put Current { curHasReachedSource = curHasReachedSource before- , curHasReachedDestination = curHasReachedDestination after0- , curVars = curVars before- }- lookInBody body1- after1 <- get- put Current { curHasReachedSource =- curHasReachedSource after0 || curHasReachedSource after1- , curHasReachedDestination =- curHasReachedDestination after0 || curHasReachedDestination after1- , curVars =- S.union (curVars after0) (curVars after1)- }- _ -> do- -- In the general case, just look through any 'Body' you can find. (This- -- is the case for loops.)- let walker = identityWalker { walkOnBody = lookInBody }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInBody . lambdaBody- }- fullWalkExpM walker walker_kernel e
− src/Futhark/Optimise/MemoryBlockMerging/Coalescing/SafetyCondition5.hs
@@ -1,120 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}--- | Find safety condition 5 for all statements.-module Futhark.Optimise.MemoryBlockMerging.Coalescing.SafetyCondition5- ( findSafetyCondition5FunDef- ) where--import qualified Data.Map.Strict as M-import qualified Data.Set as S-import Control.Monad-import Control.Monad.RWS--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (- InKernel, ExplicitMemory, ExplicitMemorish)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.Types-import Futhark.Optimise.MemoryBlockMerging.Miscellaneous---type DeclarationsSoFar = Names-type VarsInUseBeforeMem = M.Map MName Names--newtype FindM lore a = FindM { unFindM :: RWS FirstUses- VarsInUseBeforeMem DeclarationsSoFar a }- deriving (Monad, Functor, Applicative,- MonadReader FirstUses,- MonadWriter VarsInUseBeforeMem,- MonadState DeclarationsSoFar)--type LoreConstraints lore = (ExplicitMemorish lore,- ExtractKernelDefVars lore,- FullWalk lore)--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM--findSafetyCondition5FunDef :: FunDef ExplicitMemory -> FirstUses- -> VarsInUseBeforeMem-findSafetyCondition5FunDef fundef first_uses =- let m = unFindM $ do- forM_ (funDefParams fundef) lookInFParam- lookInBody $ funDefBody fundef- res = snd $ evalRWS m first_uses S.empty- in res--lookInFParam :: FParam lore -> FindM lore ()-lookInFParam (Param x _) =- modify $ S.insert x--lookInLParam :: LParam lore -> FindM lore ()-lookInLParam (Param x _) =- modify $ S.insert x--lookInBody :: LoreConstraints lore => Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore => KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--lookInStm :: LoreConstraints lore => Stm lore -> FindM lore ()-lookInStm stm@(Let _ _ e) = do- let new_decls = newDeclarationsStm stm-- first_uses <- ask- declarations_so_far <- get- forM_ (S.toList $ S.unions $ map (`lookupEmptyable` first_uses) new_decls) $ \mem ->- tell $ M.singleton mem declarations_so_far-- forM_ new_decls $ \x ->- modify $ S.insert x-- -- Special loop handling: Extract useful variables that are in use.- case e of- DoLoop _ _ loopform _ ->- case loopform of- ForLoop i _ _ _ -> modify $ S.insert i- WhileLoop c -> modify $ S.insert c- _ -> return ()-- modify $ S.union (extractKernelDefVars e)-- -- Recursive body walk.- fullWalkExpM walker walker_kernel e- where walker = identityWalker- { walkOnBody = lookInBody- , walkOnFParam = lookInFParam- , walkOnLParam = lookInLParam- }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInLambda- , walkOnKernelLParam = lookInLParam- }--lookInLambda :: LoreConstraints lore =>- Lambda lore -> FindM lore ()-lookInLambda (Lambda params body _) = do- forM_ params lookInLParam- lookInBody body--class ExtractKernelDefVars lore where- -- Extract variables from a kernel definition.- extractKernelDefVars :: Exp lore -> Names--instance ExtractKernelDefVars ExplicitMemory where- extractKernelDefVars (Op (ExpMem.Inner (Kernel _ kernelspace _ _))) =- S.fromList $ map ($ kernelspace)- [spaceGlobalId, spaceLocalId, spaceGroupId]- extractKernelDefVars _ = S.empty--instance ExtractKernelDefVars InKernel where- extractKernelDefVars _ = S.empty
− src/Futhark/Optimise/MemoryBlockMerging/CrudeMovingUp.hs
@@ -1,263 +0,0 @@--- | Move variables as much as possible upwards in a program.-module Futhark.Optimise.MemoryBlockMerging.CrudeMovingUp- ( moveUpInFunDef- ) where--import qualified Data.Set as S-import qualified Data.List as L-import qualified Data.Map.Strict as M-import Data.Maybe (mapMaybe)-import Control.Monad-import Control.Monad.RWS-import Control.Monad.Writer--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (ExplicitMemory)-import qualified Futhark.Representation.ExplicitMemory as ExpMem--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous--import Control.Monad.State-import Control.Monad.Identity---type Line = Int-data Origin = FromFParam- | FromLine Line (Exp ExplicitMemory)- deriving (Eq, Ord, Show)---- The dependencies and the location.-data PrimBinding = PrimBinding { pbFrees :: Names- , _pbConsumed :: Names- , pbOrigin :: Origin- }- deriving (Show)---- A mapping from names to PrimBinding. The key is a collection of names, since--- a statement can have multiple patterns.-type BindingMap = [(Names, PrimBinding)]---- | Call 'findHoistees' for every body, and then hoist every one of the found--- hoistees (variables).-moveUpInFunDef :: FunDef ExplicitMemory- -> (Body ExplicitMemory -> Maybe [FParam ExplicitMemory] -> [VName])- -> FunDef ExplicitMemory-moveUpInFunDef fundef findHoistees =- let scope_new = scopeOf fundef- bindingmap_cur = []- body' = hoistInBody scope_new bindingmap_cur- (Just (funDefParams fundef)) findHoistees (funDefBody fundef)- fundef' = fundef { funDefBody = body' }- in fundef'--lookupPrimBinding :: VName -> State BindingMap PrimBinding-lookupPrimBinding vname =- gets $ snd . fromJust (pretty vname ++ " was not found in BindingMap."- ++ " This should not happen!")- . L.find ((vname `S.member`) . fst)--namesDependingOn :: VName -> State BindingMap Names-namesDependingOn v =- gets $ S.unions . map fst . filter (\(_, pb) -> v `S.member` pbFrees pb)--scopeBindingMap :: (VName, NameInfo ExplicitMemory)- -> BindingMap-scopeBindingMap (x, _) = [(S.singleton x, PrimBinding S.empty S.empty FromFParam)]---- Find all variables bound in a KernelSpace.-boundInKernelSpace :: ExpMem.KernelSpace -> Names-boundInKernelSpace space =- -- This might do too much.- S.fromList ([ ExpMem.spaceGlobalId space- , ExpMem.spaceLocalId space- , ExpMem.spaceGroupId space]- ++ (case ExpMem.spaceStructure space of- ExpMem.FlatThreadSpace ts ->- map fst ts ++ mapMaybe (subExpVar . snd) ts- ExpMem.NestedThreadSpace ts ->- map (\(x, _, _, _) -> x) ts- ++ mapMaybe (subExpVar . (\(_, x, _, _) -> x)) ts- ++ map (\(_, _, x, _) -> x) ts- ++ mapMaybe (subExpVar . (\(_, _, _, x) -> x)) ts- ))---- FIXME: The results of this should maybe go in the core 'freeIn' function, or--- perhaps the ExplicitMemory module, instead of this arbitrary module.-boundInExpExtra :: Exp ExplicitMemory -> Names-boundInExpExtra = execWriter . inExp- where inExp :: Exp ExplicitMemory -> Writer Names ()- inExp e = case e of- Op (ExpMem.Inner (ExpMem.Kernel _ space _ _)) ->- tell $ boundInKernelSpace space- _ -> walkExpM walker e-- walker = identityWalker {- walkOnBody = mapM_ (inExp . stmExp) . bodyStms- }--bodyBindingMap :: [Stm ExplicitMemory] -> BindingMap-bodyBindingMap stms =- concatMap createBindingStmt $ zip [0..] stms- -- We do not need to run this recursively on any sub-bodies, since this will- -- be run for every call to hoistInBody, which *does* run recursively on- -- sub-bodies.-- where createBindingStmt :: (Line, Stm ExplicitMemory)- -> BindingMap- createBindingStmt (line, stmt@(Let (Pattern patctxelems patvalelems) _ e)) =- let stmt_vars = S.fromList (map patElemName (patctxelems ++ patvalelems))- frees = freeInStm stmt- consumed = case e of BasicOp (Update src _ _) -> S.singleton src- _ -> mempty- bound_extra = boundInExpExtra e- frees' = frees `S.difference` bound_extra- vars_binding = (stmt_vars, PrimBinding frees' consumed (FromLine line e))-- -- Some variables exist only in a shape declaration.- shape_sizes = S.fromList $ concatMap shapeSizes (patctxelems ++ patvalelems)- sizes_binding = (shape_sizes, PrimBinding frees' consumed (FromLine line e))-- -- Some expressions contain special identifiers that are used in a- -- body. This should go somewhere else than here.- param_vars = case e of- Op (ExpMem.Inner (ExpMem.Kernel _ space _ _)) ->- boundInKernelSpace space- _ -> S.empty- params_binding = (param_vars, PrimBinding S.empty S.empty FromFParam)-- bmap = [vars_binding, sizes_binding, params_binding]- in bmap-- shapeSizes (PatElem _ (ExpMem.MemArray _ shape _ _)) =- mapMaybe subExpVar $ shapeDims shape- shapeSizes _ = []--hoistInBody :: Scope ExplicitMemory- -> BindingMap- -> Maybe [FParam ExplicitMemory]- -> (Body ExplicitMemory -> Maybe [FParam ExplicitMemory] -> [VName])- -> Body ExplicitMemory- -> Body ExplicitMemory-hoistInBody scope_new bindingmap_old params findHoistees body =- let hoistees = findHoistees body params-- -- We use the possibly non-empty scope to extend our BindingMap.- bindingmap_fromscope = concatMap scopeBindingMap $ M.toList scope_new- bindingmap_body = bodyBindingMap $ stmsToList $ bodyStms body- bindingmap = bindingmap_old ++ bindingmap_fromscope ++ bindingmap_body-- -- Create a new body where all hoistees have been moved as much upwards in- -- the statement list as possible.- (Body () bnds res, bindingmap') =- foldl (\(body0, lbindingmap) -> hoist lbindingmap body0)- (body, bindingmap) hoistees-- -- Touch upon any subbodies.- bnds' = fmap (hoistRecursivelyStm bindingmap' findHoistees) bnds- body' = Body () bnds' res-- in body'--hoistRecursivelyStm :: BindingMap- -> (Body ExplicitMemory -> Maybe [FParam ExplicitMemory] -> [VName])- -> Stm ExplicitMemory- -> Stm ExplicitMemory-hoistRecursivelyStm bindingmap findHoistees (Let pat aux e) =- runIdentity (Let pat aux <$> mapExpM transform e)-- where transform = identityMapper { mapOnBody = mapper }- mapper scope_new = return . hoistInBody scope_new bindingmap' Nothing findHoistees- -- The nested body cannot move to any of its locations of its parent's- -- body, so we say that all its parent's bindings are parameters.- bindingmap' = map (\(ns, PrimBinding frees consumed _) ->- (ns, PrimBinding frees consumed FromFParam))- bindingmap---- Hoist the statement denoted by 'hoistee' as much upwards as possible in--- 'body', and return the new body.-hoist :: BindingMap- -> Body ExplicitMemory- -> VName- -> (Body ExplicitMemory, BindingMap)-hoist bindingmap_cur body hoistee =- let bindingmap = bindingmap_cur <> bodyBindingMap (stmsToList $ bodyStms body)-- body' = runState (moveLetUpwards hoistee body) bindingmap-- in body'---- Move a statement as much up as possible.-moveLetUpwards :: VName -> Body ExplicitMemory- -> State BindingMap (Body ExplicitMemory)-moveLetUpwards letname body = do- PrimBinding deps consumed letorig <- lookupPrimBinding letname-- -- Extend the dependencies with all those statements that use the consumed- -- variables of this statement, except the current statement.- deps' <- S.delete letname- <$> (S.union deps- <$> (S.unions <$> mapM namesDependingOn (S.toList consumed)))-- case letorig of- FromFParam -> return body- FromLine line_cur exp_cur ->- case exp_cur of- -- We do not want to change the structure of the program too much, so we- -- restrict the aggressive hoister to *stop* and not hoist loops and- -- kernels, as hoisting these expressions might actually make a- -- hoisting-dependent optimisation *poorer* because of some assumptions- -- about the structure. FIXME: Do this nicer in a way where it is easy- -- to argue for it.- DoLoop{} -> return body- Op ExpMem.Inner{} -> return body- _ -> do- -- Sort by how close they are to the beginning of the body. The closest- -- one should be the first one to hoist, so that the other ones can maybe- -- exploit it.- deps'' <- sortByKeyM (fmap pbOrigin . lookupPrimBinding)- $ S.toList deps'- body' <- foldM (flip moveLetUpwards) body deps''- origins <- mapM (fmap pbOrigin . lookupPrimBinding) deps''- let line_dest = case foldl max FromFParam origins of- FromFParam -> 0- FromLine n _e -> n + 1-- PrimBinding _ _ letorig' <- lookupPrimBinding letname- when (letorig' /= letorig) $ error "Assertion: This should not happen."-- stms' <- moveLetToLine letname line_cur line_dest $ stmsToList $ bodyStms body'-- return body' { bodyStms = stmsFromList stms' }---- Both move the statement to the new line and update the BindingMap.-moveLetToLine :: VName -> Line -> Line -> [Stm ExplicitMemory]- -> State BindingMap [Stm ExplicitMemory]-moveLetToLine stm_cur_name line_cur line_dest stms- | line_cur == line_dest = return stms- | otherwise = do-- let stm_cur = stms !! line_cur- stms1 = take line_cur stms ++ drop (line_cur + 1) stms- stms2 = take line_dest stms1 ++ [stm_cur] ++ drop line_dest stms1-- modify $ map (\t@(ns, PrimBinding frees consumed orig) ->- case orig of- FromFParam -> t- FromLine l e -> if l >= line_dest && l < line_cur- then (ns, PrimBinding frees consumed- (FromLine (l + 1) e))- else t)-- r <- lookupPrimBinding stm_cur_name- case r of- PrimBinding frees consumed (FromLine _ exp_cur) ->- modify $ replaceWhere stm_cur_name (PrimBinding frees consumed- (FromLine line_dest exp_cur))- _ -> error "moveLetToLine: unhandled case" -- fixme- return stms2--replaceWhere :: VName -> PrimBinding -> BindingMap -> BindingMap-replaceWhere n pb1 =- map (\(ns, pb) -> (ns, if n `S.member` ns- then pb1- else pb))
− src/Futhark/Optimise/MemoryBlockMerging/Existentials.hs
@@ -1,80 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}--- | Find all existential variables.-module Futhark.Optimise.MemoryBlockMerging.Existentials- ( findExistentials- ) where--import qualified Data.Set as S-import qualified Data.List as L-import Control.Monad-import Control.Monad.Writer--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (ExplicitMemorish)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous---newtype FindM lore a = FindM { unFindM :: Writer Names a }- deriving (Monad, Functor, Applicative,- MonadWriter Names)--type LoreConstraints lore = (ExplicitMemorish lore,- FullWalk lore)--record :: VName -> FindM lore ()-record = tell . S.singleton--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM--findExistentials :: LoreConstraints lore =>- FunDef lore -> Names-findExistentials fundef =- let m = unFindM $ lookInBody $ funDefBody fundef- existentials = execWriter m- in existentials--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm (Let (Pattern patctxelems patvalelems) _ e) = do- forM_ patvalelems $ \(PatElem var membound) ->- case membound of- ExpMem.MemArray _ _ _ (ExpMem.ArrayIn mem _) ->- when (mem `L.elem` map patElemName patctxelems)- $ record var- _ -> return ()-- case e of- DoLoop mergectxparams mergevalparams _loopform _body ->- forM_ mergevalparams $ \(Param var membound, _) ->- case membound of- ExpMem.MemArray _ _ _ (ExpMem.ArrayIn mem _) ->- when (mem `L.elem` map (paramName . fst) mergectxparams)- $ record var- _ -> return ()- _ -> return ()-- fullWalkExpM walker walker_kernel e- where walker = identityWalker- { walkOnBody = lookInBody }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInBody . lambdaBody- }
− src/Futhark/Optimise/MemoryBlockMerging/Liveness/FirstUse.hs
@@ -1,198 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}--- | Find first uses for all memory blocks.------ Array creation points. Maps statements to memory block names.------ A memory block can have more than one first use.-module Futhark.Optimise.MemoryBlockMerging.Liveness.FirstUse- ( findFirstUses- , createsNewArrayBase- ) where--import qualified Data.Set as S-import qualified Data.Map.Strict as M-import Data.Maybe (fromMaybe)-import Control.Monad-import Control.Monad.RWS--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory- (ExplicitMemory, InKernel, ExplicitMemorish)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous-import Futhark.Optimise.MemoryBlockMerging.Types---data Context = Context- { ctxVarToMem :: VarMemMappings MemorySrc- , ctxMemAliases :: MemAliases- , ctxCurOuterFirstUses :: Names- -- ^ First uses found in outer bodies.- }- deriving (Show)--newtype FindM lore a = FindM { unFindM :: RWS Context () FirstUses a }- deriving (Monad, Functor, Applicative,- MonadReader Context,- MonadWriter (),- MonadState FirstUses)--type LoreConstraints lore = (ExplicitMemorish lore,- ArrayUtils lore,- FullWalk lore)--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM---- Find the memory blocks used or aliased by a variable.-varMems :: VName -> FindM lore MNames-varMems var = do- var_to_mem <- asks ctxVarToMem- mem_aliases <- asks ctxMemAliases- return $ fromMaybe S.empty $ do- mem <- memSrcName <$> M.lookup var var_to_mem- return $ S.union (S.singleton mem) $ lookupEmptyable mem mem_aliases--recordMapping :: VName -> MName -> FindM lore ()-recordMapping stmt_var mem =- modify $ M.unionWith S.union (M.singleton stmt_var $ S.singleton mem)---- | Find all first uses of *memory blocks* in a function definition.-findFirstUses :: VarMemMappings MemorySrc -> MemAliases- -> FunDef ExplicitMemory -> FirstUses-findFirstUses var_to_mem mem_aliases fundef =- let context = Context { ctxVarToMem = var_to_mem- , ctxMemAliases = mem_aliases- , ctxCurOuterFirstUses = S.empty- }- m = unFindM $ do- forM_ (funDefParams fundef) lookInFunDefFParam- lookInBody $ funDefBody fundef- first_uses = removeEmptyMaps $ expandWithAliases mem_aliases- $ fst $ execRWS m context M.empty- in first_uses--lookInFunDefFParam :: LoreConstraints lore =>- FParam lore -> FindM lore ()-lookInFunDefFParam (Param x (ExpMem.MemArray _ _ _ (ExpMem.ArrayIn xmem _))) =- recordMapping x xmem-lookInFunDefFParam _ = return ()--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm (Let (Pattern patctxelems patvalelems) _ e) = do- outer_first_uses <- asks ctxCurOuterFirstUses- when (createsNewArray e) $ do- let e_free_vars = freeInExp e- e_mems <- S.unions <$> mapM varMems (S.toList e_free_vars)- forM_ patvalelems $ \(PatElem x membound) ->- case membound of- ExpMem.MemArray _ _ _ (ExpMem.ArrayIn xmem _) -> do- x_mems <- varMems xmem-- -- For the first use to be a proper first use, it must write to- -- the memory, but not read from it. We need to check this to- -- support multiple liveness intervals. If we don't check this,- -- the last use analysis and the interference analysis might end- -- up wrong.- when (S.null $ S.intersection x_mems e_mems)- -- We only record the mapping between the statement and the- -- memory block, not any of its aliased memory blocks. They- -- would not be aliased unless they are themselves created at- -- some point, so they will get their own FirstUses. Putting- -- them into first use here would probably also be too- -- conservative.- --- -- If it is a first use of a memory inside a loop or a kernel, and- -- that memory already has a first use outside the loop, ignore it,- -- since it is not a proper first use. This can be an issue after- -- the coalescing transformation, where multidimensional maps are- -- first-order-transformed into nested loops, each loop having its- -- own Scratch expression. FIXME: This might be too conservative- -- for multiple liveness intervals, but it does not seem to be a- -- problem with our tests. It is quite possible that this case only- -- occurs because the coalescing pass does not remove the inner- -- scratches, so maybe it should be fixed there.- $ unless (xmem `S.member` outer_first_uses)- $ recordMapping x xmem- _ -> return ()-- -- Find first uses of existential memory blocks. Fairly conservative.- -- Covers the case where a loop uses multiple arrays by saying every- -- existential memory block overlaps with every result memory block. Fine- -- for now.- forM_ patctxelems- $ \p -> forM_ patvalelems- $ \el -> lookInPatCtxElem (patElemName el) p- case e of- DoLoop mergectxparams _mergevalparams _loopform _body ->- forM_ mergectxparams- $ \p -> forM_ patvalelems- $ \el -> lookInMergeCtxParam (patElemName el) p- _ -> return ()-- cur_first_uses <- get- local (\ctx -> ctx { ctxCurOuterFirstUses = S.unions $ M.elems cur_first_uses })- $ fullWalkExpM walker walker_kernel e- where walker = identityWalker- { walkOnBody = lookInBody }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInBody . lambdaBody- }--lookInPatCtxElem :: LoreConstraints lore =>- VName -> PatElem lore -> FindM lore ()-lookInPatCtxElem x (PatElem xmem ExpMem.MemMem{}) =- recordMapping x xmem-lookInPatCtxElem _ _ = return ()--lookInMergeCtxParam :: LoreConstraints lore =>- VName -> (FParam lore, SubExp) -> FindM lore ()-lookInMergeCtxParam x (Param xmem ExpMem.MemMem{}, _) =- recordMapping x xmem-lookInMergeCtxParam _ _ = return ()--class ArrayUtils lore where- -- Does an expression constitute a new array?- createsNewArray :: Exp lore -> Bool--createsNewArrayBase :: Exp lore -> Bool-createsNewArrayBase e = case e of- BasicOp Replicate{} -> True- BasicOp Iota{} -> True- BasicOp Manifest{} -> True- BasicOp Copy{} -> True- BasicOp Concat{} -> True- BasicOp ArrayLit{} -> True- BasicOp Scratch{} -> True- _ -> False--instance ArrayUtils ExplicitMemory where- createsNewArray e = case e of- Op (ExpMem.Inner ExpMem.Kernel{}) -> True- _ -> createsNewArrayBase e--instance ArrayUtils InKernel where- createsNewArray e = case e of- Op (ExpMem.Inner ExpMem.GroupReduce{}) -> True- Op (ExpMem.Inner ExpMem.GroupScan{}) -> True- Op (ExpMem.Inner ExpMem.GroupStream{}) -> True- Op (ExpMem.Inner ExpMem.Combine{}) -> True- _ -> createsNewArrayBase e
− src/Futhark/Optimise/MemoryBlockMerging/Liveness/Interference.hs
@@ -1,520 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE LambdaCase #-}--- | Find memory block interferences. Maps a memory block to its interference--- set.--module Futhark.Optimise.MemoryBlockMerging.Liveness.Interference- ( findInterferences- ) where--import qualified Data.Set as S-import qualified Data.Map.Strict as M-import qualified Data.List as L-import Data.Maybe (mapMaybe, fromMaybe, catMaybes)-import Control.Monad-import Control.Monad.RWS-import Control.Monad.Writer--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (- ExplicitMemorish, ExplicitMemory, InKernel)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous-import Futhark.Optimise.MemoryBlockMerging.Types---data Context = Context { ctxVarToMem :: VarMemMappings MemorySrc- , ctxMemAliases :: MemAliases- , ctxFirstUses :: FirstUses- , ctxLastUses :: LastUses- , ctxExistentials :: Names- , ctxLoopCorrespondingVar :: M.Map VName (VName, SubExp)- }- deriving (Show)--type InterferencesList = [(MName, MNames)]--getInterferencesMap :: InterferencesList -> Interferences-getInterferencesMap = M.unionsWith S.union . map (uncurry M.singleton)--data Current = Current { curAlive :: MNames-- , curResPotentialKernelInterferences- :: PotentialKernelDataRaceInterferences- }- deriving (Show)--newtype FindM lore a = FindM- { unFindM :: RWS Context InterferencesList Current a }- deriving (Monad, Functor, Applicative,- MonadReader Context,- MonadWriter InterferencesList,- MonadState Current)--type LoreConstraints lore = (ExplicitMemorish lore,- KernelInterferences lore,- SpecialBodyExceptions lore,- FullWalk lore)--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM--awaken :: MName -> FindM lore ()-awaken mem = modifyCurAlive $ S.insert mem--kill :: MName -> FindM lore ()-kill mem = modifyCurAlive $ S.delete mem--modifyCurAlive :: (MNames -> MNames) -> FindM lore ()-modifyCurAlive f = modify $ \c -> c { curAlive = f $ curAlive c }--addPotentialKernelInterferenceGroup ::- PotentialKernelDataRaceInterferenceGroup -> FindM lore ()-addPotentialKernelInterferenceGroup set =- modify $ \c -> c { curResPotentialKernelInterferences =- curResPotentialKernelInterferences c ++ [set] }--recordCurrentInterferences :: FindM lore ()-recordCurrentInterferences = do- current <- gets curAlive- -- Interferences are commutative. Reflect that in the resulting data.- forM_ (S.toList current) $ \mem ->- tell [(mem, current)]--recordNewInterferences :: MNames -> FindM lore ()-recordNewInterferences mems_in_stm = do- current <- gets curAlive- -- Interferences are commutative. Reflect that in the resulting data.- forM_ (S.toList current) $ \mem ->- tell [(mem, mems_in_stm)]- forM_ (S.toList mems_in_stm) $ \mem ->- tell [(mem, current)]---- | Find all memory block interferences in a function definition.-findInterferences :: VarMemMappings MemorySrc -> MemAliases ->- FirstUses -> LastUses -> Names -> FunDef ExplicitMemory- -> (Interferences, PotentialKernelDataRaceInterferences)-findInterferences var_to_mem mem_aliases first_uses last_uses existentials fundef =- let context = Context { ctxVarToMem = var_to_mem- , ctxMemAliases = mem_aliases- , ctxFirstUses = first_uses- , ctxLastUses = last_uses- , ctxExistentials = existentials- , ctxLoopCorrespondingVar = M.empty- }- m = unFindM $ do- forM_ (funDefParams fundef) lookInFunDefFParam- lookInBody $ funDefBody fundef- (cur, interferences_list) = execRWS m context (Current S.empty [])- interferences = removeEmptyMaps $ removeKeyFromMapElems $ makeCommutativeMap- $ getInterferencesMap interferences_list- potential_kernel_interferences = curResPotentialKernelInterferences cur- in (interferences, potential_kernel_interferences)--lookInFunDefFParam :: FParam lore -> FindM lore ()-lookInFunDefFParam (Param var _) = do- first_uses_var <- lookupEmptyable var <$> asks ctxFirstUses- mapM_ awaken $ S.toList first_uses_var- recordCurrentInterferences--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds res) = do- mapM_ lookInStm bnds- lookInRes res--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds res) = do- mapM_ lookInStm bnds- lookInRes $ map kernelResultSubExp res--awakenFirstUses :: [PatElem lore] -> FindM lore ()-awakenFirstUses patvalelems =- forM_ patvalelems $ \(PatElem var _) -> do- first_uses_var <- lookupEmptyable var <$> asks ctxFirstUses- mapM_ awaken $ S.toList first_uses_var--isNoOp :: Exp lore -> Bool-isNoOp (BasicOp bop) = case bop of- Scratch{} -> True- _ -> False-isNoOp _ = False--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm stm@(Let (Pattern _patctxelems patvalelems) _ e)- | isNoOp e =- awakenFirstUses patvalelems- -- There is no reason to record interferences if the current statement will- -- not generate any code in the end. We have this check to use the result- -- index sharing analysis on loop bodies and not get bogged down by the- -- result of a Scratch statement hanging around.- | otherwise = do- awakenFirstUses patvalelems- ctx <- ask- let ctx' = ctx { ctxLoopCorrespondingVar =- M.union (ctxLoopCorrespondingVar ctx)- (findLoopCorrespondingVar ctx stm)- }- let stm_exceptions = fromMaybe [] $ do- indices <- specialBodyIndices e- let walker_exc =- identityWalker- { walkOnBody = \body -> let (body', lcv) = innermostLoopNestBody ctx body- ctx'' = ctx' { ctxLoopCorrespondingVar =- M.union (ctxLoopCorrespondingVar ctx') lcv }- in tell $ interferenceExceptions ctx''- (bodyStms body') (bodyResult body')- indices Nothing }- walker_kernel_exc =- identityKernelWalker- { walkOnKernelBody = \body -> let (body', lcv) = innermostLoopNestBody ctx body- ctx'' = ctx' { ctxLoopCorrespondingVar =- M.union (ctxLoopCorrespondingVar ctx') lcv }- in tell $ interferenceExceptions ctx''- (bodyStms body') (bodyResult body')- indices Nothing- , walkOnKernelKernelBody = \kbody -> tell $ interferenceExceptions ctx'- (kernelBodyStms kbody)- (mapMaybe (\case- ThreadsReturn _ se -> Just se- _ -> Nothing)- $ kernelBodyResult kbody)- indices- (specialBodyWriteMems stm)- }- return $ execWriter $ fullWalkExpM walker_exc walker_kernel_exc e-- first_uses <- asks ctxFirstUses- last_uses <- asks ctxLastUses- let stm_mems =- S.unions $ map (\pelem ->- let v = patElemName pelem- in S.union- (lookupEmptyable v first_uses)- (lookupEmptyable (FromStm v) last_uses)) patvalelems-- ((), stm_interferences) <- censor (const []) $ listen $ do- recordNewInterferences stm_mems- local (const ctx') $ fullWalkExpM walker walker_kernel e- let stm_interferences' =- map (\(k, vs) ->- (k, S.fromList- $ filter (\v -> not ((k, v) `L.elem` stm_exceptions- || (v, k) `L.elem` stm_exceptions))- $ S.toList vs))- stm_interferences- tell stm_interferences'-- potential_kernel_interferences <- findKernelDataRaceInterferences e- forM_ potential_kernel_interferences addPotentialKernelInterferenceGroup-- forM_ patvalelems $ \(PatElem var _) -> do- last_uses_var <- lookupEmptyable (FromStm var) <$> asks ctxLastUses- mapM_ kill last_uses_var-- where walker = identityWalker- { walkOnBody = lookInBody }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInBody . lambdaBody- }---- For perfectly nested loops. Make it possible to find the index function for--- the outer loop.-findLoopCorrespondingVar :: LoreConstraints lore =>- Context -> Stm lore -> M.Map VName (VName, SubExp)-findLoopCorrespondingVar ctx (Let (Pattern _patctxelems patvalelems) _- (DoLoop _ _ _ (Body _ stms res))) =- M.fromList $ catMaybes $ zipWith findIt patvalelems res- where findIt (PatElem pat_v (ExpMem.MemArray _ _ _ (ExpMem.ArrayIn pat_mem _))) (Var res_v)- | not (null stms) = case L.last $ stmsToList stms of- -- This is how the program looks after coalescing.- Let (Pattern _ [PatElem _last_v- (ExpMem.MemArray _ _ _ (ExpMem.ArrayIn last_stm_mem _))]) _- (BasicOp (Update _ (DimFix slice_part : _) (Var copy_v))) ->- if pat_mem == last_stm_mem- then let res_v' =- if (memSrcName <$> M.lookup copy_v (ctxVarToMem ctx))- == Just last_stm_mem- then Just copy_v- else Just res_v- in res_v' >>= \t -> Just (t, (pat_v, slice_part))- -- Fix this mess.- else Nothing- _ -> Nothing- | otherwise = Nothing- findIt _ _ = Nothing-findLoopCorrespondingVar _ _ = M.empty--innermostLoopNestBody :: LoreConstraints lore =>- Context -> Body lore -> (Body lore, M.Map VName (VName, SubExp))-innermostLoopNestBody ctx body = case stmsToList $ bodyStms body of- -- This checks for how perfect nested loops looks like after coalescing. This- -- is very brittle. If it detects such a nesting, it will ask the- -- interference exception algorithm to look in the innermost body.- Let _ _ (BasicOp Scratch{}) : loopstm@(Let _ _ (DoLoop _ _ _ body')) : _ ->- let (body'', loop_corresponding_var) = innermostLoopNestBody ctx body'- in (body'', M.union- (findLoopCorrespondingVar ctx loopstm)- loop_corresponding_var)- _ -> (body, M.empty)--lookInRes :: [SubExp] -> FindM lore ()-lookInRes ses = do- let vs = subExpVars ses- last_uses <- asks ctxLastUses- let last_uses_v =- S.unions $ map (\v -> lookupEmptyable (FromRes v) last_uses) vs- recordNewInterferences last_uses_v- mapM_ kill $ S.toList last_uses_v--firstUsesInStm :: LoreConstraints lore => FirstUses ->- Stm lore -> [KernelFirstUse]-firstUsesInStm first_uses stm =- let m = lookFUInStm stm- in snd $ evalRWS m first_uses ()--firstUsesInExp :: LoreConstraints lore =>- Exp lore -> FindM lore [KernelFirstUse]-firstUsesInExp e = do- let m = lookFUInExp e- first_uses <- asks ctxFirstUses- return $ snd $ evalRWS m first_uses ()--lookFUInStm :: LoreConstraints lore =>- Stm lore -> RWS FirstUses [KernelFirstUse] () ()-lookFUInStm (Let (Pattern _patctxelems patvalelems) _ e_stm) = do- forM_ patvalelems $ \(PatElem patname membound) ->- case membound of- ExpMem.MemArray pt _ _ (ExpMem.ArrayIn _ ixfun) -> do- fus <- lookupEmptyable patname <$> ask- forM_ fus $ \fu -> tell [(fu, patname, pt, ixfun)]- _ -> return ()- lookFUInExp e_stm--lookFUInExp :: LoreConstraints lore =>- Exp lore -> RWS FirstUses [KernelFirstUse] () ()-lookFUInExp = fullWalkExpM fu_walker fu_walker_kernel- where fu_walker = identityWalker- { walkOnBody = mapM_ lookFUInStm . bodyStms }- fu_walker_kernel = identityKernelWalker- { walkOnKernelBody = mapM_ lookFUInStm . bodyStms- , walkOnKernelKernelBody = mapM_ lookFUInStm . kernelBodyStms- , walkOnKernelLambda = mapM_ lookFUInStm . bodyStms . lambdaBody- }--class KernelInterferences lore where- findKernelDataRaceInterferences ::- Exp lore -> FindM lore (Maybe PotentialKernelDataRaceInterferenceGroup)--instance KernelInterferences ExplicitMemory where- findKernelDataRaceInterferences e = case e of- Op (ExpMem.Inner Kernel{}) -> Just <$> firstUsesInExp e- _ -> return Nothing--instance KernelInterferences InKernel where- findKernelDataRaceInterferences _ = return Nothing---- Base info for kernel bodies.-class SpecialBodyExceptions lore where- specialBodyIndices :: Exp lore -> Maybe [MName]- specialBodyWriteMems :: Stm lore -> Maybe [(MName, ExpMem.IxFun, PrimType)]--instance SpecialBodyExceptions ExplicitMemory where- specialBodyIndices (Op (ExpMem.Inner (Kernel _ kernelspace _ _))) =- Just $ map fst $ spaceDimensions kernelspace- specialBodyIndices e = specialBodyIndicesBase e-- specialBodyWriteMems (Let (Pattern _patctxelems patvalelems) _- (Op (ExpMem.Inner Kernel{}))) =- Just $ mapMaybe (\p -> case patElemAttr p of- ExpMem.MemArray t _ _ (ExpMem.ArrayIn mem ixfun) -> Just (mem, ixfun, t)- _ -> Nothing) patvalelems- specialBodyWriteMems _ = Nothing--instance SpecialBodyExceptions InKernel where- specialBodyIndices = specialBodyIndicesBase- specialBodyWriteMems = const Nothing--specialBodyIndicesBase :: Exp lore -> Maybe [MName]-specialBodyIndicesBase (DoLoop _ _ (ForLoop i _ _ _) _) = Just [i]-specialBodyIndicesBase _ = Nothing---- Use first use analysis and last use analysis to find any exceptions to the--- naive interference recorded for a statement.-interferenceExceptions :: LoreConstraints lore =>- Context -> Stms lore -> [SubExp] -> [MName] ->- Maybe [(MName, ExpMem.IxFun, PrimType)] -> [(MName, MName)]-interferenceExceptions ctx stms res indices output_mems_may =- let output_vars = subExpVars res- indices_slice = map (DimFix . Var) indices- stms_first_uses = map (\(mem, _, _, _) -> mem)- $ concatMap (firstUsesInStm (ctxFirstUses ctx)) stms- results =- concat $ flip map (stmsToList stms) $ \(Let (Pattern _patctxelems patvalelems) _ e) ->- flip map patvalelems $ \(PatElem v membound) ->- let fromread = case e of- BasicOp (Index orig slice) -> do- orig_mem <- M.lookup orig $ ctxVarToMem ctx- if- -- These two extra requirements might be superfluous.- memSrcName orig_mem `L.notElem` stms_first_uses &&- not (memSrcName orig_mem `S.member` ctxExistentials ctx)- then return (v, typeOf membound, orig_mem, slice)- else Nothing- _ -> Nothing- fromwrite = case e of- BasicOp Update{}- | ExpMem.MemArray pt _ _ _ <- membound -> do- -- The coalescing pass can have created a program where some- -- dependencies are a bit indirect. We find the core index function.- let (orig', slice') =- fixpointIterateMay- (\(v0, ss0) -> do- (v1, s1) <- M.lookup v0 (ctxLoopCorrespondingVar ctx)- return (v1, DimFix s1 : ss0))- (v, [])-- orig_mem <- M.lookup orig' $ ctxVarToMem ctx- if- -- These two extra requirements might be superfluous.- memSrcName orig_mem `L.notElem` stms_first_uses &&- not (memSrcName orig_mem `S.member` ctxExistentials ctx)- then return (v, Prim pt, orig_mem, slice')- else Nothing- _ -> Nothing- in (fromread, fromwrite)- fromreads = mapMaybe fst results- fromwrites = mapMaybe snd results- fromwrites' = filter (\(v, _, _, _) -> v `L.elem` output_vars) fromwrites-- fus_input_vars = M.fromList $ map (\(v, _, mem, _) ->- (v, S.singleton $ memSrcName mem)) fromreads- lus_input_vars = mapFromListSetUnion $ mapMaybe- (\(v, typ, mem, _) ->- let check e_pat =- let frees = freeInExp e_pat-- -- We need to handle scalars and arrays differently: A last- -- use of a scalar variable is the definite last use of the- -- memory it represents, while the last use of an array can- -- be distorted by reshapes and other aliasing operations,- -- so in that case we need to find the last use of the- -- memory block.- b = case typ of- Prim _ ->- v `S.member` frees- _ ->- memSrcName mem `L.elem`- mapMaybe ((memSrcName <$>) . (`M.lookup` ctxVarToMem ctx))- (S.toList frees)-- in b- check' (Let _ _ e) = check e- in (\stm -> (FromStm $ patElemName $ head $ patternValueElements $ stmPattern stm,- S.singleton $ memSrcName mem)) <$>- L.find check' (reverse $ stmsToList stms)) fromreads-- -- 'Just' if in kernel, 'Nothing' otherwise.- fus_output_vars = mapFromListSetUnion $ case output_mems_may of- Just _ -> []- _ -> map (\(v, _, mem, _) -> (v, S.singleton $ memSrcName mem)) fromwrites'- fus_result = mapFromListSetUnion $ case output_mems_may of- Just mems -> zip output_vars $ map (S.singleton . (\(mem, _, _) -> mem)) mems- _ -> []-- -- Extended first uses and last uses.- fus = M.unionsWith S.union [ctxFirstUses ctx, fus_input_vars, fus_output_vars]- lus = M.unionsWith S.union [ctxLastUses ctx, lus_input_vars]-- -- Memory-to-slice mappings.- input_mem_slices = M.fromList $ map (\(_, _, mem, slice) ->- (memSrcName mem, slice)) fromreads- output_mem_slices = M.fromList $ case output_mems_may of- Just mems ->- map (\(mem, _, _) -> (mem, indices_slice)) mems- _ ->- map (\(_, _, mem, slice) -> (memSrcName mem, slice)) fromwrites'- mem_slices = M.union input_mem_slices output_mem_slices-- -- Memory-to-ixfun mappings.- input_mem_ixfuns = M.fromList $ map (\(_, _, mem, _) ->- (memSrcName mem, memSrcIxFun mem)) fromreads- output_mem_ixfuns = M.fromList $ case output_mems_may of- Just mems -> map (\(mem, ixfun, _) -> (mem, ixfun)) mems- _ -> map (\(_, _, mem, _) -> (memSrcName mem, memSrcIxFun mem)) fromwrites'- mem_ixfuns = M.union input_mem_ixfuns output_mem_ixfuns-- -- Memory-to-primtype-size mappings.- input_mem_primtypes = M.fromList- $ map (\(_, t, mem, _) -> (memSrcName mem, elemType t)) fromreads- output_mem_primtypes = M.fromList $ case output_mems_may of- Just mems -> map (\(mem, _, pt) -> (mem, pt)) mems- _ -> map (\(_, t, mem, _) -> (memSrcName mem, elemType t)) fromwrites'- mem_primtypes = M.union input_mem_primtypes output_mem_primtypes-- -- Separation of input memory blocks and output memory blocks.- mem_ins0 = S.fromList $ map (\(_, _, mem, _) -> memSrcName mem) fromreads- mem_outs0 = S.fromList $ case output_mems_may of- Just mems -> map (\(mem, _, _) -> mem) mems- _ -> map (\(_, _, mem, _) -> memSrcName mem) fromwrites'- -- An input memory must not be an output memory, and vice versa.- mem_ins = S.difference mem_ins0 mem_outs0- mem_outs = S.difference mem_outs0 mem_ins0-- exceptions = snd $ evalRWS (findExceptions fus fus_result lus- mem_ins mem_outs mem_slices mem_ixfuns- mem_primtypes output_vars) () S.empty- in exceptions-- where findExceptions :: FirstUses -> FirstUses -> LastUses -> Names -> Names ->- M.Map VName (Slice SubExp) -> M.Map VName ExpMem.IxFun ->- M.Map VName PrimType -> [VName] ->- RWS () [(VName, VName)] LocalDeaths ()- findExceptions fus fus_result lus mem_ins mem_outs mem_slices mem_ixfuns mem_primtypes output_vars = do- forM_ stms $ \(Let (Pattern _patctxelems patvalelems) _ _) -> do- let vs = map patElemName patvalelems- fus_stm = S.unions $ map (`lookupEmptyable` fus) vs- lus_stm = S.unions $ map ((`lookupEmptyable` lus) . FromStm) vs- recordNewExceptions mem_ins mem_outs mem_slices mem_ixfuns mem_primtypes fus_stm- modify $ S.union lus_stm- forM_ output_vars $ \ov -> do- let fus_ov = lookupEmptyable ov fus_result- recordNewExceptions mem_ins mem_outs mem_slices mem_ixfuns mem_primtypes fus_ov-- recordNewExceptions :: Names -> Names ->- M.Map VName (Slice SubExp) -> M.Map VName ExpMem.IxFun ->- M.Map VName PrimType -> Names ->- RWS () [(VName, VName)] LocalDeaths ()- recordNewExceptions mem_ins mem_outs mem_slices mem_ixfuns mem_primtypes fus_cur = do- deaths <- get- forM_ (S.toList fus_cur) $ \mem_fu -> forM_ deaths $ \mem_killed ->- fromMaybe (return ()) $ do- slice_fu <- M.lookup mem_fu mem_slices- slice_killed <- M.lookup mem_killed mem_slices- ixfun_fu <- M.lookup mem_fu mem_ixfuns- ixfun_killed <- M.lookup mem_killed mem_ixfuns- pt_fu <- M.lookup mem_fu mem_primtypes- pt_killed <- M.lookup mem_killed mem_primtypes- return $ when- ( -- Is the killed memory read from and the first use memory- -- written to?- mem_fu `S.member` mem_outs && mem_killed `S.member` mem_ins &&- -- Same index functions?- ixfun_fu == ixfun_killed && -- too conservative?- -- Same slices?- slice_fu == slice_killed &&- -- Same primitive type byte sizes?- (primByteSize pt_fu :: Int) == primByteSize pt_killed- ) $ tell [(mem_fu, mem_killed)]---- Memory blocks that have had their last use locally in the body.-type LocalDeaths = Names
− src/Futhark/Optimise/MemoryBlockMerging/Liveness/LastUse.hs
@@ -1,281 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}--- | Find last uses for all memory blocks.------ A memory block can have more than one last use.-module Futhark.Optimise.MemoryBlockMerging.Liveness.LastUse- ( findLastUses- ) where--import qualified Data.Set as S-import qualified Data.Map.Strict as M-import Control.Monad-import Control.Monad.RWS--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory- (ExplicitMemorish, ExplicitMemory)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous-import Futhark.Optimise.MemoryBlockMerging.Types---type LastUsesList = [LastUses]--getLastUsesMap :: LastUsesList -> LastUses-getLastUsesMap = M.unionsWith S.union---- Mapping from a memory block to its currently assumed last use statement--- variable.-type OptimisticLastUses = M.Map VName (StmOrRes, Bool)--data Context = Context- { ctxVarToMem :: VarMemMappings MemorySrc- , ctxMemAliases :: MemAliases- , ctxFirstUses :: FirstUses- , ctxExistentials :: Names- , ctxCurFirstUsesOuter :: Names- }- deriving (Show)--data Current = Current- { curOptimisticLastUses :: OptimisticLastUses- , curFirstUses :: Names- }- deriving (Show)--newtype FindM lore a = FindM { unFindM :: RWS Context LastUsesList Current a }- deriving (Monad, Functor, Applicative,- MonadReader Context,- MonadWriter LastUsesList,- MonadState Current)--type LoreConstraints lore = (ExplicitMemorish lore,- FullWalk lore)--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM---- Find the memory blocks used or aliased by a variable.-varMems :: VName -> FindM lore MNames-varMems var =- maybe S.empty (S.singleton . memSrcName) <$> asks (M.lookup var . ctxVarToMem)--modifyCurOptimisticLastUses :: (OptimisticLastUses -> OptimisticLastUses) -> FindM lore ()-modifyCurOptimisticLastUses f =- modify $ \c -> c { curOptimisticLastUses = f $ curOptimisticLastUses c }--modifyCurFirstUses :: (Names -> Names) -> FindM lore ()-modifyCurFirstUses f = modify $ \c -> c { curFirstUses = f $ curFirstUses c }--withLocalCurFirstUses :: FindM lore a -> FindM lore a-withLocalCurFirstUses m = do- cur_first_uses <- gets curFirstUses- res <- m- modifyCurFirstUses $ const cur_first_uses- return res--recordMapping :: StmOrRes -> MName -> FindM lore ()-recordMapping var mem = tell [M.singleton var (S.singleton mem)]---- | Find all last uses of *memory blocks* in a function definition.-findLastUses :: VarMemMappings MemorySrc -> MemAliases -> FirstUses -> Names- -> FunDef ExplicitMemory -> LastUses-findLastUses var_to_mem mem_aliases first_uses existentials fundef =- let context = Context- { ctxVarToMem = var_to_mem- , ctxMemAliases = mem_aliases- , ctxFirstUses = first_uses- , ctxExistentials = existentials- , ctxCurFirstUsesOuter = S.empty- }- m = unFindM $ do- forM_ (funDefParams fundef) lookInFunDefFParam- lookInBody $ funDefBody fundef- mapM_ lookInRes $ bodyResult $ funDefBody fundef- optimistics <- gets curOptimisticLastUses- forM_ (M.keys optimistics) $ \mem ->- commitOptimistic mem-- last_uses = removeEmptyMaps $ getLastUsesMap- $ snd $ evalRWS m context (Current M.empty S.empty)- in last_uses---- Optimistically say that the last use of 'mem' and all its memory aliases is--- at 'x_lu'. Exclude 'exclude' from the memory aliases (necessary in a few--- edge cases).-setOptimistic :: MName -> StmOrRes -> MNames -> FindM lore ()-setOptimistic mem x_lu exclude = do- -- Will override any previous optimistic last use.- mem_aliases <- asks ctxMemAliases- let mems = S.difference (S.union (S.singleton mem)- $ lookupEmptyable mem mem_aliases) exclude-- forM_ mems $ \mem' -> do- let is_indirect = mem' /= mem- modifyCurOptimisticLastUses $ M.insert mem' (x_lu, is_indirect)---- If an optimistic last use 'mem' was added through a memory alias, forget--- about it.-removeIndirectOptimistic :: MName -> FindM lore ()-removeIndirectOptimistic mem = do- res <- M.lookup mem <$> gets curOptimisticLastUses- case res of- Just (_, True) -> -- Means that is was added indirectly.- modifyCurOptimisticLastUses $ M.delete mem- _ -> return ()---- Set the optimistic last use in stone.-commitOptimistic :: MName -> FindM lore ()-commitOptimistic mem = do- res <- M.lookup mem <$> gets curOptimisticLastUses- case res of- Just (x_lu, _) -> recordMapping x_lu mem- Nothing -> return ()--lookInFunDefFParam :: FParam lore -> FindM lore ()-lookInFunDefFParam (Param x _) = do- first_uses_x <- lookupEmptyable x <$> asks ctxFirstUses- modifyCurFirstUses $ S.union first_uses_x--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm (Let (Pattern _patctxelems patvalelems) _ e) = do- -- When an loop, a scan, a reduce, or a stream contains a use of an array that- -- is created before the expression body, it should not get a last use in a- -- statement inside the inner body, since loops can have cycles, and so its- -- proper last use should really be in the statement declaring the sub-body,- -- and not in some statement in the sub-body. See- -- 'tests/reuse/loop/copy-from-outside.fut for an example of this.- cur_first_uses <- gets curFirstUses- let mMod = case e of- If{} -> id -- If is the only other expression with a body.- _ -> local $ \ctx -> ctx { ctxCurFirstUsesOuter = cur_first_uses }-- -- First handle all pattern elements by themselves.- forM_ patvalelems $ \(PatElem x membound) ->- case membound of- ExpMem.MemArray _ _ _ (ExpMem.ArrayIn xmem _) -> do- first_uses_x <- lookupEmptyable x <$> asks ctxFirstUses- modifyCurFirstUses $ S.union first_uses_x- -- When this is a new first use of a memory block, commit the previous- -- optimistic last use of it, so that it can be considered unused in- -- the statements inbetween.- when (S.member xmem first_uses_x) $ commitOptimistic xmem- _ -> return ()-- -- Then find the new memory blocks.- let e_free_vars = freeInExp e `S.difference` S.fromList (freeExcludes e)- e_mems <- S.unions <$> mapM varMems (S.toList e_free_vars)-- mem_aliases <- asks ctxMemAliases- first_uses_outer <- asks ctxCurFirstUsesOuter- -- Then handle the pattern elements by themselves again.- forM_ patvalelems $ \(PatElem x _) ->- -- Set all memory blocks being used as optimistic last uses.- forM_ (S.toList e_mems) $ \mem -> do- -- If the memory has its first use outside the current body, it is- -- dangerous to set its last use to be in a statement inside the body,- -- since the body can be run multiple times in cases of loops or kernels,- -- so we only set the last use of a memory to this statement if it also- -- has its first use inside the current body.- --- -- If it (or any aliased memory) does have its first use outside the body,- -- we remove any existing optimistic last use, although only if such an- -- optimistic last use was added as a side effect of adding an existential- -- optimistic last use (i.e. it was aliased by the existential memory- -- which had a last use).- let from_outer = any (`S.member` first_uses_outer)- (mem : S.toList (lookupEmptyable mem mem_aliases))- if from_outer- then removeIndirectOptimistic mem- else setOptimistic mem (FromStm x) S.empty-- if S.null (lookupEmptyable mem mem_aliases)- then- -- If not existential, update the potential last use of any existential- -- memory aliasing it, but do not set the potential last use of the- -- memory itself, since there are cycles in loops, and it must also- -- contain the same data in the next iteration, so it can never be- -- reused inside the loop body, and must therefore always have its last- -- use outside the body. But since the existential memory might in the- -- current iteration refer to it, its last use needs to be updated.-- -- Note that while it is not wrong to run the code below also when the- -- memory has its first use inside the body, in that case it should not- -- be necessary, since we would be outside the body by then, and it- -- would result in a too conservative analysis. As an example, see- -- tests/mix/loop-interference-use.fut.- when from_outer $ do- -- If the memory has its first use outside the current body, we need- -- to find its actual last use (if it occurs in the body) through- -- memory aliases.- --- -- If memory block t aliases memory block u (meaning that the memory of- -- t *can* be the memory of u), and u has a potential last use here,- -- then t also has a potential last use here (the relation is not- -- commutative, so it does not work the other way round).- let reverse_mem_aliases = M.keys $ M.filter (mem `S.member`) mem_aliases- exclude = S.singleton mem- forM_ reverse_mem_aliases $ \mem' ->- setOptimistic mem' (FromStm x) exclude- else- -- Just set the last use.- unless from_outer $ setOptimistic mem (FromStm x) S.empty-- withLocalCurFirstUses $ mMod $ fullWalkExpM walker walker_kernel e- where walker = identityWalker- { walkOnBody = lookInBody }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInBody . lambdaBody- }---- Look in body results.-lookInRes :: SubExp -> FindM lore ()-lookInRes (Var v) = do- exis <- asks ctxExistentials- -- If v is a existential variable, there is no reason to record its last use,- -- as existential memory cannot be reused (this is also the case for other- -- setOptimistic calls, but not in a clear way).- unless (v `S.member` exis) $ do- mem_v <- M.lookup v <$> asks ctxVarToMem- case mem_v of- Just mem ->- setOptimistic (memSrcName mem) (FromRes v) S.empty- Nothing ->- return ()-lookInRes _ = return ()---- Some freeInExp results are too limiting and give us too conservative last use--- results (especially in the CPU pipeline). We only care about a free variable--- if we *read* from it. If it only exists for *writing*, then we don't have to--- look at its memory, since whatever is there we overwrite, and so there cannot--- be any last *use*.-freeExcludes :: Exp lore -> [VName]-freeExcludes e = case e of- DoLoop _ _mergevalparams _ _ ->- -- FIXME: If the returned memory block-associated mergevalparams do not come- -- directly from a Scratch creation, we should be able to ignore them and- -- thereby become less conservative.- []-- BasicOp (Update orig _ _) ->- [orig]-- _ -> []
− src/Futhark/Optimise/MemoryBlockMerging/MemoryAliases.hs
@@ -1,162 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE LambdaCase #-}--- | Find memory block aliases. The conceptual difference from variable aliases--- is that if a variable x has an alias y, it means that x and y use the same--- memory block, but if a memory block xmem has an alias ymem, it means that--- xmem and ymem refer to the same *memory*. This is not commutative.-module Futhark.Optimise.MemoryBlockMerging.MemoryAliases- ( findMemAliases- ) where--import Data.Maybe (mapMaybe)-import qualified Data.Map.Strict as M-import qualified Data.Set as S-import qualified Data.List as L-import Control.Monad.RWS--import Futhark.Representation.AST-import Futhark.Representation.Aliases-import Futhark.Representation.ExplicitMemory- (ExplicitMemorish, ExplicitMemory)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel-import Futhark.Analysis.Alias (analyseFun)--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous-import Futhark.Optimise.MemoryBlockMerging.Types---newtype FindM lore a = FindM { unFindM :: RWS (VarMemMappings MemorySrc) [MemAliases] () a }- deriving (Monad, Functor, Applicative,- MonadReader (VarMemMappings MemorySrc),- MonadWriter [MemAliases])--type LoreConstraints lore = (ExplicitMemorish lore,- FullWalkAliases lore)--recordMapping :: MName -> MNames -> FindM lore ()-recordMapping mem mems = tell [M.singleton mem (S.delete mem mems)]--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM--lookupMems :: Names -> FindM lore MNames-lookupMems var_aliases = do- var_to_mem <- ask- return $ S.fromList $ mapMaybe ((memSrcName <$>) . flip M.lookup var_to_mem)- $ S.toList var_aliases---- | Find all memory aliases in a function definition.-findMemAliases :: FunDef ExplicitMemory -> VarMemMappings MemorySrc -> MemAliases-findMemAliases fundef var_to_mem =- let fundef' = analyseFun fundef- m = unFindM $ lookInBody $ funDefBody fundef'- mem_aliases = M.unionsWith S.union $ snd $ evalRWS m var_to_mem ()- mem_aliases' = removeEmptyMaps $ expandWithAliases mem_aliases mem_aliases- in mem_aliases'--lookInBody :: LoreConstraints lore =>- Body (Aliases lore) -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody (Aliases lore) -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--lookInStm :: LoreConstraints lore =>- Stm (Aliases lore) -> FindM lore ()-lookInStm (Let (Pattern patctxelems patvalelems) _ e) = do- forM_ (patctxelems ++ patvalelems) lookInPatElem-- case e of- DoLoop mergectxparams mergevalparams _loopform body -> do- -- There are most likely more body results than- -- mergectxparams, but we are only interested in the first- -- body results anyway (those that have a matching location- -- with the mergectxparams).- zipWithM_ lookInMergeCtxParam mergectxparams (bodyResult body)- zipWithM_ lookInCtx patctxelems mergectxparams- mapM_ (lookInMergeValParam body) mergevalparams- mapM_ (lookInBodyTuples patctxelems (map snd mergectxparams) (bodyResult body))- patvalelems- If _ body_then body_else _ -> do- -- Alias everything. FIXME: This is maybe more conservative than- -- necessary if the If works on tuples of arrays.- let ress = mapMaybe subExpVar- (bodyResult body_then ++ bodyResult body_else)- var_to_mem <- ask- let mems = map memSrcName $ mapMaybe (`M.lookup` var_to_mem) ress- forM_ patctxelems $ \case- (PatElem patmem (_, ExpMem.MemMem{})) ->- recordMapping patmem $ S.fromList mems- _ -> return ()- _ -> return ()-- fullWalkAliasesExpM walker walker_kernel e- where walker = identityWalker- { walkOnBody = lookInBody- }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInBody . lambdaBody- }--lookInCtx :: LoreConstraints lore =>- PatElem (Aliases lore) -> (FParam (Aliases lore), SubExp)- -> FindM lore ()-lookInCtx (PatElem patmem (_, ExpMem.MemMem{})) (Param parammem ExpMem.MemMem{}, _) = do- recordMapping patmem (S.singleton parammem)- recordMapping parammem (S.singleton patmem)-lookInCtx _ _ = return ()--lookInMergeCtxParam :: LoreConstraints lore =>- (FParam (Aliases lore), SubExp) -> SubExp -> FindM lore ()-lookInMergeCtxParam (Param xmem ExpMem.MemMem{}, Var param_mem) (Var body_mem_res) = do- let aliases = S.fromList [param_mem, body_mem_res]- recordMapping xmem aliases-lookInMergeCtxParam _ _ = return ()--lookInMergeValParam :: LoreConstraints lore =>- Body (Aliases lore) -> (FParam (Aliases lore), SubExp)- -> FindM lore ()-lookInMergeValParam body (Param _ (ExpMem.MemArray _ _ _ (ExpMem.ArrayIn mem _)), _t) = do- -- FIXME: This is maybe more conservative than necessary in case you have more- -- than one loop array. Fixing this would require either changing the Aliases- -- representation, or building something on top of it.- aliases <- S.unions- <$> mapM (lookupMems . unNames) (fst $ fst $ bodyAttr body)- recordMapping mem aliases-lookInMergeValParam _ _ = return ()--lookInBodyTuples :: LoreConstraints lore =>- [PatElem (Aliases lore)]- -> [SubExp] -> [SubExp]- -> PatElem (Aliases lore)- -> FindM lore ()--- When a parameter refers to a existential memory, we want to find--- which return memory in the loop that the existential memory refers--- to.-lookInBodyTuples patctxelems body_params body_results- (PatElem _ (_, ExpMem.MemArray _ _ _ (ExpMem.ArrayIn mem _))) = do- let zipped = zip3 patctxelems body_params body_results- case L.find ((== mem) . patElemName . (\(x, _, _) -> x)) zipped of- Just (_, Var param_mem, Var res_mem) ->- recordMapping mem (S.fromList [param_mem, res_mem])- _ -> return ()-lookInBodyTuples _ _ _ _ = return ()--lookInPatElem :: LoreConstraints lore =>- PatElem (Aliases lore) -> FindM lore ()-lookInPatElem (PatElem _ (names', ExpMem.MemArray _ _ _ (ExpMem.ArrayIn xmem _))) = do- aliases <- lookupMems $ unNames names'- recordMapping xmem aliases-lookInPatElem (PatElem xmem (names', ExpMem.MemMem {})) = do- aliases <- lookupMems $ unNames names'- recordMapping xmem aliases-lookInPatElem _ = return ()
− src/Futhark/Optimise/MemoryBlockMerging/MemoryUpdater.hs
@@ -1,418 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE LambdaCase #-}---- | Transform a function based on a mapping from variable to memory and index--- function: Change every variable in the mapping to its possibly new memory--- block.-module Futhark.Optimise.MemoryBlockMerging.MemoryUpdater- ( transformFromVarMemMappings- ) where--import qualified Data.Map.Strict as M-import qualified Data.List as L-import Data.Maybe (mapMaybe, fromMaybe)-import Control.Applicative ((<|>))-import Control.Arrow (second)-import Control.Monad-import Control.Monad.RWS--import Futhark.MonadFreshNames-import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory- (ExplicitMemorish, ExplicitMemory)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.Types-import Futhark.Optimise.MemoryBlockMerging.Miscellaneous---data Context = Context { ctxVarToMem :: VarMemMappings MemoryLoc- , ctxVarToMemOrig :: VarMemMappings MName- , ctxAllocSizes :: M.Map MName SubExp- , ctxAllocSizesOrig :: M.Map MName SubExp- , ctxHasMaxedSize :: Bool- }- deriving (Show)--newtype FindM lore a = FindM { unFindM :: RWS Context () (VNameSource, [(MName, VName)]) a }- deriving (Monad, Functor, Applicative,- MonadReader Context, MonadState (VNameSource, [(MName, VName)]))--instance MonadFreshNames (FindM lore) where- getNameSource = gets fst- putNameSource s = modify $ \(_, m) -> (s, m)--modifyMemSizeMapping :: ([(MName, VName)] -> [(MName, VName)]) -> FindM lore ()-modifyMemSizeMapping f = modify $ second f--type LoreConstraints lore = (ExplicitMemorish lore,- FullMap lore,- BodyAttr lore ~ (),- ExpAttr lore ~ ())--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM---- | Transform a function to use new memory blocks.-transformFromVarMemMappings :: MonadFreshNames m =>- VarMemMappings MemoryLoc ->- VarMemMappings MName ->- M.Map MName SubExp -> M.Map MName SubExp -> Bool ->- FunDef ExplicitMemory ->- m (FunDef ExplicitMemory)-transformFromVarMemMappings var_to_mem var_to_mem_orig alloc_sizes alloc_sizes_orig has_maxed_size fundef =- let m = unFindM $ transformFunDefBody $ funDefBody fundef- ctx = Context { ctxVarToMem = var_to_mem- , ctxVarToMemOrig = var_to_mem_orig- , ctxAllocSizes = alloc_sizes- , ctxAllocSizesOrig = alloc_sizes_orig- , ctxHasMaxedSize = has_maxed_size- }- in modifyNameSource (\src ->- let (body', (src', _), ()) = runRWS m ctx (src, [])- in (fundef { funDefBody = body' }, src')- )--transformFunDefBody :: LoreConstraints lore =>- Body lore -> FindM lore (Body lore)-transformFunDefBody (Body () bnds res) = do- bnds' <- mapM transformStm $ stmsToList bnds- res' <- transformFunDefBodyResult res- return $ Body () (stmsFromList bnds') res'--transformFunDefBodyResult :: [SubExp] -> FindM lore [SubExp]-transformFunDefBodyResult ses = do- var_to_mem_orig <- asks ctxVarToMemOrig- var_to_mem <- asks ctxVarToMem- mem_to_size_orig <- asks ctxAllocSizesOrig- mem_to_size <- asks ctxAllocSizes- mem_to_new_size <- gets snd-- let check se- | Var v <- se- , Just orig <- M.lookup v var_to_mem_orig- , Just new <- memLocName <$> M.lookup v var_to_mem- = ((Var orig, Nothing), Var new) : case (M.lookup orig mem_to_size_orig,- (Var <$> L.lookup new mem_to_new_size) <|> M.lookup new mem_to_size) of- (Just size_orig, Just size_new) ->- [((size_orig, Just (Var orig)), size_new)]- _ -> []- | otherwise = []-- check_size_only se- | Var v <- se- , Just orig <- M.lookup v mem_to_size_orig- , Just new <- (Var <$> L.lookup v mem_to_new_size) <|> M.lookup v mem_to_size- , orig /= new- = [((orig, Just (Var v)), new)]- | otherwise = []- mem_orig_to_new1 = concatMap check ses- mem_orig_to_new2 = concatMap check_size_only ses- mem_orig_to_new = mem_orig_to_new1 ++ mem_orig_to_new2-- return $ zipWith (- \se ts -> fromMaybe se (- -- FIXME: This assumes that a memory block always- -- comes just after its size variable. We ought- -- to instead properly find this information from- -- the funDefRetType 'ExtSize's.- (se, Nothing) `L.lookup` mem_orig_to_new- <|> case ts of- (ts0 : _) ->- (se, Just ts0) `L.lookup` mem_orig_to_new- _ -> Nothing- )- ) ses (L.tail $ L.tails ses)--transformBody :: LoreConstraints lore =>- Body lore -> FindM lore (Body lore)-transformBody (Body () bnds res) = do- bnds' <- mapM transformStm $ stmsToList bnds- return $ Body () (stmsFromList bnds') res--transformKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore (KernelBody lore)-transformKernelBody (KernelBody () bnds res) = do- bnds' <- mapM transformStm $ stmsToList bnds- return $ KernelBody () (stmsFromList bnds') res--transformMemInfo :: ExpMem.MemInfo d u ExpMem.MemReturn -> MemoryLoc ->- ExpMem.MemInfo d u ExpMem.MemReturn-transformMemInfo meminfo memloc = case meminfo of- ExpMem.MemArray pt shape u _memreturn ->- let extixfun = ExpMem.existentialiseIxFun [] $ memLocIxFun memloc- in ExpMem.MemArray pt shape u- (ExpMem.ReturnsInBlock (memLocName memloc) extixfun)- _ -> meminfo--data BranchReturn = ExistingBranchReturn ExpMem.BodyReturns- | NewBranchReturn (Int -> ExpMem.BodyReturns)- VName VName VName--transformStm :: LoreConstraints lore =>- Stm lore -> FindM lore (Stm lore)-transformStm (Let (Pattern patctxelems patvalelems) aux e) = do- patvalelems' <- mapM transformPatValElem patvalelems-- e' <- fullMapExpM mapper mapper_kernel e- var_to_mem <- asks ctxVarToMem- var_to_mem_orig <- asks ctxVarToMemOrig- mem_to_size <- asks ctxAllocSizes- mem_to_new_size <- gets snd- (e'', patctxelems') <- case e' of- If cond body_then body_else (IfAttr rets sort) -> do- let bodyVarMemLocs body =- map (flip M.lookup var_to_mem <=< subExpVar)- $ drop (length patctxelems) $ bodyResult body-- -- FIXME: This is a mess. We try to "reverse-engineer" the origin of- -- how the If results came to look as they do, so that we can produce- -- a correct IfAttr.- findBodyResMem i body_results =- let imem = patElemName (patctxelems L.!! i)- matching_var = mapMaybe (- \(p, p_i) ->- case patElemAttr p of- ExpMem.MemArray _ _ _ (ExpMem.ArrayIn vmem _) ->- if imem == vmem- then Just p_i- else Nothing- _ ->- Nothing- ) (zip patvalelems [0..])- in do- j <- case matching_var of- [t] -> Just t- _ -> Nothing- body_res_var <- subExpVar (body_results L.!! (length patctxelems + j))- MemoryLoc mem _ixfun <- M.lookup body_res_var var_to_mem- return mem-- fixBodyExistentials body =- body { bodyResult =- zipWith (\res i -> if i < length patctxelems- then maybe res Var $ findBodyResMem i (bodyResult body)- else res)- (bodyResult body) [0..] }-- let ms_then = bodyVarMemLocs body_then- ms_else = bodyVarMemLocs body_else-- -- Fix values.- let rets' =- if ms_then == ms_else- then zipWith (\r m -> case m of- Nothing -> r- Just m' ->- transformMemInfo r m'- ) rets ms_then- else rets-- let body_then' = fixBodyExistentials body_then- body_else' = fixBodyExistentials body_else--- -- Fix existential memory blocks.- let mem_size mem = L.lookup mem mem_to_new_size <|> (subExpVar =<< M.lookup mem mem_to_size)- v_size v = do- mem <- M.lookup v (M.map memLocName var_to_mem) <|> M.lookup v var_to_mem_orig- mem_size mem-- has_maxed_size <- asks ctxHasMaxedSize- let rets_branch_returns =- L.zipWith4 (\r pat th el -> case (r, pat, th, el) of- (ExpMem.MemArray pt shape u- (ExpMem.ReturnsNewBlock space n- (Free (Var _size)) extixfun),- PatElem _- (ExpMem.MemArray _ _ _- (ExpMem.ArrayIn patmem _)),- Var v_th, Var v_el) ->- case (v_size v_th, v_size v_el) of- (Just s_th, Just s_el) ->- if not has_maxed_size --s_th == s_el || not has_maxed_size- then ExistingBranchReturn r- else NewBranchReturn- (\nth_ctxelem ->- ExpMem.MemArray pt shape u- (ExpMem.ReturnsNewBlock space n- (Ext nth_ctxelem) extixfun))- s_th s_el patmem- _ -> error ("both branch return arrays should use a memory block with a size: " ++ show v_th ++ " and " ++ show v_el)- _ -> ExistingBranchReturn r- )- rets'- patvalelems- (drop (length patctxelems) (bodyResult body_then'))- (drop (length patctxelems) (bodyResult body_else'))-- patctxelems_new <-- replicateM- (length (filter (\case- NewBranchReturn{} -> True- ExistingBranchReturn{} -> False- ) rets_branch_returns))- (newVName "new_memory_size")- let (rets'', _, body_ext_new, _, patmem_to_new_size) =- foldl (\(prev, i, ext, patctxelems_new', mapping) rb -> case rb of- ExistingBranchReturn r ->- (prev ++ [r], i, ext, patctxelems_new', mapping)- NewBranchReturn rf s_th s_el patmem ->- (prev ++ [rf i], i + 1, ext ++ [(s_th, s_el)],- tail patctxelems_new',- mapping ++ [(patmem, head patctxelems_new')])- ) ([], length patctxelems, [], patctxelems_new, []) rets_branch_returns- modifyMemSizeMapping (++ patmem_to_new_size)- let (th_ext_new, el_ext_new) = unzip body_ext_new- body_then'' = body_then' { bodyResult =- take (length patctxelems) (bodyResult body_then') ++- map Var th_ext_new ++- drop (length patctxelems) (bodyResult body_then')- }- body_else'' = body_else' { bodyResult =- take (length patctxelems) (bodyResult body_else') ++- map Var el_ext_new ++- drop (length patctxelems) (bodyResult body_else')- }- patctxelems_replaced = map (\pe -> case pe of- PatElem name (ExpMem.MemMem _size space) ->- case L.lookup name patmem_to_new_size of- Just size_new ->- PatElem name (ExpMem.MemMem (Var size_new) space)- Nothing -> pe- _ -> pe- ) patctxelems- patctxelems' = patctxelems_replaced ++ map (\v -> PatElem v (ExpMem.MemPrim (IntType Int64))) patctxelems_new-- return (If cond body_then'' body_else'' (IfAttr rets'' sort),- patctxelems')-- DoLoop mergectxparams mergevalparams loopform body -> do- -- More special loop handling because of its extra- -- pattern-like info.- mergectxparams' <- mapM (transformMergeCtxParam mergevalparams) mergectxparams- mergevalparams' <- mapM transformMergeValParam mergevalparams-- -- The body of a loop can return a memory block in its results. This is- -- the memory block used by a variable which is also part of the results.- -- If the memory block of that variable is changed, we need a way to- -- record that the memory block in the body result also needs to change.- let zipped = zip [(0::Int)..] (patctxelems ++ patvalelems)-- findMemLinks (i, PatElem _x (ExpMem.MemArray _ _ _ (ExpMem.ArrayIn xmem _))) =- case L.find (\(_, PatElem ymem _) -> ymem == xmem) zipped of- Just (j, _) -> Just (j, i)- Nothing -> Nothing- findMemLinks _ = Nothing-- mem_links = mapMaybe findMemLinks zipped-- res = bodyResult body-- fixResRecord i se- | Var _mem <- se- , Just j <- L.lookup i mem_links- , Var related_var <- res L.!! j- , Just mem_new <- M.lookup related_var var_to_mem =- Var $ memLocName mem_new- | otherwise = se-- res' = zipWith fixResRecord [(0::Int)..] res- body' = body { bodyResult = res' }-- loopform' <- case loopform of- ForLoop i it bound loop_vars ->- ForLoop i it bound <$> mapM transformForLoopVar loop_vars- WhileLoop _ -> return loopform- return (DoLoop mergectxparams' mergevalparams' loopform' body',- patctxelems)- _ -> return (e', patctxelems)- return (Let (Pattern patctxelems' patvalelems') aux e'')- where mapper = identityMapper- { mapOnBody = const transformBody- , mapOnFParam = transformFParam- , mapOnLParam = transformLParam- }- mapper_kernel = identityKernelMapper- { mapOnKernelBody = coerce . transformBody- , mapOnKernelKernelBody = coerce . transformKernelBody- , mapOnKernelLambda = coerce . transformLambda- , mapOnKernelLParam = transformLParam- }------ Update the actual memory block referred to by a context (existential) memory--- block in a loop.-transformMergeCtxParam :: [(FParam ExplicitMemory, SubExp)] ->- (FParam ExplicitMemory, SubExp)- -> FindM lore (FParam ExplicitMemory, SubExp)-transformMergeCtxParam mergevalparams (param@(Param ctxmem ExpMem.MemMem{}), mem) = do- var_to_mem <- asks ctxVarToMem-- let usesCtxMem (Param _ (ExpMem.MemArray _ _ _ (ExpMem.ArrayIn pmem _))) = ctxmem == pmem- usesCtxMem _ = False-- -- If the initial value of a loop merge parameter is a memory block name,- -- we may have to update that. If the context memory block is used in an- -- array in one of the value merge parameters, see if that array variable- -- refers to an array that has been set to reuse a memory block.- mem' = fromMaybe mem $ do- (_, Var orig_var) <- L.find (usesCtxMem . fst) mergevalparams- orig_mem <- M.lookup orig_var var_to_mem- return $ Var $ memLocName orig_mem- return (param, mem')-transformMergeCtxParam _ t = return t--transformMergeValParam :: (FParam ExplicitMemory, SubExp)- -> FindM lore (FParam ExplicitMemory, SubExp)-transformMergeValParam (Param x membound, se) = do- membound' <- newMemBound membound x- return (Param x membound', se)--transformPatValElem :: PatElem ExplicitMemory -> FindM lore (PatElem ExplicitMemory)-transformPatValElem (PatElem x membound) =- PatElem x <$> newMemBound membound x--transformFParam :: LoreConstraints lore =>- FParam lore -> FindM lore (FParam lore)-transformFParam (Param x membound) =- Param x <$> newMemBound membound x--transformLParam :: LoreConstraints lore =>- LParam lore -> FindM lore (LParam lore)-transformLParam (Param x membound) =- Param x <$> newMemBound membound x--transformLambda :: LoreConstraints lore =>- Lambda lore -> FindM lore (Lambda lore)-transformLambda (Lambda params body types) = do- params' <- mapM transformLParam params- body' <- transformBody body- return $ Lambda params' body' types--transformForLoopVar :: LoreConstraints lore =>- (LParam lore, VName) ->- FindM lore (LParam lore, VName)-transformForLoopVar (Param x membound, array) = do- membound' <- newMemBound membound x- return (Param x membound', array)---- Find a new memory block and index function if they exist.-newMemBound :: ExpMem.MemBound u -> VName -> FindM lore (ExpMem.MemBound u)-newMemBound membound var = do- var_to_mem <- asks ctxVarToMem-- let membound'- | ExpMem.MemArray pt shape u _ <- membound- , Just (MemoryLoc mem ixfun) <- M.lookup var var_to_mem =- Just $ ExpMem.MemArray pt shape u $ ExpMem.ArrayIn mem ixfun- | otherwise = Nothing-- return $ fromMaybe membound membound'
− src/Futhark/Optimise/MemoryBlockMerging/Miscellaneous.hs
@@ -1,263 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}--- | Miscellaneous helper functions. Perpetually in need of a cleanup.-module Futhark.Optimise.MemoryBlockMerging.Miscellaneous- ( makeCommutativeMap- , insertOrUpdate- , insertOrUpdateMany- , insertOrNew- , removeEmptyMaps- , removeKeyFromMapElems- , newDeclarationsStm- , lookupEmptyable- , fromJust- , maybeFromBoolM- , sortByKeyM- , mapMaybeM- , anyM- , whenM- , expandPrimExp- , expandIxFun- , mapFromListSetUnion- , fixpointIterateMay- , filterSetM- , (<&&>), (<||>)-- , expandWithAliases- , FullWalk(..)- , fullWalkAliasesExpM- , FullWalkAliases- , FullMap- , fullMapExpM- ) where--import qualified Data.Map.Strict as M-import qualified Data.Set as S-import qualified Data.List as L-import Control.Monad-import Data.Maybe (fromMaybe, catMaybes)-import Data.Function (on)--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory- (ExplicitMemory, InKernel)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel-import Futhark.Representation.Kernels.KernelExp-import Futhark.Representation.Aliases-import Futhark.Analysis.PrimExp.Convert--import qualified Futhark.Representation.ExplicitMemory.IndexFunction as IxFun-import Futhark.Optimise.MemoryBlockMerging.Types----- If a property is commutative in a map, build a map that reflects it. A bit--- crude. We could also just use a function that calculates this whenever--- needed.-makeCommutativeMap :: Ord v => M.Map v (S.Set v) -> M.Map v (S.Set v)-makeCommutativeMap m =- let names = S.toList (S.union (M.keysSet m) (S.unions (M.elems m)))- assocs = map (\n ->- let existing = lookupEmptyable n m- newly_found = S.unions $ map (\(k, v) ->- if S.member n v- then S.singleton k- else S.empty) $ M.assocs m- ns = S.union existing newly_found- in (n, ns)) names- in M.fromList assocs--insertOrUpdate :: (Ord k, Ord v) => k -> v ->- M.Map k (S.Set v) -> M.Map k (S.Set v)-insertOrUpdate k v = M.alter (insertOrNew (S.singleton v)) k--insertOrUpdateMany :: (Ord k, Ord v) => k -> S.Set v ->- M.Map k (S.Set v) -> M.Map k (S.Set v)-insertOrUpdateMany k vs = M.alter (insertOrNew vs) k--insertOrNew :: Ord a => S.Set a -> Maybe (S.Set a) -> Maybe (S.Set a)-insertOrNew xs m = Just $ case m of- Just s -> S.union xs s- Nothing -> xs--removeEmptyMaps :: M.Map k (S.Set v) -> M.Map k (S.Set v)-removeEmptyMaps = M.filter (not . S.null)--removeKeyFromMapElems :: (Ord k) => M.Map k (S.Set k) -> M.Map k (S.Set k)-removeKeyFromMapElems = M.mapWithKey S.delete--newDeclarationsStm :: Stm lore -> [VName]-newDeclarationsStm (Let (Pattern patctxelems patvalelems) _ e) =- let new_decls0 = map patElemName (patctxelems ++ patvalelems)- new_decls1 = case e of- DoLoop mergectxparams mergevalparams _loopform _body ->- -- Technically not a declaration for the current expression, but very- -- close.- map (paramName . fst) (mergectxparams ++ mergevalparams)- _ -> []- new_decls = new_decls0 ++ new_decls1- in new_decls--lookupEmptyable :: (Ord a, Monoid b) => a -> M.Map a b -> b-lookupEmptyable x m = fromMaybe mempty $ M.lookup x m--fromJust :: String -> Maybe a -> a-fromJust _ (Just x) = x-fromJust mistake Nothing = error ("error: " ++ mistake)--maybeFromBoolM :: Monad m => (a -> m Bool) -> (a -> m (Maybe a))-maybeFromBoolM f a = do- res <- f a- return $ if res- then Just a- else Nothing--expandWithAliases :: forall v. Ord v => MemAliases -> M.Map v Names -> M.Map v Names-expandWithAliases mem_aliases = fixpointIterate expand- where expand :: M.Map v Names -> M.Map v Names- expand mems_map =- M.fromList (map (\(v, mems) ->- (v, S.unions (mems : map (`lookupEmptyable` mem_aliases)- (S.toList mems))))- (M.assocs mems_map))--fixpointIterate :: Eq a => (a -> a) -> a -> a-fixpointIterate f x- | f x == x = x- | otherwise = fixpointIterate f (f x)--fixpointIterateMay :: (a -> Maybe a) -> a -> a-fixpointIterateMay f x = maybe x (fixpointIterateMay f) (f x)--mapFromListSetUnion :: (Ord k, Ord v) => [(k, S.Set v)] -> M.Map k (S.Set v)-mapFromListSetUnion = M.unionsWith S.union . map (uncurry M.singleton)---- Replace variables with subtrees of their constituents wherever possible. It--- naively expands a PrimExp as much as the input map allows, and can enable--- more expressions to have it in scope, since it will likely consist of fewer--- variables.-expandPrimExp :: M.Map VName (ExpMem.PrimExp VName) -> ExpMem.PrimExp VName- -> ExpMem.PrimExp VName-expandPrimExp var_to_pe = fixpointIterate (substituteInPrimExp var_to_pe)--expandIxFun :: M.Map VName (ExpMem.PrimExp VName) -> ExpMem.IxFun -> ExpMem.IxFun-expandIxFun var_to_pe = fixpointIterate (IxFun.substituteInIxFun var_to_pe)--(<&&>) :: Monad m => m Bool -> m Bool -> m Bool-m <&&> n = (&&) <$> m <*> n--(<||>) :: Monad m => m Bool -> m Bool -> m Bool-m <||> n = (||) <$> m <*> n--anyM :: Monad m => (a -> m Bool) -> [a] -> m Bool-anyM f xs = or <$> mapM f xs--whenM :: Monad m => m Bool -> m () -> m ()-whenM b m = do- b' <- b- when b' m--mapMaybeM :: Monad m => (a -> m (Maybe b)) -> [a] -> m [b]-mapMaybeM f xs = catMaybes <$> mapM f xs--sortByKeyM :: (Ord t, Monad m) => (a -> m t) -> [a] -> m [a]-sortByKeyM f xs =- map fst . L.sortBy (compare `on` snd) . zip xs <$> mapM f xs--filterSetM :: (Ord a, Monad m) => (a -> m Bool) -> S.Set a -> m (S.Set a)-filterSetM f xs = S.fromList <$> filterM f (S.toList xs)---- Map on both ExplicitMemory and InKernel.-class FullMap lore where- fullMapExpM :: Monad m => Mapper lore lore m -> KernelMapper InKernel InKernel m- -> Exp lore -> m (Exp lore)--instance FullMap ExplicitMemory where- fullMapExpM mapper mapper_kernel e =- case e of- Op (ExpMem.Inner kernel) ->- Op . ExpMem.Inner <$> mapKernelM mapper_kernel kernel- _ -> mapExpM mapper e--instance FullMap InKernel where- fullMapExpM mapper mapper_kernel e = case e of- Op (ExpMem.Inner ke) -> Op . ExpMem.Inner <$> case ke of- ExpMem.Combine a b c body ->- ExpMem.Combine a b c <$> mapOnKernelBody mapper_kernel body- ExpMem.GroupReduce a lambda b ->- ExpMem.GroupReduce a- <$> mapOnKernelLambda mapper_kernel lambda- <*> pure b- ExpMem.GroupScan a lambda b ->- ExpMem.GroupScan a- <$> mapOnKernelLambda mapper_kernel lambda- <*> pure b- ExpMem.GroupStream a b (ExpMem.GroupStreamLambda a1 b1 params0 params1 gsbody) c d ->- ExpMem.GroupStream a b- <$> (ExpMem.GroupStreamLambda a1 b1- <$> mapM (mapOnKernelLParam mapper_kernel) params0- <*> mapM (mapOnKernelLParam mapper_kernel) params1- <*> mapOnKernelBody mapper_kernel gsbody- )- <*> pure c <*> pure d- _ -> return ke- _ -> mapExpM mapper e---- Walk on both ExplicitMemory and InKernel.-class FullWalk lore where- fullWalkExpM :: Monad m => Walker lore m -> KernelWalker InKernel m- -> Exp lore -> m ()---- FIXME: This can maybe be integrated into the above typeclass.-class FullWalkAliases lore where- fullWalkAliasesExpM :: Monad m => Walker (Aliases lore) m- -> KernelWalker (Aliases InKernel) m- -> Exp (Aliases lore) -> m ()--instance FullWalk ExplicitMemory where- fullWalkExpM walker walker_kernel e = do- walkExpM walker e- case e of- Op (ExpMem.Inner kernel) ->- walkKernelM walker_kernel kernel- _ -> return ()--instance FullWalkAliases ExplicitMemory where- fullWalkAliasesExpM walker walker_kernel e = do- walkExpM walker e- case e of- Op (ExpMem.Inner kernel) ->- walkKernelM walker_kernel kernel- _ -> return ()--instance FullWalk InKernel where- fullWalkExpM walker walker_kernel e = case e of- Op (ExpMem.Inner ke) -> walkOnKernelExpM walker_kernel ke- _ -> walkExpM walker e--instance FullWalkAliases InKernel where- fullWalkAliasesExpM walker walker_kernel e = case e of- Op (ExpMem.Inner ke) -> walkOnKernelExpM walker_kernel ke- _ -> walkExpM walker e--walkOnKernelExpM :: Monad m => KernelWalker lore m ->- KernelExp lore -> m ()-walkOnKernelExpM walker_kernel ke = case ke of- ExpMem.Combine _ _ _ body ->- walkOnKernelBody walker_kernel body- ExpMem.GroupReduce _ lambda _ ->- walkOnKernelLambda walker_kernel lambda- ExpMem.GroupScan _ lambda _ ->- walkOnKernelLambda walker_kernel lambda- ExpMem.GroupStream _ _ gslambda _ _ ->- walkOnGroupStreamLambdaM walker_kernel gslambda- _ -> return ()--walkOnGroupStreamLambdaM :: Monad m => KernelWalker lore m ->- GroupStreamLambda lore -> m ()-walkOnGroupStreamLambdaM walker_kernel (GroupStreamLambda _ _- params0 params1 gsbody) = do- mapM_ (walkOnKernelLParam walker_kernel) params0- mapM_ (walkOnKernelLParam walker_kernel) params1- walkOnKernelBody walker_kernel gsbody
− src/Futhark/Optimise/MemoryBlockMerging/PrimExps.hs
@@ -1,105 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}--- | Get a mapping from statement name to PrimExp (if the statement has a--- primitive expression) for all statements.-module Futhark.Optimise.MemoryBlockMerging.PrimExps- ( findPrimExpsFunDef- ) where--import qualified Data.Map.Strict as M-import Data.Maybe (mapMaybe)-import Control.Monad-import Control.Monad.RWS--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory- (ExplicitMemorish, ExplicitMemory)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel-import Futhark.Tools--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous---type CurrentTypes = M.Map VName PrimType-type PrimExps = M.Map VName (PrimExp VName)--newtype FindM lore a = FindM { unFindM :: RWS () PrimExps CurrentTypes a }- deriving (Monad, Functor, Applicative,- MonadWriter PrimExps,- MonadState CurrentTypes)--type LoreConstraints lore = (ExplicitMemorish lore,- FullWalk lore)--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM---- Find/construct all 'PrimExp's in a function definition.-findPrimExpsFunDef :: FunDef ExplicitMemory -> PrimExps-findPrimExpsFunDef fundef =- let m = unFindM $ do- lookInFParams $ funDefParams fundef- lookInBody $ funDefBody fundef- res = snd $ evalRWS m () M.empty- in res--lookInFParams :: LoreConstraints lore =>- [FParam lore] -> FindM lore ()-lookInFParams params = forM_ params $ \(Param var membound) -> do- case typeOf membound of- Prim pt -> modify $ M.insert var pt- _ -> return ()-- case membound of- ExpMem.MemArray pt shape _ (ExpMem.ArrayIn mem _) -> do- let matchingSizeVar (Param mem1 (ExpMem.MemMem (Var mem_size) _))- | mem1 == mem = Just mem_size- matchingSizeVar _ = Nothing- case mapMaybe matchingSizeVar params of- [mem_size] -> do- let prod_i32 = product (map (primExpFromSubExp (IntType Int32)) (shapeDims shape))- let prod_i64 = ConvOpExp (SExt Int32 Int64) prod_i32- let pe = prod_i64 * primByteSize pt- tell $ M.singleton mem_size pe- _ -> return ()- _ -> return ()--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm (Let (Pattern _patctxelems patvalelems) _ e) = do- prim_types <- get- let varUse v = ExpMem.LeafExp v <$> M.lookup v prim_types-- case patvalelems of- [PatElem dst _] ->- forM_ (primExpFromExp varUse e) $ tell . M.singleton dst- _ -> return ()-- forM_ patvalelems $ \(PatElem var membound) ->- case typeOf membound of- Prim pt ->- modify $ M.insert var pt- _ -> return ()-- -- Recursive body walk.- fullWalkExpM walker walker_kernel e- where walker = identityWalker- { walkOnBody = lookInBody }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInBody . lambdaBody- }
− src/Futhark/Optimise/MemoryBlockMerging/Reuse.hs
@@ -1,30 +0,0 @@--- | Reuse the memory blocks of arrays.------ Enable by setting the environment variable MEMORY_BLOCK_MERGING_REUSE=1.-module Futhark.Optimise.MemoryBlockMerging.Reuse- ( reuseInProg- ) where--import Futhark.Pass--import Futhark.MonadFreshNames-import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (ExplicitMemory)--import Futhark.Optimise.MemoryBlockMerging.AuxiliaryInfo-import Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeMovingUp-import Futhark.Optimise.MemoryBlockMerging.Reuse.Core--reuseInProg :: Prog ExplicitMemory -> PassM (Prog ExplicitMemory)-reuseInProg = intraproceduralTransformation reuseInFunDef--reuseInFunDef :: MonadFreshNames m- => FunDef ExplicitMemory- -> m (FunDef ExplicitMemory)-reuseInFunDef fundef0 = do- let fundef1 = moveUpAllocSizesFunDef fundef0- aux1 = getAuxiliaryInfo fundef1- coreReuseFunDef fundef1- (auxFirstUses aux1) (auxInterferences aux1)- (auxPotentialKernelDataRaceInterferences aux1) (auxVarMemMappings aux1)- (auxActualVariables aux1) (auxExistentials aux1)
− src/Futhark/Optimise/MemoryBlockMerging/Reuse/AllocationSizeMovingUp.hs
@@ -1,32 +0,0 @@--- | Move size variables used in allocation statements upwards in the bodies of--- a program to enable more memory block reuses.------ This should be run *before* the reuse pass, as it enables more optimisations.--- Specifically, it helps with reusing memory whose size needs to be changed to--- be the maximum of itself and another size -- and so, that other size needs to--- have been hoisted so that is in scope at that point. This module hoists all--- sizes as much as possible.-module Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeMovingUp- ( moveUpAllocSizesFunDef- ) where--import qualified Data.Map.Strict as M-import Data.Maybe (fromMaybe)--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (ExplicitMemory)--import Futhark.Optimise.MemoryBlockMerging.CrudeMovingUp-import Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes--findAllocSizeHoistees :: Body ExplicitMemory -> Maybe [FParam ExplicitMemory]- -> [VName]-findAllocSizeHoistees body params =- let subexps = map fst $ M.elems- $ memBlockSizesParamsBodyNonRec (fromMaybe [] params) body- in subExpVars subexps--moveUpAllocSizesFunDef :: FunDef ExplicitMemory- -> FunDef ExplicitMemory-moveUpAllocSizesFunDef fundef =- moveUpInFunDef fundef findAllocSizeHoistees
− src/Futhark/Optimise/MemoryBlockMerging/Reuse/AllocationSizeUses.hs
@@ -1,127 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}--- | Find out where allocation sizes are used. For each statement, which sizes--- are in scope?-module Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses- ( findSizeUsesFunDef- ) where--import qualified Data.Map.Strict as M-import qualified Data.Set as S-import Data.Maybe (mapMaybe)-import Control.Monad-import Control.Monad.RWS-import Control.Monad.Writer--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory (- ExplicitMemory, ExplicitMemorish)-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous-import Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes-import Futhark.Optimise.MemoryBlockMerging.PrimExps---type SizeVars = Names-type DeclarationsSoFar = Names---- The final return value. Describes which size variables are in scope at the--- creation of the key size variable.-type UsesBefore = M.Map VName Names--newtype FindM lore a = FindM { unFindM :: RWS SizeVars- UsesBefore DeclarationsSoFar a }- deriving (Monad, Functor, Applicative,- MonadReader SizeVars,- MonadWriter UsesBefore,- MonadState DeclarationsSoFar)--type LoreConstraints lore = (ExplicitMemorish lore,- FullWalk lore)--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM--addDeclarations :: Names -> FindM lore ()-addDeclarations = modify . S.union--addUsesBefore :: VName -> Names -> FindM lore ()-addUsesBefore var declarations_so_far =- tell $ M.singleton var declarations_so_far--findSizeUsesFunDef :: FunDef ExplicitMemory -> UsesBefore-findSizeUsesFunDef fundef =- let size_vars = mapMaybe (subExpVar . fst) $ M.elems $ memBlockSizesFunDef fundef- var_to_pe = findPrimExpsFunDef fundef- -- We want to find 'uses before' for all size vars *and* which variables- -- they depend on. This is a compromise between recording the- -- relationship for only size variables and all variables. We need this- -- compromise for 'sizesCanBeMaxedKernelArray' in Reuse.Core.- find_pe_vars v0 = maybe S.empty- (S.insert v0 . execWriter . traverse- (\v -> do- tell $ S.singleton v- tell $ find_pe_vars v- return v)) $ M.lookup v0 var_to_pe- size_vars' = S.unions $ map find_pe_vars size_vars- m = unFindM $ do- forM_ (funDefParams fundef) lookInFParam- lookInBody $ funDefBody fundef- res = snd $ evalRWS m size_vars' S.empty- in res--lookInFParam :: FParam lore -> FindM lore ()-lookInFParam (Param x _) =- lookAtNewDecls $ S.singleton x--lookInLParam :: LParam lore -> FindM lore ()-lookInLParam (Param x _) =- lookAtNewDecls $ S.singleton x--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm stm@(Let _ _ e) = do- let new_decls = S.fromList $ newDeclarationsStm stm- lookAtNewDecls new_decls-- -- Recursive body walk.- fullWalkExpM walker walker_kernel e- where walker = identityWalker- { walkOnBody = lookInBody- , walkOnFParam = lookInFParam- , walkOnLParam = lookInLParam- }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInLambda- , walkOnKernelLParam = lookInLParam- }--lookInLambda :: LoreConstraints lore =>- Lambda lore -> FindM lore ()-lookInLambda (Lambda params body _) = do- forM_ params lookInLParam- lookInBody body--lookAtNewDecls :: Names -> FindM lore ()-lookAtNewDecls new_decls = do- all_size_vars <- ask- declarations_so_far <- get- let new_size_vars = S.intersection all_size_vars new_decls- forM_ new_size_vars $ \var ->- addUsesBefore var declarations_so_far- addDeclarations new_size_vars
− src/Futhark/Optimise/MemoryBlockMerging/Reuse/AllocationSizes.hs
@@ -1,132 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}--- | Find all Alloc statements and associate their memory blocks with the--- allocation size.-module Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes- ( memBlockSizesFunDef, memBlockSizesParamsBodyNonRec- , Sizes- ) where--import qualified Data.Map.Strict as M-import Control.Monad.Writer--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory- (ExplicitMemorish, ExplicitMemory, InKernel)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.Types-import Futhark.Optimise.MemoryBlockMerging.Miscellaneous----- | maps memory blocks to its size and space/type-type Sizes = M.Map MName (SubExp, Space) -- Also Space information--newtype FindM lore a = FindM { unFindM :: Writer Sizes a }- deriving (Monad, Functor, Applicative,- MonadWriter Sizes)--type LoreConstraints lore = (ExplicitMemorish lore,- AllocSizeUtils lore,- FullWalk lore)--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM--recordMapping :: VName -> (SubExp, Space) -> FindM lore ()-recordMapping var (size, space) = tell $ M.singleton var (size, space)--memBlockSizesFunDef :: LoreConstraints lore =>- FunDef lore -> Sizes-memBlockSizesFunDef fundef =- let m = unFindM $ do- mapM_ lookInFParam $ funDefParams fundef- lookInBody $ funDefBody fundef- mem_sizes = execWriter m- in mem_sizes--memBlockSizesParamsBodyNonRec :: LoreConstraints lore =>- [FParam lore] -> Body lore -> Sizes-memBlockSizesParamsBodyNonRec params body =- let m = unFindM $ do- mapM_ lookInFParam params- mapM_ lookInStm $ bodyStms body- mem_sizes = execWriter m- in mem_sizes--lookInFParam :: LoreConstraints lore =>- FParam lore -> FindM lore ()-lookInFParam (Param mem (ExpMem.MemMem size space)) =- recordMapping mem (size, space)-lookInFParam _ = return ()--lookInLParam :: LoreConstraints lore =>- LParam lore -> FindM lore ()-lookInLParam (Param mem (ExpMem.MemMem size space)) =- recordMapping mem (size, space)-lookInLParam _ = return ()--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStmRec bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStmRec bnds--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm (Let (Pattern patctxelems patvalelems) _ e) = do- case patvalelems of- [PatElem mem _] -> case lookForAllocSize e of- Just (size, space) ->- recordMapping mem (size, space)- Nothing -> return ()- _ -> return ()- mapM_ lookInPatCtxElem patctxelems--lookInStmRec :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStmRec stm@(Let _ _ e) = do- lookInStm stm-- fullWalkExpM walker walker_kernel e- where walker = identityWalker- { walkOnBody = lookInBody- , walkOnFParam = lookInFParam- , walkOnLParam = lookInLParam- }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInLambda- , walkOnKernelLParam = lookInLParam- }--lookInPatCtxElem :: LoreConstraints lore =>- PatElem lore -> FindM lore ()-lookInPatCtxElem (PatElem mem (ExpMem.MemMem size space)) =- recordMapping mem (size, space)-lookInPatCtxElem _ = return ()--lookInLambda :: LoreConstraints lore =>- Lambda lore -> FindM lore ()-lookInLambda (Lambda params body _) = do- forM_ params lookInLParam- lookInBody body--class AllocSizeUtils lore where- lookForAllocSize :: Exp lore -> Maybe (SubExp, Space)--instance AllocSizeUtils ExplicitMemory where- lookForAllocSize (Op (ExpMem.Alloc size space)) = Just (size, space)- lookForAllocSize _ = Nothing--instance AllocSizeUtils InKernel where- lookForAllocSize (Op (ExpMem.Alloc size space)) = Just (size, space)- lookForAllocSize _ = Nothing
− src/Futhark/Optimise/MemoryBlockMerging/Reuse/Core.hs
@@ -1,747 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE TupleSections #-}--- | Find array creations that can be set to use existing memory blocks instead--- of new allocations.-module Futhark.Optimise.MemoryBlockMerging.Reuse.Core- ( coreReuseFunDef- ) where--import qualified Data.Set as S-import qualified Data.Map.Strict as M-import qualified Data.List as L-import Data.Maybe (catMaybes, fromMaybe, isJust)-import Control.Monad-import Control.Monad.RWS-import Control.Monad.State-import Control.Monad.Identity--import Futhark.MonadFreshNames-import Futhark.Binder-import Futhark.Construct-import Futhark.Representation.AST-import Futhark.Analysis.PrimExp-import Futhark.Analysis.PrimExp.Convert-import Futhark.Representation.ExplicitMemory- (ExplicitMemory, ExplicitMemorish)-import Futhark.Pass.ExplicitAllocations()-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import qualified Futhark.Representation.ExplicitMemory.IndexFunction as IxFun-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.PrimExps (findPrimExpsFunDef)-import Futhark.Optimise.MemoryBlockMerging.Miscellaneous-import Futhark.Optimise.MemoryBlockMerging.Types-import Futhark.Optimise.MemoryBlockMerging.MemoryUpdater--import Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizes-import Futhark.Optimise.MemoryBlockMerging.Reuse.AllocationSizeUses---data Context = Context { ctxFirstUses :: FirstUses- -- ^ From the module Liveness.FirstUses- , ctxInterferences :: Interferences- , ctxPotentialKernelInterferences- :: PotentialKernelDataRaceInterferences- -- ^ From the module Liveness.Interferences- , ctxSizes :: Sizes- -- ^ maps a memory block to its size and space- , ctxVarToMem :: VarMemMappings MemorySrc- -- ^ From the module VariableMemory- , ctxActualVars :: M.Map VName Names- -- ^ From the module ActualVariables- , ctxExistentials :: Names- -- ^ From the module Existentials- , ctxVarPrimExps :: M.Map VName (PrimExp VName)- -- ^ From the module PrimExps- , ctxSizeVarsUsesBefore :: M.Map VName Names- -- ^ maps a memory name to the size variables available- -- at that memory block allocation point- }- deriving (Show)--data Current = Current { curUses :: M.Map MName MNames- -- ^ maps a memory block to the memory blocks that- -- have been merged into it so far- , curEqAsserts :: M.Map VName Names- -- ^ maps a variable name to other semantically equal- -- variable names-- , curVarToMemRes :: VarMemMappings MemoryLoc- -- ^ The result of the core analysis: maps an array- -- name to its memory block.-- , curVarToMaxExpRes :: M.Map MName Names- -- ^ Changes in variable uses where allocation sizes- -- are maxed from its elements. Keyed by statement- -- memory name (alloc stmt). Maps an alloc stmt to the- -- sizes that need to be taken max for.-- , curKernelMaxSizedRes :: M.Map MName (VName,- ((VName, VName),- (VName, VName)))- -- ^ Maps an alloc stmt to- -- (size0,- -- ((array0, size_var0, ixfun0),- -- (array1, size_var1, ixfun1))).- --- -- Needed for array creations in kernel- -- bodies that can only reuse memory if index functions- -- are changed, and the allocation size is maxed.- --- -- size_var0 is *not* the size of the entire allocation- -- of the key memory, but *part of* the allocation- -- size. This part will be replaced by the maximum of- -- the two sizes.- }- deriving (Show)--emptyCurrent :: Current-emptyCurrent = Current { curUses = M.empty- , curEqAsserts = M.empty- , curVarToMemRes = M.empty- , curVarToMaxExpRes = M.empty- , curKernelMaxSizedRes = M.empty- }--newtype FindM lore a = FindM { unFindM :: RWS Context () Current a }- deriving (Monad, Functor, Applicative,- MonadReader Context,- MonadState Current)--type LoreConstraints lore = (ExplicitMemorish lore,- FullWalk lore)--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM---- Lookup the memory block statically associated with a variable.-lookupVarMem :: MonadReader Context m =>- VName -> m MemorySrc-lookupVarMem var =- -- This should always be called from a place where it is certain that 'var'- -- refers to a statement with an array expression.- fromJust ("lookup memory block from " ++ pretty var) . M.lookup var- <$> asks ctxVarToMem--lookupActualVars' :: ActualVariables -> VName -> Names-lookupActualVars' actual_vars var =- -- Do this recursively.- let actual_vars' = expandWithAliases actual_vars actual_vars- in fromMaybe (S.singleton var) $ M.lookup var actual_vars'--lookupActualVars :: MonadReader Context m =>- VName -> m Names-lookupActualVars var = asks $ flip lookupActualVars' var . ctxActualVars--lookupSize :: MonadReader Context m =>- VName -> m SubExp-lookupSize var =- fst . fromJust ("lookup size from " ++ pretty var) . M.lookup var- <$> asks ctxSizes--lookupSpace :: MonadReader Context m =>- MName -> m Space-lookupSpace mem =- snd . fromJust ("lookup space from " ++ pretty mem) . M.lookup mem- <$> asks ctxSizes---- Record that the existing old_mem now also "is the same as" new_mem.-insertUse :: VName -> VName -> FindM lore ()-insertUse old_mem new_mem =- modify $ \cur -> cur { curUses = insertOrUpdate old_mem new_mem $ curUses cur }--recordMemMapping :: VName -> MemoryLoc -> FindM lore ()-recordMemMapping x mem =- modify $ \cur -> cur { curVarToMemRes = M.insert x mem $ curVarToMemRes cur }--recordMaxMapping :: MName -> VName -> FindM lore ()-recordMaxMapping mem y =- modify $ \cur -> cur { curVarToMaxExpRes = insertOrUpdate mem y- $ curVarToMaxExpRes cur }--recordKernelMaxMapping :: MName -> (VName, ((VName, VName), (VName, VName)))- -> FindM lore ()-recordKernelMaxMapping mem info =- modify $ \cur -> cur { curKernelMaxSizedRes =- M.insert mem info $ curKernelMaxSizedRes cur- }--modifyCurEqAsserts :: (M.Map VName Names -> M.Map VName Names) -> FindM lore ()-modifyCurEqAsserts f = modify $ \c -> c { curEqAsserts = f $ curEqAsserts c }---- Run a monad with a local copy of the uses. We don't want any new uses in--- nested bodies to be available for merging into when we are back in the main--- body, but we do want updates to existing uses to be propagated.-withLocalUses :: FindM lore a -> FindM lore a-withLocalUses m = do- uses_before <- gets curUses- res <- m- uses_after <- gets curUses- -- Only take the results whose memory block keys were also present prior to- -- traversing the sub-body.- let uses_before_updated = M.filterWithKey- (\mem _ -> mem `S.member` M.keysSet uses_before)- uses_after- modify $ \cur -> cur { curUses = uses_before_updated }- return res--coreReuseFunDef :: MonadFreshNames m =>- FunDef ExplicitMemory -> FirstUses ->- Interferences -> PotentialKernelDataRaceInterferences ->- VarMemMappings MemorySrc -> ActualVariables -> Names ->- m (FunDef ExplicitMemory)-coreReuseFunDef fundef first_uses interferences potential_kernel_interferences var_to_mem actual_vars existentials = do- let sizes = memBlockSizesFunDef fundef- size_uses = findSizeUsesFunDef fundef- var_to_pe = findPrimExpsFunDef fundef- context = Context- { ctxFirstUses = first_uses- , ctxInterferences = interferences- , ctxPotentialKernelInterferences = potential_kernel_interferences- , ctxSizes = sizes- , ctxVarToMem = var_to_mem- , ctxActualVars = actual_vars- , ctxExistentials = existentials- , ctxVarPrimExps = var_to_pe- , ctxSizeVarsUsesBefore = size_uses- }- m = unFindM $ do- forM_ (funDefParams fundef) lookInFParam- lookInBody $ funDefBody fundef- (res, ()) = execRWS m context emptyCurrent- var_to_mem_res = curVarToMemRes res- fundef' <- transformFromVarMemMappings var_to_mem_res (M.map memSrcName var_to_mem) (M.map fst sizes) (M.map fst sizes) False fundef- let sizes' = memBlockSizesFunDef fundef'- fundef'' <- transformFromVarMaxExpMappings (curVarToMaxExpRes res) fundef'- transformFromKernelMaxSizedMappings var_to_pe var_to_mem (M.map memLocName var_to_mem_res) sizes' actual_vars (curKernelMaxSizedRes res) fundef''--lookInFParam :: LoreConstraints lore =>- FParam lore -> FindM lore ()-lookInFParam (Param _ membound) =- -- Unique array function parameters also count as "allocations" in which- -- memory can be reused.- case membound of- ExpMem.MemArray _ _ Unique (ExpMem.ArrayIn mem _) ->- insertUse mem mem- _ -> return ()--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm (Let (Pattern _patctxelems patvalelems) _ e) = do- var_to_pe <- asks ctxVarPrimExps- let eqs | BasicOp (Assert (Var v) _ _) <- e- , Just (CmpOpExp (CmpEq _) (LeafExp v0 _) (LeafExp v1 _)) <- M.lookup v var_to_pe = do- modifyCurEqAsserts $ insertOrUpdate v0 v1- modifyCurEqAsserts $ insertOrUpdate v1 v0- | otherwise = return ()- eqs-- forM_ patvalelems $ \(PatElem var membound) -> do- -- For every declaration with a first memory use, check (through- -- handleNewArray) if it can reuse some earlier memory block.- first_uses_var <- lookupEmptyable var <$> asks ctxFirstUses- actual_vars_var <- lookupActualVars var- existentials <- asks ctxExistentials- case membound of- ExpMem.MemArray _ _ _ (ExpMem.ArrayIn mem _) ->- when (-- We require that it must be a first use, i.e. an array creation.- mem `S.member` first_uses_var- -- If the array is existential or "aliases" something that is- -- existential, we do not try to make it reuse any memory.- && not (var `S.member` existentials)- && not (any (`S.member` existentials) actual_vars_var))- $ handleNewArray var mem- _ -> return ()-- fullWalkExpM walker walker_kernel e-- where walker = identityWalker- { walkOnBody = withLocalUses . lookInBody }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . withLocalUses . lookInBody- , walkOnKernelKernelBody = coerce . withLocalUses . lookInKernelBody- , walkOnKernelLambda = coerce . withLocalUses . lookInBody . lambdaBody- }---- Check if a new array declaration x with a first use of the memory xmem can be--- set to use a previously encountered memory block.-handleNewArray :: VName -> MName -> FindM lore ()-handleNewArray x xmem = do- interferences <- asks ctxInterferences- actual_vars <- lookupActualVars x-- let notTheSame :: Monad m => MName -> MNames -> m Bool- notTheSame kmem _used_mems = return (kmem /= xmem)-- let noneInterfere :: Monad m => MName -> MNames -> m Bool- noneInterfere _kmem used_mems =- -- A memory block can have already been reused. We also check for- -- interference with any previously merged blocks.- return $ all (\used_mem -> not $ S.member xmem- $ lookupEmptyable used_mem interferences)- $ S.toList used_mems-- let noneInterfereKernelArray :: MonadReader Context m => MNames -> m Bool- noneInterfereKernelArray used_mems =- not <$> anyM (interferesInKernel xmem) (S.toList used_mems)-- let sameSpace :: MonadReader Context m =>- MName -> MNames -> m Bool- sameSpace kmem _used_mems = do- kspace <- lookupSpace kmem- xspace <- lookupSpace xmem- return (kspace == xspace)-- -- Is the size of the new memory block (xmem) equal to any of the memory- -- blocks (used_mems) using an already used memory block?- let sizesMatch :: MNames -> FindM lore Bool- sizesMatch used_mems = do- ok_sizes <- mapM lookupSize $ S.toList used_mems- new_size <- lookupSize xmem- -- Check for size equality by checking for variable name equality.- let eq_simple = new_size `L.elem` ok_sizes-- -- Check for size equality by constructing 'PrimExp's and comparing- -- those. Use the custom VarWithLooseEquality type to compare inner- -- sizes: If an equality assert statement was found earlier, consider- -- its two operands to be the same.- var_to_pe <- asks ctxVarPrimExps- eq_asserts <- gets curEqAsserts- let sePrimExp se = do- v <- subExpVar se- pe <- M.lookup v var_to_pe- let pe_expanded = expandPrimExp var_to_pe pe- traverse (\v_inner -> -- Has custom Eq instance.- pure $ VarWithLooseEquality v_inner- $ lookupEmptyable v_inner eq_asserts- ) pe_expanded- let ok_sizes_pe = map sePrimExp ok_sizes- let new_size_pe = sePrimExp new_size-- -- If new_size_pe actually denotes a PrimExp, check if it is among the- -- constructed 'PrimExp's of the sizes of the memory blocks that have- -- already been set to use the target memory block.- let eq_advanced = isJust new_size_pe && new_size_pe `L.elem` ok_sizes_pe-- return (eq_simple || eq_advanced)-- -- In case sizes do not match: Is it possible to change the size of the target- -- memory block to be a maximum of itself and the new memory block?- let sizesCanBeMaxed :: MName -> FindM lore Bool- sizesCanBeMaxed kmem = do- ksize <- lookupSize kmem- xsize <- lookupSize xmem- uses_before <- asks ctxSizeVarsUsesBefore- let ok = fromMaybe False $ do- ksize' <- subExpVar ksize- xsize' <- subExpVar xsize- return (xsize' `S.member` fromJust ("is recorded for all size variables "- ++ pretty ksize')- (M.lookup ksize' uses_before))- return ok-- let sizesCanBeMaxedKernelArray :: MName -> MNames ->- FindM lore (Maybe (VName, ((VName, VName),- (VName, VName))))- sizesCanBeMaxedKernelArray kmem used_mems = do- -- Let a kernel body have two indexed array creations result_0 and- -- result_1 with the index functions- --- -- result_0: ixfun_start_0[indices_start_0, 0i64:+res_0*1i64]- -- result_1: ixfun_start_1[indices_start_1, 0i64:+res_1*1i64]- --- -- with the additional requirements that- --- -- + ixfun_start_0 is equal to ixfun_start_1 except for mentions of- -- res_0 and res_1.- --- -- + indices_start_0 is equal to indices_start_1.- --- -- Example:- --- -- result_0: Direct(num_groups, res_0, group_size)[0, 2, 1][group_id, local_tid, 0i64:+res_0*1i64]- -- result_1: Direct(num_groups, res_1, group_size)[0, 2, 1][group_id, local_tid, 0i64:+res_1*1i64]- --- -- By default result_0 and result_1 will be set to interfere because- -- each thread can access parts of the memory of another thread if they- -- are merged. We can fix this my making both index functions describe- -- the same access pattern except for the final dimension. We want this- -- to happen for the example above:- --- -- result_0': Direct(num_groups, res_max, group_size)[0, 2, 1][group_id, local_tid, 0i64:+res_0*1i64]- -- result_1': Direct(num_groups, res_max, group_size)[0, 2, 1][group_id, local_tid, 0i64:+res_1*1i64]- --- -- Where res_max = max(res_0, res_1). Now they cover the same area in- -- space. The final index slices are kept as they were, since the shape- -- of the created array should stay the same. This means that the- -- smallest array will not be writing to all of its available space.- --- -- We need to check:- --- -- + Is res_1 in scope at the allocation? Allocation size hoisting- -- has probably been helpful here.- --- -- + Does res_0 and res_1 have the same base type size?- --- -- If true, modify the program as such:- --- -- + Insert a res_max statement before the allocation.- --- -- + Change the allocation size to use res_max instead of res_0.- --- -- + Modify both index functions to use res_max instead of res_0 and- -- res_1, respectively, except for at the final index slice.- --- -- Extension: If an array reuses an already reused array, remember to- -- update *all* index functions. Currently we avoid these cases for- -- simplicity of implementation.-- potentials <- asks ctxPotentialKernelInterferences- uses_before <- asks ctxSizeVarsUsesBefore-- let first_usess = filter (\p ->- let pot_mems = map (\(m, _, _, _) -> m) p- in kmem `elem` pot_mems && xmem `elem` pot_mems)- potentials- kmem_size <- fromJust "should be a var" . subExpVar <$> lookupSize kmem-- return $ case (S.toList used_mems, first_usess) of- -- We only support the basic case for now. FIXME (or, at the very- -- least, manage to create a program where this will have an effect).- --- -- A used_mems list of size > 1 means that kmem has already been- -- reused. This is okay, but a bit harder to keep track of.- --- -- A first_usess list of size > 1 means that xmem and kmem- -- data-race-interfere in multiple kernels. This will never happen in- -- the current implementation, but could *potentially* happen in the- -- future.- ([_], [first_uses]) -> do- (_, kmem_array, kmem_pt, kmem_ixfun) <-- L.find (\(mname, _, _, _) -> mname == kmem) first_uses- (_, xmem_array, xmem_pt, xmem_ixfun) <-- L.find (\(mname, _, _, _) -> mname == xmem) first_uses-- if (kmem, kmem_ixfun) `ixFunsCompatible` (xmem, xmem_ixfun)- then Nothing -- These are not special, and need not special handling.- else do- (kmem_ixfun_start, kmem_indices_start, kmem_final_dim) <-- IxFun.getInfoMaxUnification kmem_ixfun- (xmem_ixfun_start, xmem_indices_start, xmem_final_dim) <-- IxFun.getInfoMaxUnification xmem_ixfun-- let xmem_final_dim_before_kmem_final_dim =- maybe False (xmem_final_dim `S.member`) $- M.lookup kmem_final_dim uses_before- kmem_ixfun_start' = getIxFun' kmem_ixfun_start- (M.singleton kmem_final_dim xmem_final_dim)- xmem_ixfun_start' = getIxFun' xmem_ixfun_start- (M.singleton xmem_final_dim kmem_final_dim)-- res = if kmem_indices_start == xmem_indices_start &&- (kmem, kmem_ixfun_start') `ixFunsCompatible`- (xmem, xmem_ixfun_start') &&- (primByteSize kmem_pt :: Int) == primByteSize xmem_pt &&- xmem_final_dim_before_kmem_final_dim- then return (kmem_size,- ((kmem_array, kmem_final_dim),- (xmem_array, xmem_final_dim)))- else Nothing-- in res- _ -> Nothing-- where getIxFun' :: ExpMem.IxFun -> M.Map VName VName ->- IxFun.IxFun (PrimExp VarWithLooseEquality)- getIxFun' ixfun others =- let loose_eq_map name_inner =- -- Has custom Eq instance.- pure $ VarWithLooseEquality name_inner- $ maybe S.empty S.singleton $ M.lookup name_inner others- in runIdentity $ traverse (traverse loose_eq_map) ixfun-- let sizesCanBeMaxedKernelArray' :: MName -> MNames -> FindM lore Bool- sizesCanBeMaxedKernelArray' kmem used_mems =- isJust <$> sizesCanBeMaxedKernelArray kmem used_mems-- let noOtherUsesOfMemory :: MName -> MNames -> FindM lore Bool- noOtherUsesOfMemory _kmem _used_mems =- -- If the array in question 'x' is not the only array that uses the- -- memory (ignoring aliasing), then do not perform memory reuse. We- -- only want to reuse memory if it means we can remove an allocation.- -- FIXME: If we can check that all arrays using the memory in question- -- 'xmem' can be set to reuse some other memory, so that 'xmem' does not- -- have to be allocated, then this restriction can go away. It also- -- might be the case that the ActualVariables module does not find all- -- array connections, i.e. it concludes that two arrays are distinct- -- when they are actually not; this can happen with streams.- and . M.elems . M.mapWithKey (- \v m -> (memSrcName m /= xmem)- || (v `L.elem` actual_vars)- ) <$> asks ctxVarToMem-- let notCurrentlyDisabled :: FindM lore Bool- notCurrentlyDisabled =- -- FIXME: We currently disable reusing memory of constant size. This is- -- a problem in the misc/heston/heston32.fut benchmark (but not the- -- heston64.fut one). It would be nice to not have to disable this- -- feature, as it works well for the most part. Why is this a problem?- -- Or is it maybe something else that causes heston32 to segfault?- isJust . subExpVar <$> lookupSize xmem-- let sizesWorkOut :: MName -> MNames -> FindM lore Bool- sizesWorkOut kmem used_mems =- -- The size of an allocation is okay to reuse if it is the same as the- -- current memory size, or if it can be changed to be the maximum size- -- of the two sizes.- (notCurrentlyDisabled <&&> noneInterfereKernelArray used_mems <&&>- (sizesMatch used_mems <||> sizesCanBeMaxed kmem))- <||> sizesCanBeMaxedKernelArray' kmem used_mems-- let canBeUsed t = and <$> mapM (($ t) . uncurry)- [notTheSame, noneInterfere, sameSpace, noOtherUsesOfMemory,- sizesWorkOut]- cur_uses <- gets curUses- found_use <- catMaybes <$> mapM (maybeFromBoolM canBeUsed) (M.assocs cur_uses)-- case found_use of- (kmem, used_mems) : _ -> do- -- There is a previous memory block that we can use. Record the mapping.- insertUse kmem xmem- forM_ actual_vars $ \var -> do- ixfun <- memSrcIxFun <$> lookupVarMem var- recordMemMapping var $ MemoryLoc kmem ixfun -- Only change the memory block.-- -- Record any size-maximum change in case of sizesCanBeMaxed returning- -- True.- whenM (sizesCanBeMaxed kmem) $ do- ksize <- lookupSize kmem- xsize <- lookupSize xmem- fromMaybe (return ()) $ do- ksize' <- subExpVar ksize- xsize' <- subExpVar xsize- return $ do- recordMaxMapping kmem ksize'- recordMaxMapping kmem xsize'-- -- If we are inside a kernel body, and the current array can use the- -- memory block of another array if its size gets maximised, record this- -- change. The actual program transformation will happen later.- kernel_maxing <- sizesCanBeMaxedKernelArray kmem used_mems- forM_ kernel_maxing $ \info ->- recordKernelMaxMapping kmem info-- _ ->- -- There is no previous memory block available for use. Record that this- -- memory block is available.- insertUse xmem xmem--data VarWithLooseEquality = VarWithLooseEquality VName Names- deriving (Show)--instance Eq VarWithLooseEquality where- VarWithLooseEquality v0 vs0 == VarWithLooseEquality v1 vs1 =- not $ S.null $ S.intersection (S.insert v0 vs0) (S.insert v1 vs1)--interferesInKernel :: MonadReader Context m => MName -> MName -> m Bool-interferesInKernel mem0 mem1 = do- potentials <- asks ctxPotentialKernelInterferences-- let interferesInGroup :: PotentialKernelDataRaceInterferenceGroup -> Bool- interferesInGroup first_uses = fromMaybe False $ do- (_, _, pt0, ixfun0) <- L.find (\(mname, _, _, _) -> mname == mem0) first_uses- (_, _, pt1, ixfun1) <- L.find (\(mname, _, _, _) -> mname == mem1) first_uses- return $ interferes (pt0, ixfun0) (pt1, ixfun1)-- interferes :: (PrimType, ExpMem.IxFun) -> (PrimType, ExpMem.IxFun) -> Bool- interferes (pt0, ixfun0) (pt1, ixfun1) =- -- Must be different.- mem0 /= mem1 &&- (- -- Do the index functions range over different memory areas?- ((ixFunHasIndex ixfun0 || ixFunHasIndex ixfun1) &&- not (ixFunsCompatible (mem0, ixfun0) (mem1, ixfun1)))- ||- -- Do the arrays have different base type size? If so, they take- -- up different amounts of space, and will not be compatible.- ((primByteSize pt0 :: Int) /= primByteSize pt1)- )-- return $ any interferesInGroup potentials---- Does an index function contain an Index expression?------ If the index function of the memory annotation uses an index, it means that--- the array creation does not refer to the entire array. It is an array--- creation, but only partially: It creates part of the array, and another part--- is created in another loop iteration or kernel thread. The danger in--- declaring this memory a first use lies in how it can then be reused later in--- the iteration/thread by some memory with a *different* index in its memory--- annotation index function, which can affect reads in other threads.-ixFunHasIndex :: IxFun.IxFun num -> Bool-ixFunHasIndex = IxFun.ixFunHasIndex---- Do the two index functions describe the same range? In other words, does one--- array take up precisely the same location (offset) and size as another array--- relative to the beginning of their respective memory blocks? FIXME: This can--- be less conservative, for example by handling that different reshapes of the--- same array can describe the same offset and space, but do we have any tests--- or benchmarks where that occurs?-ixFunsCompatible :: Eq v =>- (MName, IxFun.IxFun (PrimExp v)) -> (MName, IxFun.IxFun (PrimExp v)) ->- Bool-ixFunsCompatible (_mem0, ixfun0) (_mem1, ixfun1) =- IxFun.ixFunsCompatibleRaw ixfun0 ixfun1---- Replace certain allocation sizes in a program with new variables describing--- the maximum of two or more allocation sizes.-transformFromVarMaxExpMappings :: MonadFreshNames m =>- M.Map VName Names- -> FunDef ExplicitMemory -> m (FunDef ExplicitMemory)-transformFromVarMaxExpMappings var_to_max fundef = do- var_to_new_var <-- M.fromList <$> mapM (\(k, v) -> (k,) <$> maxsToReplacement (S.toList v))- (M.assocs var_to_max)- return $ insertAndReplace var_to_new_var fundef---- A replacement is a new size variable and any new subexpressions that the new--- variable depends on.-data Replacement = Replacement- { replName :: VName -- The new variable- , replStms :: [Stm ExplicitMemory] -- The new expressions- }- deriving (Show)---- Take a list of size variables. Return a replacement consisting of a size--- variable denoting the maximum of the input sizes.-maxsToReplacement :: MonadFreshNames m =>- [VName] -> m Replacement-maxsToReplacement [] = error "maxsToReplacements: Cannot take max of zero variables"-maxsToReplacement [v] = return $ Replacement v []-maxsToReplacement vs = do- -- Should be O(lg N) number of new expressions.- let (vs0, vs1) = splitAt (length vs `div` 2) vs- Replacement m0 es0 <- maxsToReplacement vs0- Replacement m1 es1 <- maxsToReplacement vs1- vmax <- newVName "max"- let emax = BasicOp $ BinOp (SMax Int64) (Var m0) (Var m1)- new_stm = Let (Pattern [] [PatElem vmax- (ExpMem.MemPrim (IntType Int64))]) (defAux ()) emax- prev_stms = es0 ++ es1 ++ [new_stm]- return $ Replacement vmax prev_stms---- Modify a function to use the new replacements.-insertAndReplace :: M.Map MName Replacement -> FunDef ExplicitMemory ->- FunDef ExplicitMemory-insertAndReplace replaces0 fundef =- let body' = evalState (transformBody $ funDefBody fundef) replaces0- in fundef { funDefBody = body' }-- where transformBody :: Body ExplicitMemory ->- State (M.Map VName Replacement) (Body ExplicitMemory)- transformBody body = do- stms' <- concat <$> mapM transformStm (stmsToList $ bodyStms body)- return $ body { bodyStms = stmsFromList stms' }-- transformStm :: Stm ExplicitMemory ->- State (M.Map VName Replacement) [Stm ExplicitMemory]- transformStm stm@(Let (Pattern [] [PatElem mem_name- (ExpMem.MemMem _ pat_space)]) _- (Op (ExpMem.Alloc _ space))) = do- replaces <- get- case M.lookup mem_name replaces of- Just repl -> do- let prev = replStms repl- new = Let (Pattern [] [PatElem mem_name- (ExpMem.MemMem (Var (replName repl))- pat_space)]) (defAux ())- (Op (ExpMem.Alloc (Var (replName repl)) space))- -- We should only generate the new statements once.- modify $ M.adjust (\repl0 -> repl0 { replStms = [] }) mem_name- return (prev ++ [new])- Nothing -> return [stm]- transformStm (Let pat attr e) = do- let mapper = identityMapper { mapOnBody = const transformBody }- e' <- mapExpM mapper e- return [Let pat attr e']----- Change certain allocation sizes in a program.-transformFromKernelMaxSizedMappings :: MonadFreshNames m =>- M.Map VName (PrimExp VName) -> VarMemMappings MemorySrc -> VarMemMappings MName ->- Sizes -> ActualVariables -> M.Map MName (VName, ((VName, VName),- (VName, VName))) ->- FunDef ExplicitMemory -> m (FunDef ExplicitMemory)-transformFromKernelMaxSizedMappings- var_to_pe var_to_mem var_to_mem_res sizes_orig actual_vars mem_to_info fundef = do- (mem_to_size_var, arr_to_mem_ixfun) <-- unzip <$> mapM (uncurry withNewMaxVar) (M.assocs mem_to_info)- let mem_to_size_var' = M.fromList mem_to_size_var- arr_to_memloc = M.fromList $ map (\(arr, destmem, ixfun) ->- (arr, MemoryLoc destmem ixfun))- $ concat arr_to_mem_ixfun-- fundef' = insertAndReplace mem_to_size_var' fundef- sizes = memBlockSizesFunDef fundef'- transformFromVarMemMappings arr_to_memloc (M.union var_to_mem_res (M.map memSrcName var_to_mem)) (M.map fst sizes) (M.map fst sizes_orig) True fundef'-- where withNewMaxVar :: MonadFreshNames m =>- MName -> (VName,- ((VName, VName),- (VName, VName))) ->- m ((MName, Replacement),- [(VName, MName, ExpMem.IxFun)])- withNewMaxVar mem (kmem_size,- ((kmem_array, kmem_final_dim),- (xmem_array, xmem_final_dim))) = do- final_dim_max_v <- newVName "max_final_dim"- let final_dim_max_e =- BasicOp (BinOp (SMax Int32)- (Var kmem_final_dim) (Var xmem_final_dim))-- var_to_pe_extension =- M.singleton kmem_final_dim (LeafExp final_dim_max_v (IntType Int32))- var_to_pe' = M.union var_to_pe_extension var_to_pe- full_size_pe = fromJust "should exist" $ M.lookup kmem_size var_to_pe- full_size_pe_expanded = expandPrimExp var_to_pe' full_size_pe- new_full_size_m =- letExp "max" =<< primExpToExp (return . BasicOp . SubExp . Var)- full_size_pe_expanded- (alloc_size_var, alloc_size_stms) <-- modifyNameSource $ runState $ runBinderT new_full_size_m mempty- let alloc_size_fd_stm =- Let (Pattern [] [PatElem final_dim_max_v- (ExpMem.MemPrim (IntType Int32))]) (defAux ()) final_dim_max_e- alloc_size_stms' = oneStm alloc_size_fd_stm <> alloc_size_stms-- vars_kmem =- S.insert kmem_array $ lookupActualVars' actual_vars kmem_array- vars_xmem =- S.insert xmem_array $ lookupActualVars' actual_vars xmem_array-- arrayToMapping final_dim v =- let ixfun = memSrcIxFun $ fromJust "should exist"- $ M.lookup v var_to_mem- ixfun_new = IxFun.subsInIndexIxFun ixfun final_dim final_dim_max_v --newIxFun ixfun final_dim- in (v, mem, ixfun_new)- arr_to_mem_ixfun_kmem = map (arrayToMapping kmem_final_dim)- $ S.toList vars_kmem- arr_to_mem_ixfun_xmem = map (arrayToMapping xmem_final_dim)- $ S.toList vars_xmem- arr_to_mem_ixfun = arr_to_mem_ixfun_kmem ++ arr_to_mem_ixfun_xmem-- return ((mem, Replacement alloc_size_var $ stmsToList alloc_size_stms'),- arr_to_mem_ixfun)
− src/Futhark/Optimise/MemoryBlockMerging/Types.hs
@@ -1,89 +0,0 @@-module Futhark.Optimise.MemoryBlockMerging.Types- ( MName- , MNames- , MemorySrc(..)- , MemoryLoc(..)- , VarMemMappings- , MemAliases- , VarAliases- , FirstUses- , StmOrRes(..)- , LastUses- , Interferences- , ActualVariables- , PotentialKernelDataRaceInterferences- , PotentialKernelDataRaceInterferenceGroup- , KernelFirstUse- )-where--import qualified Data.Map.Strict as M--import Futhark.Representation.AST-import qualified Futhark.Representation.ExplicitMemory as ExpMem----- | Memory block VName.-type MName = VName---- | Memory block names.-type MNames = Names--data MemorySrc = MemorySrc- { memSrcName :: MName -- ^ the memory block name- , memSrcIxFun :: ExpMem.IxFun -- ^ the index function into the memory- , memSrcShape :: Shape -- ^ the shape of the original array- }- deriving (Show, Eq)--data MemoryLoc = MemoryLoc- { memLocName :: MName -- ^ the memory block name- , memLocIxFun :: ExpMem.IxFun -- ^ the index function into the memory- }- deriving (Show, Eq)---- A mapping from variable names to memory blocks (with varying details)-type VarMemMappings t = M.Map VName t---- Aliasing of memory blocks, meaning multiple memory blocks refer to the same--- actualy memory. Aliasing is not commutative.-type MemAliases = M.Map MName MNames---- Aliasing of variables, meaning the use the same memory blocks. Aliasing is--- commutative?-type VarAliases = M.Map VName Names---- First uses of memory blocks in statement denoted by variable name.-type FirstUses = M.Map VName MNames---- A last use can occur in a statement OR in a body result.-data StmOrRes = FromStm VName- | FromRes VName- deriving (Show, Eq, Ord)-type LastUses = M.Map StmOrRes MNames---- Interferences between memory blocks.-type Interferences = M.Map MName MNames---- Sets of potential interferences inside kernels because of potential data--- races. For each set, every memory block *can* interfere with every other--- memory block, but only in dire edge cases. Usually some of them can be said--- to not interfere, and sometimes array creation statements can be modified to--- have fewer interferences. See Reuse/Core.hs.-type PotentialKernelDataRaceInterferences =- [PotentialKernelDataRaceInterferenceGroup]-type PotentialKernelDataRaceInterferenceGroup = [KernelFirstUse]-type KernelFirstUse = (MName, VName, PrimType, ExpMem.IxFun)---- "Links" for handling how variables belong together.-type ActualVariables = M.Map VName Names---- Log keeping. Statement variable names to a list of topic-content-mappings.-newtype Log = Log (M.Map VName [(String, String)])- deriving (Show, Eq, Ord)--instance Semigroup Log where- Log a <> Log b = Log $ M.unionWith (++) a b--instance Monoid Log where- mempty = Log M.empty
− src/Futhark/Optimise/MemoryBlockMerging/VariableAliases.hs
@@ -1,82 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}--- | Find all variable aliases. Avoids having to use the Aliases representation--- in other modules.------ FIXME: This module is silly. It should be able to go away, with the other--- modules getting variable aliases by using the Aliases representation--- directly.-module Futhark.Optimise.MemoryBlockMerging.VariableAliases- ( findVarAliases- ) where--import qualified Data.Map.Strict as M-import qualified Data.Set as S-import Control.Monad.Writer--import Futhark.Representation.AST-import Futhark.Representation.Aliases (Aliases, unNames)-import Futhark.Representation.ExplicitMemory- (ExplicitMemorish, ExplicitMemory)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel-import Futhark.Analysis.Alias (analyseFun)--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous-import Futhark.Optimise.MemoryBlockMerging.Types---newtype FindM lore a = FindM { unFindM :: Writer [VarAliases] a }- deriving (Monad, Functor, Applicative,- MonadWriter [VarAliases])--type LoreConstraints lore = (ExplicitMemorish lore,- FullWalkAliases lore)--recordMapping :: VName -> Names -> FindM lore ()-recordMapping var names = tell [M.singleton var names]--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM---- | Find all variable aliases in a function definition.-findVarAliases :: FunDef ExplicitMemory -> VarAliases-findVarAliases fundef =- let fundef' = analyseFun fundef- m = unFindM $ lookInBody $ funDefBody fundef'- var_aliases = M.unionsWith S.union $ execWriter m- var_aliases' = removeEmptyMaps $ expandWithAliases var_aliases var_aliases- in var_aliases'--lookInBody :: LoreConstraints lore =>- Body (Aliases lore) -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody (Aliases lore) -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--lookInStm :: LoreConstraints lore =>- Stm (Aliases lore) -> FindM lore ()-lookInStm (Let (Pattern _patctxelems patvalelems) _ e) = do- mapM_ lookInPatValElem patvalelems- fullWalkAliasesExpM walker walker_kernel e- where walker = identityWalker- { walkOnBody = lookInBody- }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInBody . lambdaBody- }--lookInPatValElem :: LoreConstraints lore =>- PatElem (Aliases lore) -> FindM lore ()-lookInPatValElem (PatElem x (names', ExpMem.MemArray{})) = do- let aliases = unNames names'- recordMapping x aliases-lookInPatValElem _ = return ()
− src/Futhark/Optimise/MemoryBlockMerging/VariableMemory.hs
@@ -1,99 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}--- | Find all variable-to-memory mappings, so that other modules can lookup the--- relation. Maps array names to memory blocks.--module Futhark.Optimise.MemoryBlockMerging.VariableMemory- ( findVarMemMappings- ) where--import qualified Data.Map.Strict as M-import Control.Monad.Writer--import Futhark.Representation.AST-import Futhark.Representation.ExplicitMemory- (ExplicitMemorish, ExplicitMemory)-import qualified Futhark.Representation.ExplicitMemory as ExpMem-import Futhark.Representation.Kernels.Kernel--import Futhark.Optimise.MemoryBlockMerging.Miscellaneous-import Futhark.Optimise.MemoryBlockMerging.Types---newtype FindM lore a = FindM { unFindM :: Writer (VarMemMappings MemorySrc) a }- deriving (Monad, Functor, Applicative,- MonadWriter (VarMemMappings MemorySrc))--type LoreConstraints lore = (ExplicitMemorish lore,- FullWalk lore)--recordMapping :: VName -> MemorySrc -> FindM lore ()-recordMapping var memloc = tell $ M.singleton var memloc--coerce :: FindM flore a -> FindM tlore a-coerce = FindM . unFindM---- | Find all variable-memory block mappings in a function definition.-findVarMemMappings :: FunDef ExplicitMemory -> VarMemMappings MemorySrc-findVarMemMappings fundef =- let m = unFindM $ do- mapM_ lookInFParam $ funDefParams fundef- lookInBody $ funDefBody fundef- var_to_mem = execWriter m- in var_to_mem--lookInFParam :: LoreConstraints lore =>- FParam lore -> FindM lore ()-lookInFParam (Param x (ExpMem.MemArray _ shape _ (ExpMem.ArrayIn xmem xixfun))) = do- let memloc = MemorySrc xmem xixfun shape- recordMapping x memloc-lookInFParam _ = return ()--lookInLParam :: LoreConstraints lore =>- LParam lore -> FindM lore ()-lookInLParam (Param x (ExpMem.MemArray _ shape _ (ExpMem.ArrayIn xmem xixfun))) = do- let memloc = MemorySrc xmem xixfun shape- recordMapping x memloc-lookInLParam _ = return ()--lookInBody :: LoreConstraints lore =>- Body lore -> FindM lore ()-lookInBody (Body _ bnds _res) =- mapM_ lookInStm bnds--lookInKernelBody :: LoreConstraints lore =>- KernelBody lore -> FindM lore ()-lookInKernelBody (KernelBody _ bnds _res) =- mapM_ lookInStm bnds--lookInStm :: LoreConstraints lore =>- Stm lore -> FindM lore ()-lookInStm (Let (Pattern _patctxelems patvalelems) _ e) = do- mapM_ lookInPatValElem patvalelems- fullWalkExpM walker walker_kernel e- where walker = identityWalker- { walkOnBody = lookInBody- , walkOnFParam = lookInFParam- , walkOnLParam = lookInLParam- }- walker_kernel = identityKernelWalker- { walkOnKernelBody = coerce . lookInBody- , walkOnKernelKernelBody = coerce . lookInKernelBody- , walkOnKernelLambda = coerce . lookInLambda- , walkOnKernelLParam = lookInLParam- }--lookInPatValElem :: LoreConstraints lore =>- PatElem lore -> FindM lore ()-lookInPatValElem (PatElem x (ExpMem.MemArray _ shape _ (ExpMem.ArrayIn xmem xixfun))) = do- let memloc = MemorySrc xmem xixfun shape- recordMapping x memloc-lookInPatValElem _ = return ()--lookInLambda :: LoreConstraints lore =>- Lambda lore -> FindM lore ()-lookInLambda (Lambda params body _) = do- forM_ params lookInLParam- lookInBody body
src/Futhark/Pass/ExpandAllocations.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TypeFamilies, FlexibleContexts #-}+{-# LANGUAGE TypeFamilies, FlexibleContexts, GeneralizedNewtypeDeriving #-} -- | Expand allocations inside of maps when possible. module Futhark.Pass.ExpandAllocations ( expandAllocations )@@ -31,6 +31,7 @@ import Futhark.Util.IntegralExp import Futhark.Util (mapAccumLM) + expandAllocations :: Pass ExplicitMemory ExplicitMemory expandAllocations = Pass "expand allocations" "Expand allocations" $@@ -63,6 +64,12 @@ where transform = identityMapper { mapOnBody = \scope -> localScope scope . transformBody } +nameInfoConv :: NameInfo ExplicitMemory -> NameInfo InKernel+nameInfoConv (LetInfo mem_info) = LetInfo mem_info+nameInfoConv (FParamInfo mem_info) = FParamInfo mem_info+nameInfoConv (LParamInfo mem_info) = LParamInfo mem_info+nameInfoConv (IndexInfo it) = IndexInfo it+ transformExp :: Exp ExplicitMemory -> ExpandM (Stms ExplicitMemory, Exp ExplicitMemory) transformExp (Op (Inner (Kernel desc kspace ts kbody))) = do@@ -74,9 +81,10 @@ (alloc_stms, alloc_offsets) <- memoryRequirements kspace (kernelBodyStms kbody) variant_allocs invariant_allocs - kbody'' <- either compilerLimitationS pure $- offsetMemoryInKernelBody alloc_offsets- kbody'+ scope <- askScope+ let scope' = scopeOfKernelSpace kspace <> M.map nameInfoConv scope+ kbody'' <- either compilerLimitationS pure $+ runOffsetM scope' alloc_offsets $ offsetMemoryInKernelBody kbody' return (alloc_stms, Op $ Inner $ Kernel desc kspace ts kbody'')@@ -96,9 +104,11 @@ (alloc_stms, alloc_offsets) <- memoryRequirements kspace (bodyStms kbody) variant_allocs invariant_allocs - either compilerLimitationS pure $ do- kbody'' <- offsetMemoryInBody alloc_offsets kbody'- red_op'' <- offsetMemoryInLambda alloc_offsets red_op'+ scope <- askScope+ let scope' = scopeOfKernelSpace kspace <> M.map nameInfoConv scope+ either compilerLimitationS pure $ runOffsetM scope' alloc_offsets $ do+ kbody'' <- offsetMemoryInBody kbody'+ red_op'' <- localScope (scopeOf red_op') $ offsetMemoryInLambda red_op' return (alloc_stms, Op $ Inner $ SegRed kspace comm red_op'' nes ts kbody'')@@ -108,9 +118,53 @@ M.keys (scopeOfKernelSpace kspace <> scopeOf (bodyStms kbody)) +transformExp (Op (Inner (SegGenRed kspace ops ts kbody))) = do+ let (kbody', kbody_allocs) = extractBodyAllocations kbody+ (ops', ops_allocs) = unzip $ map extractGenRedOpAllocations ops+ variantAlloc (Var v) = v `S.member` bound_in_kernel+ variantAlloc _ = False+ allocs = kbody_allocs <> mconcat ops_allocs+ (variant_allocs, invariant_allocs) = M.partition (variantAlloc . fst) allocs++ allocsForBody variant_allocs invariant_allocs kspace kbody' $ \alloc_stms kbody'' -> do+ ops'' <- mapM offsetMemoryInGenRedOp ops'++ return (alloc_stms,+ Op $ Inner $ SegGenRed kspace ops'' ts kbody'')++ where bound_in_kernel =+ S.fromList $ map fst (spaceDimensions kspace) +++ M.keys (scopeOfKernelSpace kspace <>+ scopeOf (bodyStms kbody))++ extractGenRedOpAllocations op =+ let (lam, allocs) = extractLambdaAllocations $ genReduceOp op+ in (op { genReduceOp = lam }, allocs)++ offsetMemoryInGenRedOp op = do+ lam <- localScope (scopeOf (genReduceOp op)) $+ offsetMemoryInLambda $ genReduceOp op+ return op { genReduceOp = lam }+ transformExp e = return (mempty, e) +allocsForBody :: M.Map VName (SubExp, Space)+ -> M.Map VName (SubExp, Space)+ -> KernelSpace+ -> Body InKernel+ -> (Stms ExplicitMemory -> Body InKernel -> OffsetM b)+ -> ExpandM b+allocsForBody variant_allocs invariant_allocs kspace kbody' m = do+ (alloc_stms, alloc_offsets) <-+ memoryRequirements kspace (bodyStms kbody') variant_allocs invariant_allocs++ scope <- askScope+ let scope' = scopeOfKernelSpace kspace <> M.map nameInfoConv scope+ either compilerLimitationS pure $ runOffsetM scope' alloc_offsets $ do+ kbody'' <- offsetMemoryInBody kbody'+ m alloc_stms kbody''+ memoryRequirements :: KernelSpace -> Stms InKernel -> M.Map VName (SubExp, Space)@@ -280,100 +334,147 @@ type RebaseMap = M.Map VName (([PrimExp VName], PrimType) -> IxFun) -lookupNewBase :: VName -> ([PrimExp VName], PrimType) -> RebaseMap -> Maybe IxFun-lookupNewBase name x = fmap ($ x) . M.lookup name+newtype OffsetM a = OffsetM (ReaderT (Scope InKernel)+ (ReaderT RebaseMap (Either String)) a)+ deriving (Applicative, Functor, Monad,+ HasScope InKernel, LocalScope InKernel,+ MonadError String) -offsetMemoryInKernelBody :: RebaseMap -> KernelBody InKernel- -> Either String (KernelBody InKernel)-offsetMemoryInKernelBody initial_offsets kbody = do- stms' <- snd <$> mapAccumLM offsetMemoryInStm initial_offsets+runOffsetM :: Scope InKernel -> RebaseMap -> OffsetM a -> Either String a+runOffsetM scope offsets (OffsetM m) =+ runReaderT (runReaderT m scope) offsets++askRebaseMap :: OffsetM RebaseMap+askRebaseMap = OffsetM $ lift ask++lookupNewBase :: VName -> ([PrimExp VName], PrimType) -> OffsetM (Maybe IxFun)+lookupNewBase name x = do+ offsets <- askRebaseMap+ return $ ($ x) <$> M.lookup name offsets++offsetMemoryInKernelBody :: KernelBody InKernel -> OffsetM (KernelBody InKernel)+offsetMemoryInKernelBody kbody = do+ scope <- askScope+ stms' <- stmsFromList . snd <$>+ mapAccumLM (\scope' -> localScope scope' . offsetMemoryInStm) scope (stmsToList $ kernelBodyStms kbody)- return kbody { kernelBodyStms = stmsFromList stms' }+ return kbody { kernelBodyStms = stms' } -offsetMemoryInBody :: RebaseMap -> Body InKernel -> Either String (Body InKernel)-offsetMemoryInBody offsets (Body attr stms res) = do- stms' <- stmsFromList . snd <$> mapAccumLM offsetMemoryInStm offsets (stmsToList stms)+offsetMemoryInBody :: Body InKernel -> OffsetM (Body InKernel)+offsetMemoryInBody (Body attr stms res) = do+ scope <- askScope+ stms' <- stmsFromList . snd <$>+ mapAccumLM (\scope' -> localScope scope' . offsetMemoryInStm) scope+ (stmsToList stms) return $ Body attr stms' res -offsetMemoryInLambda :: RebaseMap -> Lambda InKernel -> Either String (Lambda InKernel)-offsetMemoryInLambda offset lam = do- body <- offsetMemoryInBody offset $ lambdaBody lam- return $ lam { lambdaBody = body }+offsetMemoryInStm :: Stm InKernel -> OffsetM (Scope InKernel, Stm InKernel)+offsetMemoryInStm (Let pat attr e) = do+ pat' <- offsetMemoryInPattern pat+ e' <- localScope (scopeOfPattern pat') $ offsetMemoryInExp e+ scope <- askScope+ -- Try to recompute the index function. Fall back to creating rebase+ -- operations with the RebaseMap.+ rts <- runReaderT (expReturns e') scope+ let pat'' = Pattern (patternContextElements pat')+ (zipWith pick (patternValueElements pat') rts)+ stm = Let pat'' attr e'+ let scope' = scopeOf stm <> scope+ return (scope', stm)+ where pick :: PatElemT (MemInfo SubExp NoUniqueness MemBind) ->+ ExpReturns -> PatElemT (MemInfo SubExp NoUniqueness MemBind)+ pick (PatElem name (MemArray pt s u _ret))+ (MemArray _ _ _ (Just (ReturnsInBlock m extixfun)))+ | Just ixfun <- instantiateIxFun extixfun =+ PatElem name (MemArray pt s u (ArrayIn m ixfun))+ pick p _ = p -offsetMemoryInStm :: RebaseMap -> Stm InKernel- -> Either String (RebaseMap, Stm InKernel)-offsetMemoryInStm offsets (Let pat attr e) = do- (offsets', pat') <- offsetMemoryInPattern offsets pat- e' <- offsetMemoryInExp offsets e- return (offsets', Let pat' attr e')+ instantiateIxFun :: ExtIxFun -> Maybe IxFun+ instantiateIxFun = traverse (traverse inst)+ where inst Ext{} = Nothing+ inst (Free x) = return x -offsetMemoryInPattern :: RebaseMap -> Pattern InKernel- -> Either String (RebaseMap, Pattern InKernel)-offsetMemoryInPattern offsets (Pattern ctx vals) = do- offsets' <- foldM inspectCtx offsets ctx- return (offsets', Pattern ctx $ map (inspectVal offsets') vals)- where inspectVal offsets' = fmap $ offsetMemoryInMemBound offsets'- inspectCtx ctx_offsets patElem+offsetMemoryInPattern :: Pattern InKernel -> OffsetM (Pattern InKernel)+offsetMemoryInPattern (Pattern ctx vals) = do+ mapM_ inspectCtx ctx+ Pattern ctx <$> mapM inspectVal vals+ where inspectVal patElem = do+ new_attr <- offsetMemoryInMemBound $ patElemAttr patElem+ return patElem { patElemAttr = new_attr }+ inspectCtx patElem | Mem _ space <- patElemType patElem, space /= Space "local" = throwError $ unwords ["Cannot deal with existential memory block", pretty (patElemName patElem), "when expanding inside kernels."]- | otherwise =- return ctx_offsets+ | otherwise = return () -offsetMemoryInParam :: RebaseMap -> Param (MemBound u) -> Param (MemBound u)-offsetMemoryInParam offsets fparam =- fparam { paramAttr = offsetMemoryInMemBound offsets $ paramAttr fparam }+offsetMemoryInParam :: Param (MemBound u) -> OffsetM (Param (MemBound u))+offsetMemoryInParam fparam = do+ fparam' <- offsetMemoryInMemBound $ paramAttr fparam+ return fparam { paramAttr = fparam' } -offsetMemoryInMemBound :: RebaseMap -> MemBound u -> MemBound u-offsetMemoryInMemBound offsets (MemArray pt shape u (ArrayIn mem ixfun))- | Just new_base <- lookupNewBase mem (IxFun.base ixfun, pt) offsets =- MemArray pt shape u $ ArrayIn mem $ IxFun.rebase new_base ixfun-offsetMemoryInMemBound _ summary =- summary+offsetMemoryInMemBound :: MemBound u -> OffsetM (MemBound u)+offsetMemoryInMemBound summary@(MemArray pt shape u (ArrayIn mem ixfun)) = do+ new_base <- lookupNewBase mem (IxFun.base ixfun, pt)+ return $ fromMaybe summary $ do+ new_base' <- new_base+ return $ MemArray pt shape u $ ArrayIn mem $ IxFun.rebase new_base' ixfun+offsetMemoryInMemBound summary = return summary -offsetMemoryInBodyReturns :: RebaseMap -> BodyReturns -> BodyReturns-offsetMemoryInBodyReturns offsets (MemArray pt shape u (ReturnsInBlock mem ixfun))- | Just ixfun' <- isStaticIxFun ixfun,- Just new_base <- lookupNewBase mem (IxFun.base ixfun', pt) offsets =- MemArray pt shape u $ ReturnsInBlock mem $- IxFun.rebase (fmap (fmap Free) new_base) ixfun-offsetMemoryInBodyReturns _ br = br+offsetMemoryInBodyReturns :: BodyReturns -> OffsetM BodyReturns+offsetMemoryInBodyReturns br@(MemArray pt shape u (ReturnsInBlock mem ixfun))+ | Just ixfun' <- isStaticIxFun ixfun = do+ new_base <- lookupNewBase mem (IxFun.base ixfun', pt)+ return $ fromMaybe br $ do+ new_base' <- new_base+ return $+ MemArray pt shape u $ ReturnsInBlock mem $+ IxFun.rebase (fmap (fmap Free) new_base') ixfun+offsetMemoryInBodyReturns br = return br -offsetMemoryInExp :: RebaseMap -> Exp InKernel -> Either String (Exp InKernel)-offsetMemoryInExp offsets (DoLoop ctx val form body) =- DoLoop (zip ctxparams' ctxinit) (zip valparams' valinit) form <$>- offsetMemoryInBody offsets body- where (ctxparams, ctxinit) = unzip ctx- (valparams, valinit) = unzip val- ctxparams' = map (offsetMemoryInParam offsets) ctxparams- valparams' = map (offsetMemoryInParam offsets) valparams-offsetMemoryInExp offsets (Op (Inner (GroupStream w max_chunk lam accs arrs))) = do- body <- offsetMemoryInBody offsets $ groupStreamLambdaBody lam- let lam' = lam { groupStreamLambdaBody = body- , groupStreamAccParams = map (offsetMemoryInParam offsets) $- groupStreamAccParams lam- , groupStreamArrParams = map (offsetMemoryInParam offsets) $- groupStreamArrParams lam+offsetMemoryInLambda :: Lambda InKernel -> OffsetM (Lambda InKernel)+offsetMemoryInLambda lam = do+ body <- offsetMemoryInBody $ lambdaBody lam+ return $ lam { lambdaBody = body }++offsetMemoryInExp :: Exp InKernel -> OffsetM (Exp InKernel)+offsetMemoryInExp (DoLoop ctx val form body) = do+ let (ctxparams, ctxinit) = unzip ctx+ (valparams, valinit) = unzip val+ ctxparams' <- mapM offsetMemoryInParam ctxparams+ valparams' <- mapM offsetMemoryInParam valparams+ body' <- localScope (scopeOfFParams ctxparams' <> scopeOfFParams valparams' <> scopeOf form) (offsetMemoryInBody body)+ return $ DoLoop (zip ctxparams' ctxinit) (zip valparams' valinit) form body'+offsetMemoryInExp (Op (Inner (GroupStream w max_chunk lam accs arrs))) = do+ lam_accs <- mapM offsetMemoryInParam $ groupStreamAccParams lam+ lam_arrs <- mapM offsetMemoryInParam $ groupStreamArrParams lam+ let lam' = lam { groupStreamAccParams = lam_accs+ , groupStreamArrParams = lam_arrs }- return $ Op $ Inner $ GroupStream w max_chunk lam' accs arrs-offsetMemoryInExp offsets (Op (Inner (GroupReduce w lam input))) = do- body <- offsetMemoryInBody offsets $ lambdaBody lam+ body <- localScope (scopeOf lam') $ offsetMemoryInBody $ groupStreamLambdaBody lam+ let lam'' = lam' { groupStreamLambdaBody = body }+ return $ Op $ Inner $ GroupStream w max_chunk lam'' accs arrs+offsetMemoryInExp (Op (Inner (GroupReduce w lam input))) = do+ body <- localScope (scopeOf lam) $ offsetMemoryInBody $ lambdaBody lam let lam' = lam { lambdaBody = body } return $ Op $ Inner $ GroupReduce w lam' input-offsetMemoryInExp offsets (Op (Inner (GroupGenReduce w dests lam nes vals locks))) = do- body <- offsetMemoryInBody offsets $ lambdaBody lam- let lam' = lam { lambdaBody = body- , lambdaParams = map (offsetMemoryInParam offsets) $ lambdaParams lam- }- return $ Op $ Inner $ GroupGenReduce w dests lam' nes vals locks-offsetMemoryInExp offsets (Op (Inner (Combine cspace ts active body))) =- Op . Inner . Combine cspace ts active <$> offsetMemoryInBody offsets body-offsetMemoryInExp offsets e = mapExpM recurse e+offsetMemoryInExp (Op (Inner (GroupScan w lam input))) = do+ body <- localScope (scopeOf lam) $ offsetMemoryInBody $ lambdaBody lam+ let lam' = lam { lambdaBody = body }+ return $ Op $ Inner $ GroupScan w lam' input+offsetMemoryInExp (Op (Inner (GroupGenReduce w dests lam nes vals locks))) = do+ lam_params <- mapM offsetMemoryInParam $ lambdaParams lam+ let lam' = lam { lambdaParams = lam_params }+ body <- localScope (scopeOf lam') $ offsetMemoryInBody $ lambdaBody lam+ let lam'' = lam' { lambdaBody = body }+ return $ Op $ Inner $ GroupGenReduce w dests lam'' nes vals locks+offsetMemoryInExp (Op (Inner (Combine cspace ts active body))) =+ Op . Inner . Combine cspace ts active <$> offsetMemoryInBody body+offsetMemoryInExp e = mapExpM recurse e where recurse = identityMapper- { mapOnBody = const $ offsetMemoryInBody offsets- , mapOnBranchType = return . offsetMemoryInBodyReturns offsets+ { mapOnBody = \bscope -> localScope bscope . offsetMemoryInBody+ , mapOnBranchType = offsetMemoryInBodyReturns } ---- Slicing allocation sizes out of a kernel.
src/Futhark/Pass/ExplicitAllocations.hs view
@@ -545,6 +545,13 @@ red_op' <- allocInSegRedLambda (spaceGlobalId space) (spaceNumThreads space) red_op return $ Inner $ SegRed space comm red_op' nes ts body' +handleKernel (SegGenRed space ops ts body) = do+ body' <- subInKernel $ localScope (scopeOfKernelSpace space) $ allocInBodyNoDirect body+ ops' <- forM ops $ \op -> do+ lam <- allocInSegRedLambda (spaceGlobalId space) (spaceNumThreads space) $ genReduceOp op+ return op { genReduceOp = lam }+ return $ Inner $ SegGenRed space ops' ts body'+ subInKernel :: AllocM InInKernel OutInKernel a -> AllocM fromlore2 ExplicitMemory a subInKernel = subAllocM handleKernelExp True
src/Futhark/Pass/ExtractKernels.hs view
@@ -171,6 +171,7 @@ import Data.List import Futhark.Representation.SOACS+import qualified Futhark.Representation.SOACS.SOAC as SOAC import Futhark.Representation.SOACS.Simplify (simplifyStms, simpleSOACS) import qualified Futhark.Representation.Kernels as Out import Futhark.Representation.Kernels.Kernel@@ -462,9 +463,9 @@ addStms bnds letBind_ pat $ Op kernel -transformStm path (Let orig_pat (StmAux cs _) (Op (GenReduce w ops bucket_fun imgs))) = do+transformStm _ (Let orig_pat (StmAux cs _) (Op (GenReduce w ops bucket_fun imgs))) = do bfun' <- Kernelise.transformLambda bucket_fun- genReduceKernel path [] orig_pat [] [] cs w ops bfun' imgs+ genReduceKernel orig_pat [] [] cs w ops bfun' imgs transformStm _ bnd = runBinder_ $ FOT.transformStmRecursively bnd@@ -1281,7 +1282,7 @@ -> [Int] -> Certificates -> SubExp- -> [GenReduceOp SOACS]+ -> [SOAC.GenReduceOp SOACS] -> InKernelLambda -> [VName] -> KernelM KernelsStms@@ -1292,129 +1293,48 @@ (nest_stms, _, ispace, inputs, _rets) <- flatKernel nest let orig_pat = Pattern [] $ rearrangeShape perm $ patternValueElements $ loopNestingPattern $ fst nest- path <- asks kernelPath+ -- The input/output arrays _must_ correspond to some kernel input, -- or else the original nested GenReduce would have been ill-typed. -- Find them.- ops' <- forM ops $ \(GenReduceOp num_bins dests nes op) ->- GenReduceOp num_bins+ ops' <- forM ops $ \(SOAC.GenReduceOp num_bins dests nes op) ->+ SOAC.GenReduceOp num_bins <$> mapM (fmap kernelInputArray . findInput inputs) dests <*> pure nes <*> pure op- -- We should also remove those from the kernel nest, as otherwise- -- the generated code may be ill-typed (referencing a consumed- -- array). They will not be used anywhere else (due to uniqueness- -- constraints), so this is safe.- let all_dests = concatMap genReduceDest ops' liftDistribM $ (nest_stms<>) <$> inScopeOf nest_stms- (genReduceKernel path (kernelNestLoops $ removeArraysFromNest all_dests nest)- orig_pat ispace inputs cs genred_w ops' lam arrs)+ (genReduceKernel orig_pat ispace inputs cs genred_w ops' lam arrs) where findInput kernel_inps a = maybe bad return $ find ((==a) . kernelInputName) kernel_inps bad = fail "Ill-typed nested GenReduce encountered." -genReduceKernel :: KernelPath -> [LoopNesting]- -> Pattern -> [(VName, SubExp)] -> [KernelInput]- -> Certificates -> SubExp -> [GenReduceOp SOACS]+genReduceKernel :: Pattern -> [(VName, SubExp)] -> [KernelInput]+ -> Certificates -> SubExp -> [SOAC.GenReduceOp SOACS] -> InKernelLambda -> [VName] -> DistribM KernelsStms-genReduceKernel path nests orig_pat ispace inputs cs genred_w ops lam arrs = do- ops' <- forM ops $ \(GenReduceOp num_bins dests nes op) ->- GenReduceOp num_bins dests nes <$> Kernelise.transformLambda op+genReduceKernel orig_pat ispace inputs cs genred_w ops lam arrs = do+ ops' <- forM ops $ \(SOAC.GenReduceOp num_bins dests nes op) -> do+ let (shape, op') = isVectorMap op+ Out.GenReduceOp num_bins dests nes shape <$> Kernelise.transformLambda op' - let isDest = flip elem $ concatMap genReduceDest ops'+ let isDest = flip elem $ concatMap Out.genReduceDest ops' inputs' = filter (not . isDest . kernelInputArray) inputs - (histos, k_stms) <- blockedGenReduce genred_w ispace inputs' ops' lam arrs-- let histos' = chunks (map (length . genReduceDest) ops') histos- pes = chunks (map (length . genReduceDest) ops') $ patternElements orig_pat-- (fmap (certify cs) k_stms<>) . mconcat <$>- inScopeOf k_stms (mapM combineIntermediateResults (zip3 pes ops histos'))-- where depth = length nests-- mkBodies num_histos pes num_bins nes op histos = runBinder $ do- body_with_reshape <- runBodyBinder $- fmap resultBody $ forM histos $ \histo -> do- histo_dims <- arrayDims <$> lookupType histo- -- Drop the num_histos dimension dimension.- let final_dims = take depth histo_dims ++ drop (depth+1) histo_dims- letSubExp "histo_flattened" $ BasicOp $ Reshape (map DimNew final_dims) histo-- -- Move the num_histos dimension innermost wrt. segments and bins.- histos_tr <- forM histos $ \h -> do- h_t <- lookupType h- let histo_perm = [0..depth-1] ++ [depth+1,depth] ++ [depth+2..arrayRank h_t-1]- letExp (baseString h <> "_tr") $ BasicOp $ Rearrange histo_perm h- histos_tr_t <- mapM lookupType histos_tr-- op_renamed <- renameLambda op- map_params <- forM (lambdaReturnType op) $ \t ->- newParam "bin" $ t `arrayOfRow` num_histos- (map_res, map_stms) <- runBinder $ do- form <- reduceSOAC Commutative op_renamed nes- letTupExp "bin_combined" $ Op $- Screma num_histos form $ map paramName map_params- inner_segred_pat <- fmap (Pattern []) <$> forM pes $ \pe ->- PatElem <$> newVName "inner_segred" <*>- pure (stripArray depth $ patElemType pe)- nests' <-- moreArrays (map paramName map_params) histos_tr_t histos_tr $- nests ++ [MapNesting inner_segred_pat cs num_bins $ zip (lambdaParams lam) arrs]-- let collapse_body = reconstructMapNest nests' (map (rowType . patElemType) pes) $- mkBody map_stms $ map Var map_res-- return (body_with_reshape, collapse_body)-- combineIntermediateResults (pes, GenReduceOp num_bins _ nes op, histos) = do- num_histos <- arraysSize depth <$> mapM lookupType histos-- ((body_with_reshape, collapse_body), aux_stms) <- mkBodies num_histos pes num_bins nes op histos-- segmented_reduce_stms <-- inScopeOf aux_stms $ transformStms path $ stmsToList $ bodyStms collapse_body-- let body_with_segred = mkBody segmented_reduce_stms $- bodyResult collapse_body-- runBinder_ $ do- addStms aux_stms-- -- Avoid the segmented reduction if num_histos is 1.- num_histos_is_one <-- letSubExp "num_histos_is_one" $- BasicOp $ CmpOp (CmpEq int32) num_histos $ intConst Int32 1-- letBindNames (map patElemName pes) $- If num_histos_is_one body_with_reshape body_with_segred $- IfAttr (staticShapes $ map patElemType pes) IfNormal+ k_stms <- blockedGenReduce orig_pat genred_w ispace inputs' ops' lam arrs -reconstructMapNest :: [LoopNesting] -> [Type] -> BodyT SOACS -> BodyT SOACS-reconstructMapNest [] _ body = body-reconstructMapNest (MapNesting pat cs w ps_and_arrs : nests) ts body =- mkBody (oneStm $ Let pat (StmAux cs ()) $ Op $ Screma w (mapSOAC map_lam) arrs) $- map Var $ patternNames pat- where (ps, arrs) = unzip ps_and_arrs- map_lam = Lambda { lambdaReturnType = ts- , lambdaParams = ps- , lambdaBody = reconstructMapNest nests (map rowType ts) body- }+ return $ certify cs <$> k_stms -moreArrays :: MonadFreshNames m =>- [VName] -> [Type] -> [VName] -> [LoopNesting]- -> m [LoopNesting]-moreArrays _ _ _ [] = return []-moreArrays ps more_ts more_arrs (MapNesting pat cs w ps_and_arrs : nests) = do- ps' <- case nests of [] -> return $ zipWith Param ps row_ts- _ -> zipWithM newParam (map baseString ps) row_ts- pat' <- renamePattern pat- let outer = MapNesting pat' cs w $ ps_and_arrs ++ zip ps' more_arrs- (outer:) <$> moreArrays ps row_ts (map paramName ps') nests- where row_ts = map rowType more_ts+isVectorMap :: Lambda -> (Shape, Lambda)+isVectorMap lam+ | [Let (Pattern [] pes) _ (Op (Screma w form arrs))] <-+ stmsToList $ bodyStms $ lambdaBody lam,+ bodyResult (lambdaBody lam) == map (Var . patElemName) pes,+ Just map_lam <- isMapSOAC form,+ arrs == map paramName (lambdaParams lam) =+ let (shape, lam') = isVectorMap map_lam+ in (Shape [w] <> shape, lam')+ | otherwise = (mempty, lam) segmentedScanomapKernel :: KernelNest -> [Int]
src/Futhark/Pass/ExtractKernels/BlockedKernel.hs view
@@ -42,7 +42,7 @@ import qualified Futhark.Pass.ExtractKernels.Kernelise as Kernelise import Futhark.Representation.AST.Attributes.Aliases import qualified Futhark.Analysis.Alias as Alias-import Futhark.Representation.SOACS.SOAC (composeLambda, Scan, Reduce, nilFn, GenReduceOp(..))+import qualified Futhark.Representation.SOACS.SOAC as SOAC import Futhark.Util import Futhark.Util.IntegralExp @@ -345,7 +345,7 @@ -> [(VName, SubExp)] -> [SubExp] -> [VName] -> m (Stms Kernels) blockedReduction pat w comm reduce_lam map_lam ispace nes arrs = runBinder_ $ do- fold_lam <- composeLambda nilFn reduce_lam map_lam+ fold_lam <- SOAC.composeLambda SOAC.nilFn reduce_lam map_lam fold_lam' <- chunkLambda pat nes fold_lam let arr_idents = drop (length nes) $ patternIdents pat@@ -358,152 +358,34 @@ ispace nes (arrs ++ map_out_arrs) blockedGenReduce :: (MonadFreshNames m, HasScope Kernels m) =>- SubExp+ Pattern Kernels+ -> SubExp -> [(VName,SubExp)] -- ^ Segment indexes and sizes. -> [KernelInput] -> [GenReduceOp InKernel] -> Lambda InKernel -> [VName]- -> m ([VName], Stms Kernels)-blockedGenReduce arr_w segments inputs ops lam arrs = runBinder $ do- let (segment_is, segment_sizes) = unzip segments- depth = length segments+ -> m (Stms Kernels)+blockedGenReduce pat arr_w ispace inps ops lam arrs = runBinder_ $ do+ let (_, segment_sizes) = unzip ispace arr_w_64 <- letSubExp "arr_w_64" =<< eConvOp (SExt Int32 Int64) (toExp arr_w) segment_sizes_64 <- mapM (letSubExp "segment_size_64" <=< eConvOp (SExt Int32 Int64) . toExp) segment_sizes total_w <- letSubExp "genreduce_elems" =<< foldBinOp (Mul Int64) arr_w_64 segment_sizes_64- (_, KernelSize num_groups group_size elems_per_thread_64 _ num_threads) <-+ (_, KernelSize num_groups group_size _ _ num_threads) <- blockedKernelSize total_w - kspace <- newKernelSpace (num_groups, group_size, num_threads) $ FlatThreadSpace []- let ltid = spaceLocalId kspace- gtid = spaceGlobalId kspace- nthreads = spaceNumThreads kspace-- -- Determining the degree of cooperation (heuristic):- -- coop_lvl := size of histogram (Cooperation level)- -- num_histos := (threads / coop_lvl) (Number of histograms)- -- threads := min(physical_threads, segment_size)- num_histos <- forM ops $ \(GenReduceOp w _ _ _) ->- letSubExp "num_histos" =<< eDivRoundingUp Int32 (eSubExp nthreads)- (foldBinOp (Mul Int32) w segment_sizes)-- -- Initialize sub-histograms.- sub_histos <- forM (zip ops num_histos) $ \(GenReduceOp w dests nes _, num_histos') -> do- -- If num_histos' is 1, then we just reuse the original- -- destination. The idea is to avoid a copy if we are writing a- -- small number of values into a very large prior histogram. This- -- only works if neither the Reshape nor the If results in a copy.- let num_histos_is_one = BasicOp $ CmpOp (CmpEq int32) num_histos' $ intConst Int32 1-- reuse_dest =- fmap resultBody $ forM dests $ \dest -> do- (segment_dims, hist_dims) <- splitAt depth . arrayDims <$> lookupType dest- letSubExp "sub_histo" $ BasicOp $- Reshape (map DimNew $ segment_dims ++ num_histos' : hist_dims) dest-- make_subhistograms =- -- To incorporate the original values of the genreduce target, we- -- copy those values to the first subhistogram here.- fmap resultBody $ forM (zip nes dests) $ \(ne, dest) -> do- blank <- letExp "sub_histo_blank" $- BasicOp $ Replicate (Shape $ segment_sizes ++ [num_histos', w]) ne- let (zero, one) = (intConst Int32 0, intConst Int32 1)- slice <- fullSlice <$> lookupType blank <*>- pure (map (flip (DimSlice zero) one) segment_sizes ++ [DimFix zero])- letSubExp "sub_histo" $ BasicOp $ Update blank slice $ Var dest-- letTupExp "histo_dests" =<<- eIf (pure num_histos_is_one) reuse_dest make_subhistograms-- let sub_histos' = concat sub_histos- dest_ts <- mapM lookupType sub_histos'-- lock_arrs <- forM (zip ops num_histos) $ \(GenReduceOp w _ _ _, num_histos') ->- letExp "locks_arr" $ BasicOp $- Replicate (Shape $ segment_sizes ++ [num_histos', w]) (intConst Int32 0)-- (kres, kstms) <- runBinder $ localScope (scopeOfKernelSpace kspace) $ do- let toInt64 = eConvOp (SExt Int32 Int64)- i <- newVName "i"- -- The merge parameters are the histogram we are constructing.- merge_params <- zipWithM newParam (map baseString sub_histos')- (map (`toDecl` Unique) dest_ts)- group_size_64 <- letSubExp "group_size_64" =<<- toInt64 (toExp group_size)- let merge = zip merge_params $ map Var sub_histos'- form = ForLoop i Int64 elems_per_thread_64 []-- loop_body <- runBodyBinder $ localScope (scopeOfFParams (map fst merge) <>- scopeOf form) $ do- -- Compute the offset into the input and output. To this a- -- thread can add its local ID to figure out which element it is- -- responsible for. The calculation is done with 64-bit- -- integers to avoid overflow, but the final segment indexes are- -- 32 bit.- offset <- letSubExp "offset" =<<- eBinOp (Add Int64)- (eBinOp (Mul Int64)- (toInt64 $ toExp $ spaceGroupId kspace)- (eBinOp (Mul Int64) (toExp elems_per_thread_64) (toExp group_size_64)))- (eBinOp (Mul Int64) (toExp i) (toExp group_size_64))-- -- Construct segment indices.- j <- letSubExp "j" =<< eBinOp (Add Int64) (toExp offset) (toInt64 $ toExp ltid)- l <- newVName "l"- let bindIndex v = letBindNames_ [v] <=< toExp- zipWithM_ bindIndex (segment_is++[l]) $- map (ConvOpExp (SExt Int64 Int32)) .- unflattenIndex (map (ConvOpExp (SExt Int32 Int64) .- primExpFromSubExp int32) $ segment_sizes ++ [arr_w]) $- primExpFromSubExp int64 j-- -- We execute the bucket function once and update each histogram serially.- -- We apply the bucket function if j=offset+ltid is less than- -- num_elements. This also involves writing to the mapout- -- arrays.- let in_bounds = pure $ BasicOp $ CmpOp (CmpSlt Int64) j total_w-- in_bounds_branch = do- -- Read segment inputs.- mapM_ (addStm <=< readKernelInput) inputs-- -- Read array input.- arr_elems <- forM arrs $ \a -> do- a_t <- lookupType a- let slice = fullSlice a_t [DimFix $ Var l]- letSubExp (baseString a ++ "_elem") $ BasicOp $ Index a slice-- -- Apply bucket function.- resultBody <$> eLambda lam (map eSubExp arr_elems)-- not_in_bounds_branch =- return $ resultBody $ replicate (length ops) (intConst Int32 (-1)) ++- concatMap genReduceNeutral ops-- lam_res <- letTupExp "bucket_fun_res" =<<- eIf in_bounds in_bounds_branch not_in_bounds_branch-- let (buckets, vs) = splitAt (length ops) $ map Var lam_res- perOp :: [a] -> [[a]]- perOp = chunks $ map (length . genReduceDest) ops-- ops_res <- forM (zip6 ops (perOp $ map paramName merge_params) buckets (perOp vs) lock_arrs num_histos) $- \(GenReduceOp dest_w _ _ comb_op, subhistos, bucket, vs', lock_arrs', num_histos') -> do- -- Compute subhistogram index for each thread.- subhisto_ind <- letSubExp "subhisto_ind" =<<- eBinOp (SDiv Int32)- (toExp gtid)- (eDivRoundingUp Int32 (toExp nthreads) (eSubExp num_histos'))- fmap (map Var) $ letTupExp "genreduce_res" $ Op $- GroupGenReduce (segment_sizes ++ [num_histos', dest_w])- subhistos comb_op (map Var segment_is ++ [subhisto_ind, bucket]) vs' lock_arrs'-- return $ resultBody $ concat ops_res+ gtid <- newVName "gtid"+ kspace <- newKernelSpace (num_groups, group_size, num_threads) $+ FlatThreadSpace $ ispace ++ [(gtid, arr_w)] - result <- letTupExp "result" $ DoLoop [] merge form loop_body- return $ map KernelInPlaceReturn result+ body <- runBodyBinder $ localScope (scopeOfKernelSpace kspace) $ do+ mapM_ (addStm <=< readKernelInput) inps+ forM_ (zip (lambdaParams lam) arrs) $ \(p, arr) -> do+ arr_t <- lookupType arr+ letBindNames_ [paramName p] $+ BasicOp $ Index arr $ fullSlice arr_t [DimFix $ Var gtid]+ return $ lambdaBody lam - let kbody = KernelBody () kstms kres- letTupExp "histograms" $ Op $ Kernel (KernelDebugHints "gen_reduce" []) kspace dest_ts kbody+ letBind_ pat $ Op $ SegGenRed kspace ops (lambdaReturnType lam) body blockedMap :: (MonadFreshNames m, HasScope Kernels m) => Pattern Kernels -> SubExp@@ -653,8 +535,8 @@ -- First stage scan kernel. scanKernel1 :: (MonadBinder m, Lore m ~ Kernels) => SubExp -> KernelSize- -> Scan InKernel- -> Reduce InKernel+ -> SOAC.Scan InKernel+ -> SOAC.Reduce InKernel -> Lambda InKernel -> [VName] -> m (Kernel InKernel) scanKernel1 w scan_sizes (scan_lam, scan_nes) (_comm, red_lam, red_nes) foldlam arrs = do@@ -902,13 +784,13 @@ -- need them. blockedScan :: (MonadBinder m, Lore m ~ Kernels) => Pattern Kernels -> SubExp- -> Scan InKernel- -> Reduce InKernel+ -> SOAC.Scan InKernel+ -> SOAC.Reduce InKernel -> Lambda InKernel -> SubExp -> [(VName, SubExp)] -> [KernelInput] -> [VName] -> m [VName] blockedScan pat w (scan_lam, scan_nes) (comm, red_lam, red_nes) map_lam segment_size ispace inps arrs = do- foldlam <- composeLambda scan_lam red_lam map_lam+ foldlam <- SOAC.composeLambda scan_lam red_lam map_lam (_, first_scan_size) <- blockedKernelSize =<< asIntS Int64 w my_index <- newVName "my_index"
src/Futhark/Pass/ExtractKernels/Segmented.hs view
@@ -13,7 +13,7 @@ import Futhark.Representation.Kernels import Futhark.Representation.SOACS.SOAC (nilFn) import Futhark.MonadFreshNames-import Futhark.Tools hiding (true, false)+import Futhark.Tools hiding (false) import Futhark.Pass.ExtractKernels.BlockedKernel addFlagToLambda :: (MonadBinder m, Lore m ~ Kernels) =>
− src/Futhark/Pass/ExtractKernels/Split.hs
@@ -1,41 +0,0 @@--- | Functionality for identifying chunks of interesting parallelism--- inside of a map nesting.-module Futhark.Pass.ExtractKernels.Split- ( splitMap) where--import Control.Monad.RWS.Strict-import Control.Monad.Reader-import Control.Monad.Trans.Maybe-import qualified Data.Map.Strict as M-import qualified Data.Set as S-import Data.Maybe-import Data.List-import qualified Data.Semigroup as Sem--import Futhark.Representation.SOACS-import Futhark.Representation.SOACS.Simplify (simplifyStms, simpleSOACS)-import qualified Futhark.Representation.Kernels as Out-import Futhark.Representation.Kernels.Kernel-import Futhark.MonadFreshNames-import Futhark.Tools-import qualified Futhark.Transform.FirstOrderTransform as FOT-import qualified Futhark.Pass.ExtractKernels.Kernelise as Kernelise-import Futhark.Transform.Rename-import Futhark.Pass-import Futhark.Transform.CopyPropagate-import Futhark.Pass.ExtractKernels.Distribution-import Futhark.Pass.ExtractKernels.ISRWIM-import Futhark.Pass.ExtractKernels.BlockedKernel-import Futhark.Pass.ExtractKernels.Segmented-import Futhark.Pass.ExtractKernels.Interchange-import Futhark.Pass.ExtractKernels.Intragroup-import Futhark.Util-import Futhark.Util.Log--type KernelsStms = Out.Stms Out.Kernels-type InKernelStms = Out.Stms Out.InKernel-type InKernelLambda = Out.Lambda Out.InKernel--splitMap :: (MonadFreshNames m) =>- Scope SOACS -> a -> m [a]-splitMap scope loop = return [loop]
src/Futhark/Passes.hs view
@@ -19,7 +19,6 @@ import Futhark.Optimise.TileLoops import Futhark.Optimise.DoubleBuffer import Futhark.Optimise.Unstream-import Futhark.Optimise.MemoryBlockMerging import Futhark.Pass.ExpandAllocations import Futhark.Pass.ExplicitAllocations import Futhark.Pass.ExtractKernels@@ -63,49 +62,6 @@ then onePass inPlaceLowering else passes [] --- Do we use the coalescing part of memory block merging? Currently disabled by--- default. Enable by setting the environment variable--- MEMORY_BLOCK_MERGING_COALESCING=1.-usesMemoryBlockMergingCoalescing :: Bool-usesMemoryBlockMergingCoalescing =- isEnvVarSet "MEMORY_BLOCK_MERGING_COALESCING" False--memoryBlockMergingCoalescingMaybe :: Pipeline ExplicitMemory ExplicitMemory-memoryBlockMergingCoalescingMaybe =- passes $ if usesMemoryBlockMergingCoalescing- then [ memoryBlockMergingCoalescing- , simplifyExplicitMemory- ]- else []--memoryBlockMergingCoalescingMaybeCPU :: Pipeline ExplicitMemory ExplicitMemory-memoryBlockMergingCoalescingMaybeCPU = memoryBlockMergingCoalescingMaybe--memoryBlockMergingCoalescingMaybeGPU :: Pipeline ExplicitMemory ExplicitMemory-memoryBlockMergingCoalescingMaybeGPU = memoryBlockMergingCoalescingMaybe---- Do we use the reuse part of memory block merging? Currently disabled by--- default. Enable by setting the environment variable--- MEMORY_BLOCK_MERGING_REUSE=1.-usesMemoryBlockMergingReuse :: Bool-usesMemoryBlockMergingReuse =- isEnvVarSet "MEMORY_BLOCK_MERGING_REUSE" False--memoryBlockMergingReuseMaybe :: Pipeline ExplicitMemory ExplicitMemory-memoryBlockMergingReuseMaybe =- passes $ if usesMemoryBlockMergingReuse- then [ memoryBlockMergingReuse- , simplifyExplicitMemory- ]- else []--memoryBlockMergingReuseMaybeCPU :: Pipeline ExplicitMemory ExplicitMemory-memoryBlockMergingReuseMaybeCPU = memoryBlockMergingReuseMaybe--memoryBlockMergingReuseMaybeGPU :: Pipeline ExplicitMemory ExplicitMemory-memoryBlockMergingReuseMaybeGPU = memoryBlockMergingReuseMaybe-- kernelsPipeline :: Pipeline SOACS Kernels kernelsPipeline = standardPipeline >>>@@ -138,9 +94,7 @@ , simplifyExplicitMemory , doubleBuffer , simplifyExplicitMemory- ] >>>- memoryBlockMergingCoalescingMaybeCPU >>>- memoryBlockMergingReuseMaybeCPU+ ] gpuPipeline :: Pipeline SOACS ExplicitMemory gpuPipeline =@@ -153,6 +107,4 @@ , simplifyExplicitMemory , expandAllocations , simplifyExplicitMemory- ] >>>- memoryBlockMergingCoalescingMaybeGPU >>>- memoryBlockMergingReuseMaybeGPU+ ]
src/Futhark/Representation/AST/Attributes/Types.hs view
@@ -53,7 +53,6 @@ , generaliseExtTypes , existentialiseExtTypes , shapeMapping- , shapeMapping' , shapeExtMapping -- * Abbreviations@@ -73,7 +72,7 @@ import Control.Monad.State import Data.Maybe import Data.Monoid ((<>))-import Data.List (elemIndex)+import Data.List (elemIndex, foldl') import qualified Data.Set as S import qualified Data.Map.Strict as M @@ -431,32 +430,34 @@ -- be of equal length and their corresponding elements have the same -- types modulo exact dimensions (but matching array rank is -- important). The result is a mapping from named dimensions of @ts1@--- to the corresponding dimension in @ts2@.+-- to a set of the corresponding dimensions in @ts2@ (because they may+-- not fit exactly). -- -- This function is useful when @ts1@ are the value parameters of some -- function and @ts2@ are the value arguments, and we need to figure -- out which shape context to pass.-shapeMapping :: [TypeBase Shape u0] -> [TypeBase Shape u1] -> M.Map VName SubExp+shapeMapping :: [TypeBase Shape u0] -> [TypeBase Shape u1] -> M.Map VName (S.Set SubExp) shapeMapping ts = shapeMapping' ts . map arrayDims -- | Like @shapeMapping@, but works with explicit dimensions.-shapeMapping' :: [TypeBase Shape u] -> [[a]] -> M.Map VName a-shapeMapping' = dimMapping arrayDims id match+shapeMapping' :: Ord a => [TypeBase Shape u] -> [[a]] -> M.Map VName (S.Set a)+shapeMapping' = dimMapping arrayDims id match (M.unionWith (<>)) where match Constant{} _ = M.empty- match (Var v) dim = M.singleton v dim+ match (Var v) dim = M.singleton v $ S.singleton dim -- | Like 'shapeMapping', but produces a mapping for the dimensions context. shapeExtMapping :: [TypeBase ExtShape u] -> [TypeBase Shape u1] -> M.Map Int SubExp-shapeExtMapping = dimMapping arrayExtDims arrayDims match+shapeExtMapping = dimMapping arrayExtDims arrayDims match mappend where match Free{} _ = mempty match (Ext i) dim = M.singleton i dim dimMapping :: Monoid res => (t1 -> [dim1]) -> (t2 -> [dim2]) -> (dim1 -> dim2 -> res)+ -> (res -> res -> res) -> [t1] -> [t2] -> res-dimMapping getDims1 getDims2 f ts1 ts2 =- mconcat $ concat $ zipWith (zipWith f) (map getDims1 ts1) (map getDims2 ts2)+dimMapping getDims1 getDims2 f comb ts1 ts2 =+ foldl' comb mempty $ concat $ zipWith (zipWith f) (map getDims1 ts1) (map getDims2 ts2) int8 :: PrimType int8 = IntType Int8
src/Futhark/Representation/Aliases.hs view
@@ -56,7 +56,6 @@ import Futhark.Binder import Futhark.Transform.Substitute import Futhark.Analysis.Rephrase-import Futhark.Representation.AST.Attributes.Ranges() import qualified Futhark.Util.Pretty as PP -- | The lore for the basic representation.
src/Futhark/Representation/ExplicitMemory.hs view
@@ -554,7 +554,7 @@ TC.bad $ TC.TypeError $ "Array " ++ pretty v ++ " returned by function, but has nontrivial index function " ++- pretty ixfun ++ " " ++ show ixfun+ pretty ixfun matchBranchReturnType :: ExplicitMemorish lore => [BodyReturns]@@ -644,7 +644,7 @@ let x_ixfun' = substConstsInExtIndFun x_ixfun y_ixfun' = existentialiseIxFun0 y_ixfun unless (x_ixfun' == y_ixfun') $- throwError $ unwords ["Index function unification fails1!",+ throwError $ unwords ["Index function unification failed (ReturnsInBlock)", "\nixfun of body result: ", pretty y_ixfun', "\nixfun of return type: ", pretty x_ixfun', "\nand context elements: ", pretty ctx_res]@@ -654,7 +654,7 @@ let x_ixfun' = substConstsInExtIndFun x_ixfun y_ixfun' = existentialiseIxFun0 y_ixfun unless (x_ixfun' == y_ixfun') $- throwError $ unwords ["Index function unification fails2!",+ throwError $ unwords ["Index function unification failed (ReturnsNewBlock)", "\nixfun of body result: ", pretty y_ixfun', "\nixfun of return type: ", pretty x_ixfun', "\nand context elements: ", pretty ctx_res]@@ -1017,6 +1017,8 @@ extendedScope (varReturns arr) (castScope $ scopeOf $ kernelBodyStms body) correct _ ret = return ret+ opReturns (Inner (SegGenRed _ ops _ _)) =+ concat <$> mapM (mapM varReturns . genReduceDest) ops opReturns k = extReturns <$> opType k
src/Futhark/Representation/ExplicitMemory/IndexFunction.hs view
@@ -1,447 +1,789 @@--- | An index function represents a mapping from an array index space--- to a flat byte offset.-module Futhark.Representation.ExplicitMemory.IndexFunction- (--- IxFun(..)- IxFun- , index- , iota- , offsetIndex- , strideIndex- , permute- , rotate- , reshape- , slice- , base- , rebase- , repeat- , shape- , rank- , linearWithOffset- , rearrangeWithOffset- , isLinear- , isDirect- , substituteInIxFun- , getInfoMaxUnification- , subsInIndexIxFun- , ixFunsCompatibleRaw- , ixFunHasIndex- , offsetIndexDWIM- )- where--import Control.Arrow (first)-import Data.Maybe-import Data.Monoid ((<>))-import Data.List hiding (repeat)-import Control.Monad.Identity-import Control.Monad.Writer--import Prelude hiding (mod, repeat)--import qualified Data.List as L-import qualified Data.Map.Strict as M--import Futhark.Transform.Substitute-import Futhark.Transform.Rename--import Futhark.Representation.AST.Syntax- (ShapeChange, DimChange(..), DimIndex(..), Slice, sliceDims, unitSlice, VName(..))-import Futhark.Representation.AST.Attributes.Names-import Futhark.Representation.AST.Attributes.Reshape-import Futhark.Representation.AST.Attributes.Rearrange-import Futhark.Representation.AST.Pretty ()-import Futhark.Util.IntegralExp-import Futhark.Util.Pretty-import Futhark.Util-import Futhark.Analysis.PrimExp.Convert--type Shape num = [num]-type Indices num = [num]-type Permutation = [Int]--data IxFun num = Direct (Shape num)- | Permute (IxFun num) Permutation- | Rotate (IxFun num) (Indices num)- | Index (IxFun num) (Slice num)- | Reshape (IxFun num) (ShapeChange num)- | Repeat (IxFun num) [Shape num] (Shape num)- deriving (Eq,Show)--instance Pretty num => Pretty (IxFun num) where- ppr (Direct dims) =- text "Direct" <> parens (commasep $ map ppr dims)- ppr (Permute fun perm) = ppr fun <> ppr perm- ppr (Rotate fun offsets) = ppr fun <> brackets (commasep $ map ((text "+" <>) . ppr) offsets)- ppr (Index fun is) = ppr fun <> brackets (commasep $ map ppr is)- ppr (Reshape fun oldshape) =- ppr fun <> text "->reshape" <>- parens (commasep (map ppr oldshape))- ppr (Repeat fun outer_shapes inner_shape) =- ppr fun <> text "->repeat" <> parens (commasep (map ppr $ outer_shapes++ [inner_shape]))--instance Substitute num => Substitute (IxFun num) where- substituteNames substs = fmap $ substituteNames substs--instance FreeIn num => FreeIn (IxFun num) where- freeIn = foldMap freeIn--instance Functor IxFun where- fmap f = runIdentity . traverse (return . f)--instance Foldable IxFun where- foldMap f = execWriter . traverse (tell . f)--instance Traversable IxFun where- traverse f (Direct dims) =- Direct <$> traverse f dims- traverse f (Permute ixfun perm) =- Permute <$> traverse f ixfun <*> pure perm- traverse f (Rotate ixfun offsets) =- Rotate <$> traverse f ixfun <*> traverse f offsets- traverse f (Index ixfun is) =- Index <$> traverse f ixfun <*> traverse (traverse f) is- traverse f (Reshape ixfun dims) =- Reshape <$> traverse f ixfun <*> traverse (traverse f) dims- traverse f (Repeat ixfun outer_shapes inner_shape) =- Repeat <$> traverse f ixfun <*>- traverse (traverse f) outer_shapes <*>- traverse f inner_shape--instance Substitute num => Rename (IxFun num) where- rename = substituteRename--index :: (Pretty num, IntegralExp num) =>- IxFun num -> Indices num -> num -> num--index (Direct dims) is element_size =- sum (zipWith (*) is slicesizes) * element_size- where slicesizes = drop 1 $ sliceSizes dims--index (Permute fun perm) is_new element_size =- index fun is_old element_size- where is_old = rearrangeShape (rearrangeInverse perm) is_new--index (Rotate fun offsets) is element_size =- index fun (zipWith mod (zipWith (+) is offsets) dims) element_size- where dims = shape fun--index (Index fun js) is element_size =- index fun (adjust js is) element_size- where adjust (DimFix j:js') is' = j : adjust js' is'- adjust (DimSlice j _ s:js') (i:is') = j + i * s : adjust js' is'- adjust _ _ = []--index (Reshape fun newshape) is element_size =- let new_indices = reshapeIndex (shape fun) (newDims newshape) is- in index fun new_indices element_size--index (Repeat fun outer_shapes _) is element_size =- -- Discard those indices that are just repeats. It is intentional- -- that we cut off those indices that correspond to the innermost- -- repeated dimensions.- index fun is' element_size- where flags dims = replicate (length dims) True ++ [False]- is' = map snd $ filter (not . fst) $ zip (concatMap flags outer_shapes) is--iota :: Shape num -> IxFun num-iota = Direct--offsetIndex :: (Eq num, IntegralExp num) =>- IxFun num -> num -> IxFun num-offsetIndex ixfun i | i == 0 = ixfun-offsetIndex ixfun i =- case shape ixfun of- d:ds -> slice ixfun (DimSlice i (d-i) 1 : map (unitSlice 0) ds)- [] -> error "offsetIndex: underlying index function has rank zero"--strideIndex :: (Eq num, IntegralExp num) =>- IxFun num -> num -> IxFun num-strideIndex ixfun s =- case shape ixfun of- d:ds -> slice ixfun (DimSlice (fromInt32 0) d s : map (unitSlice (fromInt32 0)) ds)- [] -> error "offsetIndex: underlying index function has rank zero"--permute :: IntegralExp num =>- IxFun num -> Permutation -> IxFun num-permute (Permute ixfun oldperm) perm- | rearrangeInverse oldperm == perm = ixfun- | otherwise = permute ixfun (rearrangeCompose perm oldperm)-permute ixfun perm- | perm == sort perm = ixfun- | otherwise = Permute ixfun perm--rotate :: IntegralExp num =>- IxFun num -> Indices num -> IxFun num-rotate (Rotate ixfun old_offsets) offsets =- Rotate ixfun $ zipWith (+) old_offsets offsets-rotate ixfun offsets = Rotate ixfun offsets--repeat :: IxFun num -> [Shape num] -> Shape num -> IxFun num-repeat = Repeat--reshape :: (Eq num, IntegralExp num) =>- IxFun num -> ShapeChange num -> IxFun num--reshape Direct{} newshape =- Direct $ map newDim newshape--reshape (Reshape ixfun _) newshape =- reshape ixfun newshape--reshape (Permute ixfun perm) newshape- | Just (head_coercions, reshapes, tail_coercions) <-- splitCoercions newshape,- num_coercions <- length (head_coercions ++ tail_coercions),- (head_perms, mid_perms, end_perms) <-- splitAt3 (length head_coercions) (length perm - num_coercions) perm,- sequential mid_perms,- first_reshaped <- foldl min (rank ixfun) mid_perms,- extra_dims <- length newshape - length (shape ixfun),- perm' <- map (shiftDim first_reshaped extra_dims) head_perms ++- take (length reshapes) [first_reshaped..] ++- map (shiftDim first_reshaped extra_dims) end_perms,- newshape' <- rearrangeShape (rearrangeInverse perm') newshape =- Permute (reshape ixfun newshape') perm'- where splitCoercions newshape' = do- let (head_coercions, newshape'') = span isCoercion newshape'- let (reshapes, tail_coercions) = break isCoercion newshape''- guard (all isCoercion tail_coercions)- return (head_coercions, reshapes, tail_coercions)-- isCoercion DimCoercion{} = True- isCoercion _ = False-- shiftDim last_reshaped extra_dims x- | x > last_reshaped = x + extra_dims- | otherwise = x-- sequential [] = True- sequential (x:xs) = and $ zipWith (==) xs [x+1, x+2..]--reshape (Index ixfun slicing) newshape- | [newdim] <- newDims newshape,- Just slicing' <- findSlice slicing (Just newdim) =- Index ixfun slicing'- | (is, rem_slicing) <- splitSlice slicing,- (fixed_ds, sliced_ds) <- splitAt (length is) $ shape ixfun,- and $ zipWith isSliceOf rem_slicing sliced_ds =- -- Move the reshape beneath the slicing.- let newshape' = map DimCoercion fixed_ds ++ newshape- in Index (reshape ixfun newshape') $- map DimFix is ++ map (unitSlice (fromInt32 0)) (newDims newshape)- where isSliceOf (DimSlice _ d1 1) d2 = d1 == d2- isSliceOf _ _ = False-- findSlice (DimFix i:is) d = (DimFix i:) <$> findSlice is d- findSlice (DimSlice j _ stride:is) d = do- d' <- d- (DimSlice j d' stride:) <$> findSlice is Nothing- findSlice [] Just{} = Nothing- findSlice [] Nothing = Just []--reshape ixfun newshape- | shape ixfun == map newDim newshape =- ixfun- | rank ixfun == length newshape,- Just _ <- shapeCoercion newshape =- ixfun- | otherwise =- Reshape ixfun newshape--splitSlice :: Slice num -> ([num], Slice num)-splitSlice [] = ([], [])-splitSlice (DimFix i:is) = first (i:) $ splitSlice is-splitSlice is = ([], is)--slice :: (Eq num, IntegralExp num) =>- IxFun num -> Slice num -> IxFun num-slice ixfun is- -- Avoid identity slicing.- | is == map (unitSlice 0) (shape ixfun) = ixfun-slice (Index ixfun mis) is =- Index ixfun $ reslice mis is- where reslice mis' [] = mis'- reslice (DimFix j:mis') is' =- DimFix j : reslice mis' is'- reslice (DimSlice orig_k _ orig_s:mis') (DimSlice new_k n new_s:is') =- DimSlice (orig_k + new_k * orig_s) n (orig_s*new_s) : reslice mis' is'- reslice (DimSlice orig_k _ orig_s:mis') (DimFix i:is') =- DimFix (orig_k+i*orig_s) : reslice mis' is'- reslice _ _ = error "IndexFunction slice: invalid arguments"-slice ixfun [] = ixfun-slice ixfun is = Index ixfun is--rank :: IntegralExp num =>- IxFun num -> Int-rank = length . shape--shape :: IntegralExp num =>- IxFun num -> Shape num-shape (Direct dims) =- dims-shape (Permute ixfun perm) =- rearrangeShape perm $ shape ixfun-shape (Rotate ixfun _) =- shape ixfun-shape (Index _ how) =- sliceDims how-shape (Reshape _ dims) =- map newDim dims-shape (Repeat ixfun outer_shapes inner_shape) =- concat (zipWith repeated outer_shapes (shape ixfun)) ++ inner_shape- where repeated outer_ds d = outer_ds ++ [d]--base :: IxFun num -> Shape num-base (Direct dims) =- dims-base (Permute ixfun _) =- base ixfun-base (Rotate ixfun _) =- base ixfun-base (Index ixfun _) =- base ixfun-base (Reshape ixfun _) =- base ixfun-base (Repeat ixfun _ _) =- base ixfun--rebase :: (Eq num, IntegralExp num) =>- IxFun num- -> IxFun num- -> IxFun num-rebase new_base (Direct old_shape)- | old_shape == shape new_base = new_base- | otherwise = reshape new_base $ map DimCoercion old_shape-rebase new_base (Permute ixfun perm) =- permute (rebase new_base ixfun) perm-rebase new_base (Rotate ixfun offsets) =- rotate (rebase new_base ixfun) offsets-rebase new_base (Index ixfun is) =- slice (rebase new_base ixfun) is-rebase new_base (Reshape ixfun new_shape) =- reshape (rebase new_base ixfun) new_shape-rebase new_base (Repeat ixfun outer_shapes inner_shape) =- Repeat (rebase new_base ixfun) outer_shapes inner_shape---- This function does not cover all possible cases. It's a "best--- effort" kind of thing.-linearWithOffset :: (Eq num, IntegralExp num) =>- IxFun num -> num -> Maybe num-linearWithOffset (Direct _) _ =- Just 0-linearWithOffset (Reshape ixfun _) element_size =- linearWithOffset ixfun element_size-linearWithOffset (Index ixfun is) element_size = do- is' <- fixingOuter is inner_shape- inner_offset <- linearWithOffset ixfun element_size- let slices = take m $ drop 1 $ sliceSizes $ shape ixfun- return $ inner_offset + sum (zipWith (*) slices is') * element_size- where m = length is- inner_shape = shape ixfun- fixingOuter (DimFix i:is') (_:ds) = (i:) <$> fixingOuter is' ds- fixingOuter (DimSlice off _ 1:is') (_:ds)- | is' == map (unitSlice 0) ds = Just [off]- fixingOuter is' ds- | is' == map (unitSlice 0) ds = Just []- fixingOuter _ _ = Nothing-linearWithOffset _ _ = Nothing--rearrangeWithOffset :: (Eq num, IntegralExp num) =>- IxFun num -> num -> Maybe (num, [(Int,num)])-rearrangeWithOffset (Reshape ixfun _) element_size =- rearrangeWithOffset ixfun element_size-rearrangeWithOffset (Permute ixfun perm) element_size = do- offset <- linearWithOffset ixfun element_size- return (offset, zip perm $ rearrangeShape perm $ shape ixfun)-rearrangeWithOffset _ _ =- Nothing--isLinear :: (Eq num, IntegralExp num) => IxFun num -> Bool-isLinear =- (==Just 0) . flip linearWithOffset 1--isDirect :: IxFun num -> Bool-isDirect Direct{} = True-isDirect _ = False---- | Substituting a name with a PrimExp in an index function.-substituteInIxFun :: (Ord a) => M.Map a (PrimExp a) -> IxFun (PrimExp a)- -> IxFun (PrimExp a)-substituteInIxFun tab (Direct pes) =- Direct $ map (substituteInPrimExp tab) pes-substituteInIxFun tab (Permute ixfun p) =- Permute (substituteInIxFun tab ixfun) p-substituteInIxFun tab (Rotate ixfun pes) =- Rotate (substituteInIxFun tab ixfun) $ map (substituteInPrimExp tab) pes-substituteInIxFun tab (Index ixfun sl) =- Index (substituteInIxFun tab ixfun) $ map (fmap $ substituteInPrimExp tab) sl-substituteInIxFun tab (Reshape ixfun newshape) =- Reshape (substituteInIxFun tab ixfun) $ map (fmap $ substituteInPrimExp tab) newshape-substituteInIxFun tab (Repeat ixfun outer_shapes inner_shape) =- Repeat (substituteInIxFun tab ixfun) outer_shapes inner_shape----------------------------------------------------------------- Niels' functions for memory management: ------- these are prime candidates to be removed/re-written -----------------------------------------------------------------type IxFn = IxFun (PrimExp VName)--getInfoMaxUnification :: IxFn -> Maybe (IxFn, Slice (PrimExp VName), VName)-getInfoMaxUnification (Index ixfun_start slc) =- case L.span isDimFix slc of- (indices_start, [DimSlice _start_offset- (LeafExp final_dim@VName{} (IntType Int32))- _stride]) ->- Just (ixfun_start, indices_start, final_dim)- _ -> Nothing- where isDimFix DimFix{} = True- isDimFix _ = False-getInfoMaxUnification _ = Nothing---- Are two index functions *identical*? (Silly approach, but the Eq--- instance is used for something else.)-ixFunsCompatibleRaw :: Eq num => IxFun num -> IxFun num -> Bool-ixFunsCompatibleRaw ixfun0 ixfun1 = ixfun0 `primEq` ixfun1- where primEq a b = case (a, b) of- (Direct sa, Direct sb) ->- sa == sb- (Permute a1 pa, Permute b1 pb) ->- a1 `primEq` b1 && pa == pb- (Rotate a1 ia, Rotate b1 ib) ->- a1 `primEq` b1 && ia == ib- (Index a1 sa, Index b1 sb) ->- a1 `primEq` b1 && sa == sb- (Reshape a1 sa, Reshape b1 sb) ->- a1 `primEq` b1 && sa == sb- (Repeat a1 ssa sa, Repeat b1 ssb sb) ->- a1 `primEq` b1 && ssa == ssb && sa == sb- _ -> False--ixFunHasIndex :: IxFun num -> Bool-ixFunHasIndex ixfun = case ixfun of- Direct _ -> False- Permute ixfun' _ -> ixFunHasIndex ixfun'- Rotate ixfun' _ -> ixFunHasIndex ixfun'- Index{} -> True- Reshape ixfun' _ -> ixFunHasIndex ixfun'- Repeat ixfun' _ _ -> ixFunHasIndex ixfun'--subsInIndexIxFun :: IxFn -> VName -> VName -> IxFn-subsInIndexIxFun (Index ixfun_start slc) final_dim final_dim_max_v =- let tab = M.singleton final_dim (LeafExp final_dim_max_v (IntType Int32))- ixfun_start' = substituteInIxFun tab ixfun_start- in Index ixfun_start' slc-subsInIndexIxFun _ _ _ = error "In IxFun.subsInIndexIxFun: should-not-happen case reached!"--offsetIndexDWIM :: Int -> IxFn -> PrimExp VName -> IxFn-offsetIndexDWIM n_ignore_initial ixfun offset =- fromMaybe (offsetIndex ixfun offset) $ case ixfun of- Index ixfun1 dimindices ->- let (dim_first, dim_rest) = L.splitAt n_ignore_initial dimindices- in case dim_rest of- (DimFix i : dim_rest') ->- Just $ Index ixfun1 (dim_first ++ DimFix (i + offset) : dim_rest')- _ -> Nothing- _ -> Nothing+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}+-- | This module contains a representation for the index function based on+-- linear-memory accessor descriptors; see Zhu, Hoeflinger and David work.+module Futhark.Representation.ExplicitMemory.IndexFunction+ ( IxFun(..)+ , index+ , iota+ , offsetIndex+ , strideIndex+ , permute+ , rotate+ , reshape+ , slice+ , rebase+ , repeat+ , isContiguous+ , shape+ , rank+ , linearWithOffset+ , rearrangeWithOffset+ , isDirect+ , isLinear+ , substituteInIxFun+ )+ where++import Prelude hiding (mod, repeat)+import Data.List hiding (repeat)+import qualified Data.List.NonEmpty as NE+import Data.List.NonEmpty (NonEmpty(..))+import Data.Function (on)+import Data.Maybe (isJust)+import Control.Monad.Identity+import Control.Monad.Writer+import qualified Data.Map.Strict as M++import Futhark.Transform.Substitute+import Futhark.Transform.Rename+import Futhark.Representation.AST.Syntax+ (ShapeChange, DimChange(..), DimIndex(..), Slice, unitSlice, dimFix)+import Futhark.Representation.AST.Attributes+import Futhark.Util.IntegralExp+import Futhark.Util.Pretty+import Futhark.Analysis.PrimExp.Convert+++-- | LMAD's representation consists of a permutation, a general offset, and, for+-- each dimension a stride, rotate factor, number of elements, permutation, and+-- monotonicity and unit-stride info for each dimension. Note that the+-- permutation is not strictly necessary in that the permutation can be+-- performed directly on LMAD dimensions, but then it is difficult to extract+-- the permutation back from an LMAD.+--+-- LMAD algebra is closed under composition w.r.t. operators such as permute,+-- repeat, index and slice. However, other operations, such as reshape, cannot+-- always be represented inside the LMAD algebra.+--+-- It follows that the general representation of an index function is a list of+-- LMADS, in which each following LMAD in the list implicitly corresponds to an+-- irregular reshaping operation.+--+-- However, we expect that the common case is when the index function is one+-- LMAD -- we call this the 'nice' representation.+--+-- Finally, the list of LMADs is tupled with the shape of the original array,+-- and with contiguous info, i.e., if we instantiate all the points of the+-- current index function, do we get a contiguous memory interval?+--+-- By definition, the LMAD denotes the set of points (simplified):+--+-- \{ o + \Sigma_{j=0}^{k} ((i_j+r_j) `mod` n_j)*s_j,+-- \forall i_j such that 0<=i_j<n_j, j=1..k \}+type Shape num = [num]+type Indices num = [num]+type Permutation = [Int]++data Monotonicity = Inc | Dec | Unknown+ -- ^ monotonously increasing, decreasing or unknown+ deriving (Show, Eq)++data LMADDim num = LMADDim { ldStride :: num+ , ldRotate :: num+ , ldShape :: num+ , ldPerm :: Int+ , ldMon :: Monotonicity+ }+ deriving (Show, Eq)++data LMAD num = LMAD { lmadOffset :: num+ , lmadDims :: [LMADDim num]+ }+ deriving (Show, Eq)++data IxFun num = IxFun { ixfunLMADs :: NonEmpty (LMAD num)+ , base :: Shape num+ , ixfunContig :: Bool+ -- ^ ignoring permutations, is the index function contiguous?+ }+ deriving (Show, Eq)+++instance Pretty Monotonicity where+ ppr = text . show++instance Pretty num => Pretty (LMAD num) where+ ppr (LMAD offset dims) =+ braces $ semisep [ text "offset: " <> oneLine (ppr offset)+ , text "strides: " <> p ldStride+ , text "rotates: " <> p ldRotate+ , text "shape: " <> p ldShape+ , text "permutation: " <> p ldPerm+ , text "monotonicity: " <> p ldMon+ ]+ where p f = oneLine $ brackets $ commasep $ map (ppr . f) dims++instance Pretty num => Pretty (IxFun num) where+ ppr (IxFun lmads oshp cg) =+ braces $ semisep [ text "base: " <> brackets (commasep $ map ppr oshp)+ , text "contiguous: " <> text (show cg)+ , text "LMADs: " <> brackets (commasep $ NE.toList $ NE.map ppr lmads)+ ]+++instance Substitute num => Substitute (LMAD num) where+ substituteNames substs = fmap $ substituteNames substs++instance Substitute num => Substitute (IxFun num) where+ substituteNames substs = fmap $ substituteNames substs++instance Substitute num => Rename (LMAD num) where+ rename = substituteRename++instance Substitute num => Rename (IxFun num) where+ rename = substituteRename+++instance FreeIn num => FreeIn (LMAD num) where+ freeIn = foldMap freeIn++instance FreeIn num => FreeIn (IxFun num) where+ freeIn = foldMap freeIn+++instance Functor LMAD where+ fmap f = runIdentity . traverse (return . f)++instance Functor IxFun where+ fmap f = runIdentity . traverse (return . f)+++instance Foldable LMAD where+ foldMap f = execWriter . traverse (tell . f)++instance Foldable IxFun where+ foldMap f = execWriter . traverse (tell . f)+++instance Traversable LMAD where+ traverse f (LMAD offset dims) =+ LMAD <$> f offset <*> traverse f' dims+ where f' (LMADDim s r n p m) =+ LMADDim <$> f s <*> f r <*> f n <*> pure p <*> pure m++instance Traversable IxFun where+ traverse f (IxFun lmads oshp cg) =+ IxFun <$> traverse (traverse f) lmads <*> traverse f oshp <*> pure cg++(++@) :: [a] -> NonEmpty a -> NonEmpty a+es ++@ (ne :| nes) = case es of+ e : es' -> e :| es' ++ [ne] ++ nes+ [] -> ne :| nes++(@++@) :: NonEmpty a -> NonEmpty a -> NonEmpty a+(x :| xs) @++@ (y :| ys) = x :| xs ++ [y] ++ ys++invertMonotonicity :: Monotonicity -> Monotonicity+invertMonotonicity Inc = Dec+invertMonotonicity Dec = Inc+invertMonotonicity Unknown = Unknown++lmadPermutation :: LMAD num -> Permutation+lmadPermutation = map ldPerm . lmadDims++setLMADPermutation :: Permutation -> LMAD num -> LMAD num+setLMADPermutation perm lmad =+ lmad { lmadDims = zipWith (\dim p -> dim { ldPerm = p }) (lmadDims lmad) perm }++setLMADShape :: Shape num -> LMAD num -> LMAD num+setLMADShape shp lmad = lmad { lmadDims = zipWith (\dim s -> dim { ldShape = s }) (lmadDims lmad) shp }++-- | Substitute a name with a PrimExp in an LMAD.+substituteInLMAD :: Ord a => M.Map a (PrimExp a) -> LMAD (PrimExp a)+ -> LMAD (PrimExp a)+substituteInLMAD tab (LMAD offset dims) =+ let offset' = substituteInPrimExp tab offset+ dims' = map (\(LMADDim s r n p m) ->+ LMADDim+ (substituteInPrimExp tab s)+ (substituteInPrimExp tab r)+ (substituteInPrimExp tab n)+ p m)+ dims+ in LMAD offset' dims'++-- | Substitute a name with a PrimExp in an index function.+substituteInIxFun :: (Ord a) => M.Map a (PrimExp a) -> IxFun (PrimExp a)+ -> IxFun (PrimExp a)+substituteInIxFun tab (IxFun lmads oshp cg) =+ IxFun (NE.map (substituteInLMAD tab) lmads)+ (map (substituteInPrimExp tab) oshp)+ cg++-- | Is this is a row-major array?+isDirect :: (Eq num, IntegralExp num) => IxFun num -> Bool+isDirect ixfun@(IxFun (LMAD offset dims :| []) oshp True) =+ let strides_expected = reverse $ scanl (*) 1 (reverse (tail oshp))+ in hasContiguousPerm ixfun &&+ length oshp == length dims &&+ offset == 0 &&+ all (\(LMADDim s r n p _, m, d, se) ->+ s == se && r == 0 && n == d && p == m)+ (zip4 dims [0..length dims - 1] oshp strides_expected)+isDirect _ = False++-- | Does the index function have contiguous memory support?+isContiguous :: (Eq num, IntegralExp num) => IxFun num -> Bool+isContiguous ixfun = ixfunContig ixfun && hasContiguousPerm ixfun++-- | Does the index function have an ascending permutation?+hasContiguousPerm :: IxFun num -> Bool+hasContiguousPerm (IxFun (lmad :| []) _ _) =+ let perm = lmadPermutation lmad+ in perm == sort perm+hasContiguousPerm _ = False++-- | Shape of an index function.+shape :: (Eq num, IntegralExp num) => IxFun num -> Shape num+shape (IxFun (lmad :| _) _ _) = lmadShape lmad++-- | Shape of an LMAD.+lmadShape :: (Eq num, IntegralExp num) => LMAD num -> Shape num+lmadShape lmad = permuteInv (lmadPermutation lmad) $ lmadShapeBase lmad++-- | Shape of an LMAD, ignoring permutations.+lmadShapeBase :: (Eq num, IntegralExp num) => LMAD num -> Shape num+lmadShapeBase = map ldShape . lmadDims++-- | Compute the flat memory index for a complete set `inds` of array indices+-- and a certain element size `elem_size`.+index :: (IntegralExp num, Eq num) =>+ IxFun num -> Indices num -> num -> num+index = indexFromLMADs . ixfunLMADs+ where indexFromLMADs :: (IntegralExp num, Eq num) =>+ NonEmpty (LMAD num) -> Indices num -> num -> num+ indexFromLMADs (lmad :| []) inds elm_size = indexLMAD lmad inds elm_size+ indexFromLMADs (lmad1 :| lmad2 : lmads) inds elm_size =+ let i_flat = indexLMAD lmad1 inds 1+ new_inds = unflattenIndex (permuteFwd (lmadPermutation lmad2) $ lmadShapeBase lmad2) i_flat+ in indexFromLMADs (lmad2 :| lmads) new_inds elm_size++ -- | Compute the flat index of an LMAD.+ indexLMAD :: (IntegralExp num, Eq num) =>+ LMAD num -> Indices num -> num -> num+ indexLMAD lmad@(LMAD off dims) inds elm_size =+ let prod = sum $ zipWith flatOneDim+ (map (\(LMADDim s r n _ _) -> (s, r, n)) dims)+ (permuteInv (lmadPermutation lmad) inds)+ ind = off + prod+ in if elm_size == 1 then ind else ind * elm_size++-- | iota.+iota :: IntegralExp num => Shape num -> IxFun num+iota ns =+ let rs = replicate (length ns) 0+ in IxFun (makeRotIota Inc 0 (zip rs ns) :| []) ns True++-- | Permute dimensions.+permute :: IntegralExp num =>+ IxFun num -> Permutation -> IxFun num+permute (IxFun (lmad :| lmads) oshp cg) perm_new =+ let perm_cur = lmadPermutation lmad+ perm = map (perm_cur !!) perm_new+ in IxFun (setLMADPermutation perm lmad :| lmads) oshp cg++-- | Repeat dimensions.+repeat :: (Eq num, IntegralExp num) =>+ IxFun num -> [Shape num] -> Shape num -> IxFun num+repeat (IxFun (lmad@(LMAD off dims) :| lmads) oshp _) shps shp =+ let perm = lmadPermutation lmad+ -- inverse permute the shapes and update the permutation+ lens = map (\s -> 1 + length s) shps+ (shps', lens') = unzip $ permuteInv perm $ zip shps lens+ scn = drop 1 $ scanl (+) 0 lens'+ perm' = concatMap (\(p, l) -> map (\i-> (scn !! p) - l + i) [0..l-1])+ $ zip perm lens+ tmp = length perm'+ perm'' = perm' ++ [tmp..tmp-1+length shp]++ dims' = concatMap (\(shp_k, srnp) ->+ map fakeDim shp_k ++ [srnp]+ ) $ zip shps' dims+ lmad' = setLMADPermutation perm'' $ LMAD off (dims' ++ map fakeDim shp)+ in IxFun (lmad' :| lmads) oshp False -- XXX: Can we be less conservative?+ where fakeDim x = LMADDim 0 0 x 0 Unknown++-- | Rotate an index function.+rotate :: (Eq num, IntegralExp num) =>+ IxFun num -> Indices num -> IxFun num+rotate (IxFun (lmad@(LMAD off dims) :| lmads) oshp cg) offs =+ let dims' = zipWith (\(LMADDim s r n p f) o ->+ if s == 0 then LMADDim 0 0 n p Unknown+ else LMADDim s (r + o) n p f+ ) dims (permuteInv (lmadPermutation lmad) offs)+ in IxFun (LMAD off dims' :| lmads) oshp cg++-- | Handle the case where a slice can stay within a single LMAD.+sliceOneLMAD :: (Eq num, IntegralExp num) =>+ IxFun num -> Slice num -> Maybe (IxFun num)+sliceOneLMAD (IxFun (lmad@(LMAD _ ldims) :| lmads) oshp cg) is = do+ let perm = lmadPermutation lmad+ is' = permuteInv perm is+ cg' = cg && slicePreservesContiguous lmad is'+ guard $ harmlessRotation lmad is'+ let lmad' = foldl sliceOne (LMAD (lmadOffset lmad) []) $ zip is' ldims+ -- need to remove the fixed dims from the permutation+ perm' = updatePerm perm $ map fst $ filter (isJust . dimFix . snd) $+ zip [0..length is' - 1] is'++ return $ IxFun (setLMADPermutation perm' lmad' :| lmads) oshp cg'+ where updatePerm ps inds = foldl (\acc p -> acc ++ decrease p) [] ps+ where decrease p =+ let d = foldl (\n i -> if i == p then -1+ else if i > p+ then n+ else if n /= -1 then n + 1+ else n+ ) 0 inds+ in if d == -1 then [] else [p - d]++ harmlessRotation' :: (Eq num, IntegralExp num) =>+ LMADDim num -> DimIndex num -> Bool+ harmlessRotation' _ (DimFix _) = True+ harmlessRotation' (LMADDim 0 _ _ _ _) _ = True+ harmlessRotation' (LMADDim _ 0 _ _ _) _ = True+ harmlessRotation' (LMADDim _ _ n _ _) dslc+ | dslc == DimSlice (n - 1) n (-1) ||+ dslc == unitSlice 0 n = True+ harmlessRotation' _ _ = False++ harmlessRotation :: (Eq num, IntegralExp num) =>+ LMAD num -> Slice num -> Bool+ harmlessRotation (LMAD _ dims) iss =+ and $ zipWith harmlessRotation' dims iss++ -- XXX: TODO: what happens to r on a negative-stride slice; is there+ -- such a case?+ sliceOne :: (Eq num, IntegralExp num) =>+ LMAD num -> (DimIndex num, LMADDim num) -> LMAD num+ sliceOne (LMAD off dims) (DimFix i, LMADDim s r n _ _) =+ LMAD (off + flatOneDim (s, r, n) i) dims+ sliceOne (LMAD off dims) (DimSlice _ ne _, LMADDim 0 _ _ p _) =+ LMAD off (dims ++ [LMADDim 0 0 ne p Unknown])+ sliceOne (LMAD off dims) (dmind, dim@(LMADDim _ _ n _ _))+ | dmind == unitSlice 0 n = LMAD off (dims ++ [dim])+ sliceOne (LMAD off dims) (dmind, LMADDim s r n p m)+ | dmind == DimSlice (n - 1) n (-1) =+ let r' = if r == 0 then 0 else n - r+ off' = off + flatOneDim (s, 0, n) (n - 1)+ in LMAD off' (dims ++ [LMADDim (s * (-1)) r' n p (invertMonotonicity m)])+ sliceOne (LMAD off dims) (DimSlice b ne 0, LMADDim s r n p _) =+ LMAD (off + flatOneDim (s, r, n) b) (dims ++ [LMADDim 0 0 ne p Unknown])+ sliceOne (LMAD off dims) (DimSlice bs ns ss, LMADDim s 0 _ p m) =+ let m' = case sgn ss of+ Just 1 -> m+ Just (-1) -> invertMonotonicity m+ _ -> Unknown+ in LMAD (off + s * bs) (dims ++ [LMADDim (ss * s) 0 ns p m'])+ sliceOne _ _ = error "slice: reached impossible case"++ slicePreservesContiguous :: (Eq num, IntegralExp num) =>+ LMAD num -> Slice num -> Bool+ slicePreservesContiguous (LMAD _ dims) slc =+ -- remove from the slice the LMAD dimensions that have stride 0.+ -- If the LMAD was contiguous in mem, then these dims will not+ -- influence the contiguousness of the result.+ -- Also normalize the input slice, i.e., 0-stride and size-1+ -- slices are rewritten as DimFixed.+ let (dims', slc') = unzip $+ filter ((/= 0) . ldStride . fst) $+ zip dims $ map normIndex slc+ -- Check that:+ -- 1. a clean split point exists between Fixed and Sliced dims+ -- 2. the outermost sliced dim has +/- 1 stride AND is unrotated or full.+ -- 3. the rest of inner sliced dims are full.+ (_, success) =+ foldl (\(found, res) (slcdim, LMADDim _ r n _ _) ->+ case (slcdim, found) of+ (DimFix{}, True ) -> (found, False)+ (DimFix{}, False) -> (found, res)+ (DimSlice _ ne ds, False) -> -- outermost sliced dim: +/-1 stride+ let res' = (r == 0 || n == ne) && (ds == 1 || ds == -1)+ in (True, res && res')+ (DimSlice _ ne ds, True) -> -- inner sliced dim: needs to be full+ let res' = (n == ne) && (ds == 1 || ds == -1)+ in (found, res && res')+ ) (False, True) $ zip slc' dims'+ in success++ normIndex :: (Eq num, IntegralExp num) =>+ DimIndex num -> DimIndex num+ normIndex (DimSlice b 1 _) = DimFix b+ normIndex (DimSlice b _ 0) = DimFix b+ normIndex d = d++-- | Slice an index function.+slice :: (Eq num, IntegralExp num) =>+ IxFun num -> Slice num -> IxFun num+slice _ [] = error "slice: empty slice"+slice ixfun@(IxFun (lmad@(LMAD _ _) :| lmads) oshp cg) dim_slices+ -- Avoid identity slicing.+ | dim_slices == map (unitSlice 0) (shape ixfun) = ixfun+ | Just ixfun' <- sliceOneLMAD ixfun dim_slices = ixfun'+ | otherwise =+ case sliceOneLMAD (iota (lmadShape lmad)) dim_slices of+ Just (IxFun (lmad' :| []) _ cg') ->+ IxFun (lmad' :| lmad : lmads) oshp (cg && cg')+ _ -> error "slice: reached impossible case"++-- | Handle the simple case where all reshape dimensions are coercions.+reshapeCoercion :: (Eq num, IntegralExp num) =>+ IxFun num -> ShapeChange num -> Maybe (IxFun num)+reshapeCoercion (IxFun (lmad@(LMAD off dims) :| lmads) _ cg) newshape = do+ let perm = lmadPermutation lmad+ (head_coercions, reshapes, tail_coercions) <- splitCoercions newshape+ let hd_len = length head_coercions+ num_coercions = hd_len + length tail_coercions+ dims' = permuteFwd perm dims+ mid_dims = take (length dims - num_coercions) $ drop hd_len dims'+ num_rshps = length reshapes+ guard (num_rshps == 0 || (num_rshps == 1 && length mid_dims == 1))+ let dims'' = map snd $ sortBy (compare `on` fst) $+ zipWith (\ld n -> (ldPerm ld, ld { ldShape = n }))+ dims' (newDims newshape)+ lmad' = LMAD off dims''+ return $ IxFun (lmad' :| lmads) (newDims newshape) cg++-- | Handle the case where a reshape operation can stay inside a single LMAD.+--+-- There are four conditions that all must hold for the result of a reshape+-- operation to remain in the one-LMAD domain:+--+-- (1) the permutation of the underlying LMAD must leave unchanged+-- the LMAD dimensions that were *not* reshape coercions.+-- (2) the repetition of dimensions of the underlying LMAD must+-- refer only to the coerced-dimensions of the reshape operation.+-- (3) similarly, the rotated dimensions must refer only to+-- dimensions that are coerced by the reshape operation.+-- (4) finally, the underlying memory is contiguous (and monotonous).+--+-- If any of these conditions do not hold, then the reshape operation will+-- conservatively add a new LMAD to the list, leading to a representation that+-- provides less opportunities for further analysis.+reshapeOneLMAD :: (Eq num, IntegralExp num) =>+ IxFun num -> ShapeChange num -> Maybe (IxFun num)+reshapeOneLMAD ixfun@(IxFun (lmad@(LMAD off dims) :| lmads) _ cg) newshape = do+ let perm = lmadPermutation lmad+ (head_coercions, reshapes, tail_coercions) <- splitCoercions newshape+ let hd_len = length head_coercions+ num_coercions = hd_len + length tail_coercions+ dims_perm = permuteFwd perm dims+ mid_dims = take (length dims - num_coercions) $ drop hd_len dims_perm+ -- Ignore rotates, as we only care about not having rotates in the+ -- dimensions that aren't coercions (@mid_dims@), which we check+ -- separately.+ mon = ixfunMonotonicityRots True ixfun++ guard $+ -- checking conditions (2) and (3)+ all (\ (LMADDim s r _ _ _) -> s /= 0 && r == 0) mid_dims &&+ -- checking condition (1)+ consecutive hd_len (map ldPerm mid_dims) &&+ -- checking condition (4)+ hasContiguousPerm ixfun && cg && (mon == Inc || mon == Dec)++ -- make new permutation+ let rsh_len = length reshapes+ diff = length newshape - length dims+ iota_shape = [0..length newshape-1]+ perm' = map (\i -> let ind = if i < hd_len+ then i else i - diff+ in if (i >= hd_len) && (i < hd_len + rsh_len)+ then i -- already checked mid_dims not affected+ else let p = ldPerm (dims !! ind)+ in if p < hd_len+ then p+ else p + diff+ ) iota_shape+ -- split the dimensions+ (support_inds, repeat_inds) =+ foldl (\(sup, rpt) (i, shpdim, ip) ->+ case (i < hd_len, i >= hd_len + rsh_len, shpdim) of+ (True, _, DimCoercion n) ->+ case dims_perm !! i of+ (LMADDim 0 _ _ _ _) -> ( sup, (ip, n) : rpt )+ (LMADDim _ r _ _ _) -> ( (ip, (r, n)) : sup, rpt )+ (_, True, DimCoercion n) ->+ case dims_perm !! (i-diff) of+ (LMADDim 0 _ _ _ _) -> ( sup, (ip, n) : rpt )+ (LMADDim _ r _ _ _) -> ( (ip, (r, n)) : sup, rpt )+ (False, False, _) ->+ ( (ip, (0, newDim shpdim)) : sup, rpt )+ -- already checked that the reshaped+ -- dims cannot be repeats or rotates+ _ -> error "reshape: reached impossible case"+ ) ([], []) $ reverse $ zip3 iota_shape newshape perm'++ (sup_inds, support) = unzip $ sortBy (compare `on` fst) support_inds+ (rpt_inds, repeats) = unzip repeat_inds+ LMAD off' dims_sup = makeRotIota mon off support+ repeats' = map (\n -> LMADDim 0 0 n 0 Unknown) repeats+ dims' = map snd $ sortBy (compare `on` fst)+ $ zip sup_inds dims_sup ++ zip rpt_inds repeats'+ lmad' = LMAD off' dims'+ return $ IxFun (setLMADPermutation perm' lmad' :| lmads) (newDims newshape) cg+ where consecutive _ [] = True+ consecutive i [p]= i == p+ consecutive i ps = and $ zipWith (==) ps [i, i+1..]++splitCoercions :: (Eq num, IntegralExp num) =>+ ShapeChange num -> Maybe (ShapeChange num, ShapeChange num, ShapeChange num)+splitCoercions newshape' = do+ let (head_coercions, newshape'') = span isCoercion newshape'+ (reshapes, tail_coercions) = break isCoercion newshape''+ guard (all isCoercion tail_coercions)+ return (head_coercions, reshapes, tail_coercions)+ where isCoercion DimCoercion{} = True+ isCoercion _ = False++-- | Reshape an index function.+reshape :: (Eq num, IntegralExp num) =>+ IxFun num -> ShapeChange num -> IxFun num+reshape ixfun new_shape+ | Just ixfun' <- reshapeCoercion ixfun new_shape = ixfun'+ | Just ixfun' <- reshapeOneLMAD ixfun new_shape = ixfun'+reshape (IxFun (lmad0 :| lmad0s) oshp cg) new_shape =+ case iota (newDims new_shape) of+ IxFun (lmad :| []) _ _ -> IxFun (lmad :| lmad0 : lmad0s) oshp cg+ _ -> error "reshape: reached impossible case"++rank :: IntegralExp num =>+ IxFun num -> Int+rank (IxFun (LMAD _ sss :| _) _ _) = length sss++-- | Handle the case where a rebase operation can stay within m + n - 1 LMADs,+-- where m is the number of LMADs in the index function, and n is the number of+-- LMADs in the new base. If both index function have only on LMAD, this means+-- that we stay within the single-LMAD domain.+--+-- We can often stay in that domain if the original ixfun is essentially a+-- slice, e.g. `x[i, (k1,m,s1), (k2,n,s2)] = orig`.+--+-- XXX: TODO: handle repetitions in both lmads.+--+-- How to handle repeated dimensions in the original?+--+-- (a) Shave them off of the last lmad of original+-- (b) Compose the result from (a) with the first+-- lmad of the new base+-- (c) apply a repeat operation on the result of (b).+--+-- However, I strongly suspect that for in-place update what we need is actually+-- the INVERSE of the rebase function, i.e., given an index function new-base+-- and another one orig, compute the index function ixfun0 such that:+--+-- new-base == rebase ixfun0 ixfun, or equivalently:+-- new-base == ixfun o ixfun0+--+-- because then I can go bottom up and compose with ixfun0 all the index+-- functions corresponding to the memory block associated with ixfun.+rebaseNice :: (Eq num, IntegralExp num) =>+ IxFun num -> IxFun num -> Maybe (IxFun num)+rebaseNice+ new_base@(IxFun (lmad_base :| lmads_base) _ cg_base)+ ixfun@(IxFun lmads shp cg) = do+ let (lmad_full :| lmads') = NE.reverse lmads+ ((outer_shapes, inner_shape), lmad) = shaveoffRepeats lmad_full+ dims = lmadDims lmad+ perm = lmadPermutation lmad+ perm_base = lmadPermutation lmad_base++ guard $+ -- Core rebase condition.+ base ixfun == shape new_base+ -- Conservative safety conditions: ixfun is contiguous and has known+ -- monotonicity for all dimensions.+ && cg && all ((/= Unknown) . ldMon) dims+ -- XXX: We should be able to handle some basic cases where both index+ -- functions have non-trivial permutations.+ && (hasContiguousPerm ixfun || hasContiguousPerm new_base)+ -- We need the permutations to be of the same size if we want to compose+ -- them. They don't have to be of the same size if the ixfun has a trivial+ -- permutation. Supporting this latter case allows us to rebase when ixfun+ -- has been created by slicing with fixed dimensions.+ && (length perm == length perm_base || hasContiguousPerm ixfun)+ -- To not have to worry about ixfun having non-1 strides, we also check that+ -- it is a row-major array (modulo permutation, which is handled+ -- separately). Accept a non-full innermost dimension. XXX: Maybe this can+ -- be less conservative?+ && and (zipWith3 (\sn ld inner -> sn == ldShape ld || (inner && ldStride ld == 1))+ shp dims (replicate (length dims - 1) False ++ [True]))++ -- Compose permutations, reverse strides and adjust offset if necessary.+ let perm_base' = if hasContiguousPerm ixfun+ then perm_base+ else map (perm !!) perm_base+ lmad_base' = setLMADPermutation perm_base' lmad_base+ dims_base = lmadDims lmad_base'+ n_fewer_dims = length dims_base - length dims+ (dims_base', offs_contrib) = unzip $+ zipWith (\(LMADDim s1 r1 n1 p1 _) (LMADDim _ r2 _ _ m2) ->+ let (s', off') | m2 == Inc = (s1, 0)+ | otherwise = (s1 * (-1), s1 * (n1 - 1))+ r' | m2 == Inc = if r2 == 0 then r1 else r1 + r2+ | r1 == 0 = r2+ | r2 == 0 = n1 - r1+ | otherwise = n1 - r1 + r2+ in (LMADDim s' r' n1 (p1 - n_fewer_dims) Inc, off'))+ -- If @dims@ is morally a slice, it might have fewer dimensions than+ -- @dims_base@. Drop extraneous outer dimensions.+ (drop n_fewer_dims dims_base) dims+ off_base = lmadOffset lmad_base' + sum offs_contrib+ lmad_base''+ | lmadOffset lmad == 0 = LMAD off_base dims_base'+ | otherwise =+ -- If the innermost dimension of the ixfun was not full (but still+ -- had a stride of 1), add its offset relative to the new base.+ setLMADShape (lmadShape lmad)+ (LMAD (off_base + ldStride (last dims_base) * lmadOffset lmad)+ dims_base')+ new_base' = IxFun (lmad_base'' :| lmads_base) shp cg_base+ IxFun lmads_base' _ _ = if all null outer_shapes && null inner_shape+ then new_base'+ else repeat new_base' outer_shapes inner_shape+ lmads'' = lmads' ++@ lmads_base'+ return $ IxFun lmads'' shp (cg && cg_base)+ where shaveoffRepeats :: (Eq num, IntegralExp num) =>+ LMAD num -> (([Shape num], Shape num), LMAD num)+ shaveoffRepeats lmad =+ -- Given an input lmad, this function computes a repetition @r@ and a new lmad+ -- @res@, such that @repeat r res@ is identical to the input lmad.+ let perm = lmadPermutation lmad+ dims = lmadDims lmad+ -- compute the Repeat:+ resacc= foldl (\acc (LMADDim s _ n _ _) ->+ case acc of+ rpt : acc0 ->+ if s == 0 then (n : rpt) : acc0+ else [] : (rpt : acc0)+ _ -> error "shaveoffRepeats: empty accumulator"+ ) [[]] $ reverse $ permuteFwd perm dims+ last_shape = last resacc+ shapes = take (length resacc - 1) resacc+ -- update permutation and lmad:+ howManyRepLT k =+ foldl (\i (LMADDim s _ _ p _) ->+ if s == 0 && p < k then i + 1 else i+ ) 0 dims+ dims' = foldl (\acc (LMADDim s r n p info) ->+ if s == 0 then acc+ else let p' = p - howManyRepLT p+ in LMADDim s r n p' info : acc+ ) [] $ reverse dims+ lmad' = LMAD (lmadOffset lmad) dims'+ in ((shapes, last_shape), lmad')++-- | Rebase an index function on top of a new base.+rebase :: (Eq num, IntegralExp num) =>+ IxFun num -> IxFun num -> IxFun num+rebase new_base@(IxFun lmads_base shp_base cg_base) ixfun@(IxFun lmads shp cg)+ | Just ixfun' <- rebaseNice new_base ixfun = ixfun'+ -- In the general case just concatenate LMADs since this refers to index+ -- function composition, which is always safe.+ | otherwise =+ let (lmads_base', shp_base') =+ if base ixfun == shape new_base+ then (lmads_base, shp_base)+ else let IxFun lmads' shp_base'' _ = reshape new_base $ map DimCoercion shp+ in (lmads', shp_base'')+ in IxFun (lmads @++@ lmads_base') shp_base' (cg && cg_base)++ixfunMonotonicity :: (Eq num, IntegralExp num) => IxFun num -> Monotonicity+ixfunMonotonicity = ixfunMonotonicityRots False++-- | Offset index. Results in the index function corresponding to indexing with+-- @i@ on the outermost dimension.+offsetIndex :: (Eq num, IntegralExp num) =>+ IxFun num -> num -> IxFun num+offsetIndex ixfun i | i == 0 = ixfun+offsetIndex ixfun i =+ case shape ixfun of+ d : ds -> slice ixfun (DimSlice i (d - i) 1 : map (unitSlice 0) ds)+ [] -> error "offsetIndex: underlying index function has rank zero"++-- | Stride index. Results in the index function corresponding to making the+-- outermost dimension strided by @s@.+strideIndex :: (Eq num, IntegralExp num) =>+ IxFun num -> num -> IxFun num+strideIndex ixfun s =+ case shape ixfun of+ d : ds -> slice ixfun (DimSlice 0 d s : map (unitSlice 0) ds)+ [] -> error "offsetIndex: underlying index function has rank zero"++-- | If the memory support of the index function is contiguous and row-major+-- (i.e., no transpositions, repetitions, rotates, etc.), then this should+-- return the offset from which the memory-support of this index function+-- starts.+linearWithOffset :: (Eq num, IntegralExp num) =>+ IxFun num -> num -> Maybe num+linearWithOffset ixfun@(IxFun (lmad :| []) _ cg) elem_size+ | hasContiguousPerm ixfun && cg && ixfunMonotonicity ixfun == Inc =+ Just $ lmadOffset lmad * elem_size+linearWithOffset _ _ = Nothing++-- | Similar restrictions to @linearWithOffset@ except for transpositions, which+-- are returned together with the offset.+rearrangeWithOffset :: (Eq num, IntegralExp num) =>+ IxFun num -> num -> Maybe (num, [(Int,num)])+rearrangeWithOffset (IxFun (lmad :| []) oshp cg) elem_size = do+ -- Note that @cg@ describes whether the index function is+ -- contiguous, *ignoring permutations*. This function requires that+ -- functionality.+ let perm = lmadPermutation lmad+ perm_contig = [0..length perm-1]+ offset <- linearWithOffset+ (IxFun (setLMADPermutation perm_contig lmad :| []) oshp cg) elem_size+ return (offset, zip perm (permuteFwd perm (lmadShapeBase lmad)))+rearrangeWithOffset _ _ = Nothing++isLinear :: (Eq num, IntegralExp num) => IxFun num -> Bool+isLinear = (== Just 0) . flip linearWithOffset 1++permuteFwd :: Permutation -> [a] -> [a]+permuteFwd ps elems = map (elems !!) ps++permuteInv :: Permutation -> [a] -> [a]+permuteInv ps elems = map snd $ sortBy (compare `on` fst) $ zip ps elems++flatOneDim :: (Eq num, IntegralExp num) =>+ (num, num, num) -> num -> num+flatOneDim (s, r, n) i+ | s == 0 = 0+ | r == 0 = i * s+ | otherwise = ((i + r) `mod` n) * s++-- | Generalised iota with user-specified offset and strides.+makeRotIota :: IntegralExp num =>+ Monotonicity -> num -> [(num, num)] -> LMAD num+makeRotIota mon off support+ | mon == Inc || mon == Dec =+ let rk = length support+ (rs, ns) = unzip support+ ss0 = reverse $ take rk $ scanl (*) 1 $ reverse ns+ ss = if mon == Inc+ then ss0+ else map (* (-1)) ss0+ ps = map fromIntegral [0..rk-1]+ fi = replicate rk mon+ in LMAD off $ zipWith5 LMADDim ss rs ns ps fi+ | otherwise = error "makeRotIota: requires Inc or Dec"++-- | Check monotonicity of an index function.+ixfunMonotonicityRots :: (Eq num, IntegralExp num) =>+ Bool -> IxFun num -> Monotonicity+ixfunMonotonicityRots ignore_rots (IxFun (lmad :| lmads) _ _) =+ let mon0 = lmadMonotonicityRots lmad+ in if all ((== mon0) . lmadMonotonicityRots) lmads+ then mon0+ else Unknown+ where lmadMonotonicityRots :: (Eq num, IntegralExp num) =>+ LMAD num -> Monotonicity+ lmadMonotonicityRots (LMAD _ dims)+ | all (isMonDim Inc) dims = Inc+ | all (isMonDim Dec) dims = Dec+ | otherwise = Unknown++ isMonDim :: (Eq num, IntegralExp num) =>+ Monotonicity -> LMADDim num -> Bool+ isMonDim mon (LMADDim s r _ _ ldmon) =+ s == 0 || ((ignore_rots || r == 0) && mon == ldmon)
− src/Futhark/Representation/ExplicitMemory/Lmad.hs
@@ -1,761 +0,0 @@-{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}--- | An index function represents a mapping from an array index space--- to a flat byte offset. This implements a representation for the--- index function based on linear-memory accessor descriptors, see--- Zhu, Hoeflinger and David work. Our specific representation is:--- LMAD = \overline{s,r,n}^k + o, where `o` is the offset, and `s_j`,--- `r_j`, and `n_j` are the stride, the rotate factor and the number--- of elements on dimension j. Dimensions are ordered in row major fashion.--- By definition, the LMAD above denotes the set of points:--- \{ o + \Sigma_{j=0}^{k} ((i_j+r_j) `mod` n_j)*s_j,--- \forall i_j such that 0<=i_j<n_j, j=1..k \}----module Futhark.Representation.ExplicitMemory.Lmad- (- IxFun(..)- , index- , iota- , offsetIndex- , strideIndex- , permute- , rotate- , reshape- , slice- , base- , rebase- , repeat- , isContiguous- , shape- , rank- , getMonotonicity- , linearWithOffset- , rearrangeWithOffset- , isDirect- , isLinear- , substituteInIxFun- )- where--import Data.List as L hiding (repeat)-import Control.Monad.Identity-import Control.Monad.Writer-import Prelude hiding (mod, repeat)-import qualified Data.Map.Strict as M--import Futhark.Transform.Substitute-import Futhark.Transform.Rename--import Futhark.Representation.AST.Syntax- (ShapeChange, DimChange(..), DimIndex(..), Slice, unitSlice, VName)-import Futhark.Representation.AST.Attributes-import Futhark.Util.IntegralExp-import Futhark.Util.Pretty-import Futhark.Analysis.PrimExp.Convert----import Debug.Trace--type Shape num = [num]-type Indices num = [num]-type Permutation = [Int]---- | TODO: should only be: Inc | Dec | Unknown--- because together with the contiguosness--- this is enough information-data DimInfo = Inc | Dec | Unknown- -- ^ monotonously increasing, decreasing or unknwon- deriving (Show,Eq)---- | LMAD's representation consists of a permutation,--- a general offset, and, for each dimension a stride,--- rotate factor, number of elements, permutation, and--- ``fullness'' and unit-stride info for each dimension.--- Note that the permutation is not strictly necessary--- in that the permutation can be performed directly--- on Lmad dimensions, but then it is difficult to--- extract the permutation back from an Lmad.-data Lmad num = Lmad num [(num, num, num, Int, DimInfo)]- deriving (Show,Eq)---- | LMAD algebra is closed under composition w.r.t.--- operators such as permute, repeat, index and slice.--- However, other operations, such as reshape, cannot be--- always represented inside the LMAD algebra.--- It follows that the general representation of an index--- function is a list of LMADS, in which each following--- LMAD in the list implicitly corresponds to an irregular--- reshaping operation.--- However, we expect that the common case is when the index--- function is one LMAD -- we call this the `Nice` representation.--- Finally, the list of LMADs is tupled with the shape of the--- original array, and with contiguous info, i.e., if we instantiate--- all the points of the current index function, do we get a--- contiguous memory interval?-data IxFun num = IxFun [Lmad num] (Shape num) Bool- deriving (Show,Eq)-------------------------------------- Instances Implementation --------------------------------------instance Pretty DimInfo where- ppr Inc = text "I"- ppr Dec = text "D"- ppr Unknown = text "U"--instance Pretty num => Pretty (Lmad num) where- ppr (Lmad tau srnps) =- let (ss, rs, ns, ps, fs) = unzip5 srnps- in text " | " <> ppr tau <>- text " + " <> brackets (commasep $ map ppr ss) <>- text "v" <> brackets (commasep $ map ppr rs) <>- text "v" <> brackets (commasep $ map ppr ns) <>- text "v" <> brackets (commasep $ map ppr ps) <>- text "v" <> brackets (commasep $ map ppr fs) <>- text " | "--instance Pretty num => Pretty (IxFun num) where- ppr (IxFun lmads orgshp cg) =- text "Shape: " <> braces (commasep $ map ppr orgshp) <>- text " LMADS: " <> braces (stack $ map ppr lmads) <>- text " CONTIG: "<> text (show cg)--instance Substitute num => Substitute (Lmad num) where- substituteNames substs = fmap $ substituteNames substs--instance Substitute num => Substitute (IxFun num) where- substituteNames substs = fmap $ substituteNames substs--instance Substitute num => Rename (Lmad num) where- rename = substituteRename--instance Substitute num => Rename (IxFun num) where- rename = substituteRename---instance FreeIn num => FreeIn (Lmad num) where- freeIn = foldMap freeIn--instance FreeIn num => FreeIn (IxFun num) where- freeIn = foldMap freeIn--instance Functor Lmad where- fmap f = runIdentity . traverse (return . f)--instance Functor IxFun where- fmap f = runIdentity . traverse (return . f)--instance Foldable Lmad where- foldMap f = execWriter . traverse (tell . f)--instance Foldable IxFun where- foldMap f = execWriter . traverse (tell . f)--instance Traversable Lmad where- traverse f (Lmad x l) =- Lmad <$> f x <*> traverse f' l- where f' (a, b, c, k, info) =- (,,,,) <$> f a <*> f b <*> f c <*> pure k <*> pure info--instance Traversable IxFun where- traverse f (IxFun lmads shp cg) =- IxFun <$> traverse (traverse f) lmads <*> traverse f shp <*> pure cg---- | Substituting a name with a PrimExp in an Lmad.-substituteInLmad :: M.Map VName (PrimExp VName) -> Lmad (PrimExp VName)- -> Lmad (PrimExp VName)-substituteInLmad tab (Lmad off srnpds) =- let off' = substituteInPrimExp tab off- srnpds' = map (\(s,r,n,p,d) ->- ( substituteInPrimExp tab s- , substituteInPrimExp tab r- , substituteInPrimExp tab n- , p, d- )- ) srnpds- in Lmad off' srnpds'---- | Substituting a name with a PrimExp in an index function.-substituteInIxFun :: M.Map VName (PrimExp VName) -> IxFun (PrimExp VName)- -> IxFun (PrimExp VName)-substituteInIxFun tab (IxFun lmads shp b) =- IxFun (map (substituteInLmad tab) lmads)- (map (substituteInPrimExp tab) shp)- b------------------------------------------------ Index Function/LMAD Implementation -------------------------------------------------- | whether this is a row-major array-isDirect :: (Eq num, IntegralExp num) => IxFun num -> Bool-isDirect (IxFun [Lmad off info] shp True)- | length shp == length info,- all (\((s,r,n,p,_),i,d) -> s==1 && r==0 && n==d && p==i)- (zip3 info [0..length info - 1] shp),- off == 0 = True- | otherwise = False-isDirect _ = False---- | whether an index function has contiguous memory support-isContiguous :: (Eq num, IntegralExp num) => IxFun num -> Bool-isContiguous (IxFun _ _ cg) = cg---- | Shape of an Lmad-shape0 :: (Eq num, IntegralExp num) => Lmad num -> Shape num-shape0 lmad@(Lmad _ srns) =- map (\(_,_,z,_,_)->z) $ permuteInv (getPermutation lmad) srns---- | Shape of an index function-shape :: (Eq num, IntegralExp num) => IxFun num -> Shape num-shape (IxFun [] _ _) = error "shape: empty index function"-shape (IxFun (lmad:_) _ _) = shape0 lmad---- | Computing the flat memory index for a complete set `inds`--- of array indices and a certain element size `elem_size`.-index :: (IntegralExp num, Eq num) =>- IxFun num -> Indices num -> num -> num-index (IxFun [] _ _) _ _ = error "index: empty index function"-index (IxFun [lmad] _ _) iis elm_size = index0 lmad iis elm_size-index (IxFun (lmad1:lmad2:lmads) oshp c) iis elm_size =- let i_flat = index0 lmad1 iis 1- new_inds = unflattenIndex (shape0 lmad2) i_flat- in index (IxFun (lmad2:lmads) oshp c) new_inds elm_size---- | Helper for index: computing the flat index of an Lmad.-index0 :: (Eq num, IntegralExp num) =>- Lmad num -> Indices num -> num -> num-index0 lmad@(Lmad tau srnps) inds elm_size =- let prod = sum $ zipWith flatOneDim- (map (\(s,r,n,_,_) -> (s,r,n)) srnps)- (permuteInv (getPermutation lmad) inds)- ind = tau + prod- in if elm_size == 1 then ind else ind * elm_size---- | iota-iota :: (IntegralExp num) => Shape num -> IxFun num-iota ns = IxFun [makeRotIota Inc 0 $ zip rs ns] ns True- where rs = replicate (length ns) 0---- | permute dimensions-permute :: IntegralExp num =>- IxFun num -> Permutation -> IxFun num-permute (IxFun [] _ _) _ = error "permute: empty index function"-permute (IxFun (lmad:lmads) oshp cg) ps =- let perm = map (\p -> ps !! p) $ getPermutation lmad- in IxFun (setPermutation perm lmad : lmads) oshp cg---- | repeating dimensions-repeat :: (Eq num, IntegralExp num) =>- IxFun num -> [Shape num] -> Shape num -> IxFun num-repeat (IxFun [] _ _) _ _ = error "repeat: empty index function"-repeat (IxFun (lmad@(Lmad tau srnps) : lmads) oshp cg) shps shp =- let perm = getPermutation lmad- -- inverse permute the shapes and update the permutation!- lens = map (\s -> 1 + length s) shps- (shps', lens') = unzip $ permuteInv perm $ zip shps lens- scn = drop 1 $ scanl (+) 0 lens'- perm' = concatMap (\(p,l) -> map (\i-> (scn!!p)-l+i) [0..l-1])- $ zip perm lens- tmp = length perm'- perm'' = perm' ++ [tmp..tmp-1+length shp]-- srnps' = concatMap (\(shp_k, srnp)->- map fakeDim shp_k ++ [srnp]- ) $ zip shps' srnps- lmad' = setPermutation perm'' $ Lmad tau (srnps' ++ map fakeDim shp)- in IxFun (lmad' : lmads) oshp cg- where fakeDim x = (0,0,x,0,Unknown)---- | Rotating an index function:-rotate :: (Eq num, IntegralExp num) =>- IxFun num -> Indices num -> IxFun num-rotate (IxFun [] _ _) _ = error "rotate: empty index function"-rotate (IxFun (lmad@(Lmad off srnps) : lmads) oshp cg) offs =- let srnps' = zipWith (\(s,r,n,p,f) o ->- if s == 0 then (0,0,n,p,Unknown)- else (s,r+o,n,p,f)- ) srnps $ permuteInv (getPermutation lmad) offs- in IxFun (Lmad off srnps':lmads) oshp cg----- | Slicing an index function.-slice :: (Eq num, IntegralExp num) =>- IxFun num -> Slice num -> IxFun num-slice (IxFun [] _ _) _ = error "slice: empty index function"-slice _ [] = error "slice: empty slice ???"-slice ixfn dim_slices- -- Avoid identity slicing.- | dim_slices == map (unitSlice 0) (shape ixfn) = ixfn-slice (IxFun (lmad@(Lmad _ srnpfs):lmads) oshp cg) is =- let perm= getPermutation lmad- is' = permuteInv perm is- contig = cg && preservesContiguous lmad is'- in if harmlessRotation lmad is'- then let lmad' = foldl sliceOne (Lmad (getOffset lmad) [])- $ zip is' srnpfs- -- need to remove the fixed dims from the permutation- perm' = updatePerm perm $ map fst $ filter isFixedDim $- zip [0..length is' - 1] is'- in IxFun (setPermutation perm' lmad':lmads) oshp contig- else -- falls outside LMAD formula, hence append a new LMAD- case slice (iota (shape0 lmad)) is of- IxFun [lmad'] _ _ -> IxFun (lmad':lmad:lmads) oshp contig- _ -> error "slice: reached impossible case!"- where isFixedDim (_,DimFix{}) = True- isFixedDim _ = False-- updatePerm ps inds = foldl (\acc p -> acc ++ decrease p) [] ps- where decrease p =- let d = foldl (\n i -> if i == p then (-1)- else if i > p- then n- else if n /= (-1) then n+1- else n- ) 0 inds- in if d == (-1) then [] else [p-d]-- harmlessRotation0 :: (Eq num, IntegralExp num) =>- (num,num,num,Int,DimInfo) -> DimIndex num -> Bool- harmlessRotation0 _ (DimFix _) = True- harmlessRotation0 (0,_,_,_,_) _ = True- harmlessRotation0 (_,0,_,_,_) _ = True- harmlessRotation0 (_,_,n,_,_) dslc- | dslc == DimSlice (n-1) n (-1) ||- dslc == unitSlice 0 n = True- harmlessRotation0 _ _ = False-- harmlessRotation :: (Eq num, IntegralExp num) =>- Lmad num -> Slice num -> Bool- harmlessRotation (Lmad _ srnps) iss =- and $ zipWith harmlessRotation0 srnps iss-- -- | TODO: what happens to r on a negative-stride slice; is there a such case?- sliceOne :: (Eq num, IntegralExp num) =>- Lmad num -> (DimIndex num, (num,num,num,Int,DimInfo)) -> Lmad num- sliceOne (Lmad tau srns) (DimFix i, (s,r,n,_,_)) =- Lmad (tau + flatOneDim (s,r,n) i) srns- sliceOne (Lmad tau srns) (DimSlice _ ne _, (0,_,_,p,_)) =- Lmad tau (srns ++ [(0,0,ne,p,Unknown)])- sliceOne (Lmad tau srns) (dmind, srn@(_,_,n,_,_))- | dmind == unitSlice 0 n = Lmad tau (srns ++ [srn])- sliceOne (Lmad tau srns) (dmind, (s,r,n,p,f))- | dmind == DimSlice (n-1) n (-1) =- let r' = if r == 0 then 0 else n-r- in Lmad tau' (srns ++ [(s*(-1),r',n,p, invertInfo f)])- where tau' = tau + flatOneDim (s,0,n) (n-1)- sliceOne (Lmad tau srns) (DimSlice b ne 0, (s,r,n,p,_)) =- Lmad (tau + flatOneDim (s,r,n) b) (srns ++ [(0,0,ne,p,Unknown)])- sliceOne (Lmad tau srns) (DimSlice bs ns ss, (s,0,_,p,f)) =- let f' = case sgn ss of- Just 1 -> f- Just (-1) -> invertInfo f- _ -> Unknown- in Lmad (tau + s*bs) (srns ++ [(ss*s,0,ns,p,f')])- sliceOne _ _ = error "slice: reached impossible case!"-- normIndex :: (Eq num, IntegralExp num) =>- DimIndex num -> DimIndex num- normIndex (DimSlice b 1 _) = DimFix b- normIndex (DimSlice b _ 0) = DimFix b- normIndex d = d-- preservesContiguous :: (Eq num, IntegralExp num) =>- Lmad num -> Slice num -> Bool- preservesContiguous (Lmad _ srnps) slc =- -- remove from the slice the Lmad dimensions who have stride 0.- -- If the Lmad was contiguous in mem, then these dims will not- -- influence the contiguousness of the result.- -- Also normalize the input slice, i.e., 0-stride and size-1- -- slices are rewritten as DimFixed.- let (srnps', slc') = unzip $- filter (\((s,_,_,_,_),_) -> s /= 0) $- zip srnps $ map normIndex slc- -- Check that:- -- 1. a clean split point exists between Fixed and Sliced dims- -- 2. the outermost sliced dim has +/- 1 stride AND is unrottated or full.- -- 3. the rest of inner sliced dims are full.- (_, success) =- foldl (\(found,res) (slcdim, (_,r,n,_,_)) ->- case (slcdim, found) of- (DimFix{}, True ) -> (found, False)- (DimFix{}, False) -> (found, res)- (DimSlice _ ne ds, False) -> -- outermost sliced dim: +/-1 stride- let res' = (r == 0 || n == ne) && (ds == 1 || ds == (-1))- in (True, res && res')- (DimSlice _ ne ds, True) -> -- inner sliced dim: needs to be full- let res' = (n == ne) && (ds == 1 || ds == (-1))- in (found, res && res')- ) (False,True) $ zip slc' srnps'- in success---- | Reshaping an index function.--- There are four conditions that all must hold for the result--- of a reshape operation to remain into the one-Lmad domain:--- (1) the permutation of the underlying Lmad must leave unchanged--- the Lmad dimensions that were *not* reshape coercions.--- (2) the repetition of dimensions of the underlying Lmad must--- refer only to the coerced-dimensions of the reshape operation.--- (3) similarly, the rotated dimensions must refer only to--- dimensions that are coerced by the reshape operation.--- (4) finally, the underlying memory is contiguous (and monotonous)------ If any of this conditions does not hold then the reshape operation--- will conservatively add a new Lmad to the list, leading to a--- representation that provides less opportunities for further analysis.------ Actually there are some special cases that need to be treated,--- for example if everything is a coercion, then it should succeed--- no matter what.-reshape :: (Eq num, IntegralExp num) =>- IxFun num -> ShapeChange num -> IxFun num-reshape (IxFun [] _ _) _ =- error "reshape: empty index function"--reshape ixfn@(IxFun (lmad@(Lmad tau srnps):lmads) oshp cg) newshape- | -- first take care of the case when this is all a coercion!- perm <- getPermutation lmad,- Just (head_coercions, reshapes, tail_coercions) <-- splitCoercions newshape,- hd_len <- length head_coercions,- num_coercions <- hd_len + length tail_coercions,- srnps' <- permuteFwd perm srnps,- mid_srnps <- take (length srnps - num_coercions) $- drop hd_len srnps',- num_rshps <- length reshapes,- num_rshps == 0 || (num_rshps == 1 && length mid_srnps == 1),- srnps'' <- map snd $ L.sortBy sortGT $- zipWith (\(s,r,_,p,f) n -> (p,(s,r,n,p,f)))- srnps' $ newDims newshape- = IxFun (Lmad tau srnps'':lmads) oshp cg-- | perm <- getPermutation lmad,- Just (head_coercions, reshapes, tail_coercions) <-- splitCoercions newshape,- hd_len <- length head_coercions,- num_coercions <- hd_len + length tail_coercions,- srnps_perm <- permuteFwd perm srnps,- mid_srnps <- take (length srnps - num_coercions) $- drop hd_len srnps_perm,- -- checking conditions (2) and (3)- all (\ (s,r,_,_,_) -> s /= 0 && r == 0) mid_srnps,- -- checking condition (1)- consecutive hd_len $ map (\(_,_,_,p,_)->p) mid_srnps,- -- checking condition (4)- info <- getMonotonicityRots True ixfn,- cg && (info == Inc || info == Dec),- -- make new permutation- rsh_len <- length reshapes,- diff <- length newshape - length srnps,- iota_shape <- [0..length newshape-1],- perm' <- map (\i -> let ind = if i < hd_len- then i else i - diff- in if (i>=hd_len) && (i < hd_len+rsh_len)- then i -- already checked mid_srnps not affected- else let (_,_,_,p,_) = srnps !! ind- in if p < hd_len- then p else p + diff- ) iota_shape,- -- split the dimensions- (suport_inds, repeat_inds) <-- foldl (\(sup,rpt) (i,shpdim,ip) ->- case (i < hd_len, i >= hd_len+rsh_len, shpdim) of- (True, _, DimCoercion n) ->- case srnps_perm !! i of- (0,_,_,_,_) -> ( sup, (ip,n) : rpt )- (_,r,_,_,_) -> ( (ip,(r,n)) : sup, rpt )- (_, True, DimCoercion n) ->- case srnps_perm !! (i-diff) of- (0,_,_,_,_) -> ( sup, (ip,n) : rpt )- (_,r,_,_,_) -> ( (ip,(r,n)) : sup, rpt )- (False, False, _) ->- ( (ip, (0, newDim shpdim)) : sup, rpt )- -- already checked that the reshaped- -- dims cannot be repeats or rotates- _ -> error "reshape: reached impossible case!"- ) ([],[]) $ reverse $ zip3 iota_shape newshape perm',-- (sup_inds, support) <- unzip $ L.sortBy sortGT suport_inds,- (rpt_inds, repeats) <- unzip repeat_inds,- Lmad tau' srnps_sup <- makeRotIota info tau support,- repeats' <- map (\n -> (0,0,n,0,Unknown)) repeats,- srnps' <- map snd $ L.sortBy sortGT $- zip sup_inds srnps_sup ++ zip rpt_inds repeats'- = IxFun (setPermutation perm' (Lmad tau' srnps') : lmads) oshp cg- where splitCoercions newshape' = do- let (head_coercions, newshape'') = span isCoercion newshape'- let (reshapes, tail_coercions) = break isCoercion newshape''- guard (all isCoercion tail_coercions)- return (head_coercions, reshapes, tail_coercions)-- isCoercion DimCoercion{} = True- isCoercion _ = False-- consecutive _ [] = True- consecutive i [p]= i == p- consecutive i ps = and $ zipWith (==) ps [i, i+1..]--reshape (IxFun lmads oshp cg) newshape =- let new_dims = newDims newshape- in case iota new_dims of- IxFun [lmad] _ _ -> IxFun (lmad : lmads) oshp cg- _ -> error "reshape: impossible case reached"---rank :: IntegralExp num =>- IxFun num -> Int-rank (IxFun [] _ _) = error "rank: empty index function"-rank (IxFun (Lmad _ sss : _) _ _) = length sss--base :: IxFun num -> Shape num-base (IxFun [] _ _) = error "base: empty index function"-base (IxFun _ osh _) = osh---- | Correctness assumption: the shape of the new base is--- equal to the base of the index function (to be rebased).-rebase :: (Eq num, IntegralExp num) =>- IxFun num- -> IxFun num- -> IxFun num-rebase (IxFun [] _ _) _ = error "base: empty index function 1"-rebase _ (IxFun [] _ _) = error "base: empty index function 2"---- | Special Case: `x[i, (k1,m,s1), (k2,n,s2)] = orig`--- The new base would be the slice of x.--- If orig is full (contiguous) and monotonicity is known--- for all orig's dimensions (i.e., either Inc or Dec)--- Then we can compose the two into one lmad, the result--- mainly adapts the index function of the new base.--- How to handle repeated dimensions in the original?--- (a) Shave them off of the last lmad of original--- (b) Compose the result from (a) with the first--- lmad of the new base--- (c) apply a repeat operation on the result of (b).--- However, I strongly suspect that for in-place update--- what we need is actually the INVERSE of the rebase function,--- i.e., given an index function new-base and another one orig,--- compute the index function ixfn0 such that:--- new-base == rebase ixfn0 ixfn, or equivalently:--- new-base == ixfn o ixfn0--- because then I can go bottom up and compose with ixfn0--- all the index functions corresponding to the memory--- block associated with ixfn.-rebase newbase@(IxFun (lmad_base:lmads_base) shp_base cg_base)- ixfn@(IxFun lmads shp cg)- | lmad_full <- last lmads,- (repeats, lmad) <- shaveoffRepeats lmad_full,- perm <- getPermutation lmad,- srnps<- getLmadDims lmad,- -- sanity condition- base ixfn == shape newbase,- -- TODO: handle repetitions in both lmads.- -- 1) orig is full and monotonicity is known for all dims- cg && length shp == length srnps,- and $ zipWith (\n2 (_,_,n1,_,i1) -> n1 == n2 && i1 /= Unknown)- shp srnps,- -- Building the result srnps: compose permutations,- -- reverse strides and adjust offset if necessary.- perm_base <- getPermutation lmad_base,- perm' <- map (\p -> perm !! p) perm_base,- lmad_base' <- setPermutation perm' lmad_base,- (srnps_base, taus_contrib) <- unzip $- zipWith (\ (s1,r1,n1,p1,_) (_,r2,_,_,i2) ->- -- assumes the monotonicity of all dimensions is known- let (s', tau') = if i2 == Inc then (s1,0)- else (s1*(-1),s1*(n1-1))- r' | i2 == Inc = if r2 == 0 then r1 else r1+r2- | r1 == 0 = r2- | r2 == 0 = n1-r1- | otherwise = n1-r1+r2- in ((s',r',n1,p1,Inc),tau')- ) (getLmadDims lmad_base') $- permuteInv perm_base srnps,- -- Make resulting lmads:- tau_base' <- getOffset lmad_base' + sum taus_contrib,- lmad_base'' <- Lmad tau_base' srnps_base,- -- Put the repeat back on top of the result- newbase' <- IxFun (lmad_base'':lmads_base) shp_base cg_base,- (reps, rep) <- repeats,- IxFun lmads_base'' _ _ <- repeat newbase' reps rep,- lmads' <- take (length lmads - 1) lmads ++ lmads_base''- = IxFun lmads' shp_base (cg && cg_base)---- General case: just concatenate Lmads since this--- refers to index-function composition -- always safe!- | base ixfn == shape newbase =- IxFun (lmads ++ lmad_base:lmads_base) shp_base (cg && cg_base)-- | otherwise =- let IxFun lmads' shp_base' _ = reshape newbase $ map DimCoercion shp- in IxFun (lmads ++ lmads') shp_base' (cg && cg_base)--getMonotonicity :: (Eq num, IntegralExp num) => IxFun num -> DimInfo-getMonotonicity = getMonotonicityRots False---- | results in the index function corresponding to indexing--- with `i` on the outermost dimension.-offsetIndex :: (Eq num, IntegralExp num) =>- IxFun num -> num -> IxFun num-offsetIndex ixfun i | i == 0 = ixfun-offsetIndex ixfun i =- case shape ixfun of- d:ds -> slice ixfun (DimSlice i (d-i) 1 : map (unitSlice 0) ds)- [] -> error "offsetIndex: underlying index function has rank zero"---- | results in the index function corresponding to making--- the outermost dimension strided by `s`.-strideIndex :: (Eq num, IntegralExp num) =>- IxFun num -> num -> IxFun num-strideIndex ixfun s =- case shape ixfun of- d:ds -> slice ixfun (DimSlice 0 d s : map (unitSlice 0) ds)- [] -> error "offsetIndex: underlying index function has rank zero"----- | If the memory support of the index function is contiguous--- and row-major (i.e., no transpositions, repetitions,--- rotates, etc.), then this should return the offset from--- which the memory-support of this index function starts.-linearWithOffset :: (Eq num, IntegralExp num) =>- IxFun num -> num -> Maybe num-linearWithOffset (IxFun [] _ _) _ =- error "linearWithOffset: empty index function"-linearWithOffset ixfn@(IxFun [lmad] _ cg) elem_size- | mon <- getMonotonicity ixfn,- perm <- getPermutation lmad,- cg && mon == Inc,- all (\(s,_,_,_,_) -> s /= 0) (getLmadDims lmad),- perm == [0..length perm - 1],- off <- getOffset lmad = return $ off * elem_size- | otherwise = Nothing-linearWithOffset _ _ = Nothing---- | Similar restrictions to `linearWithOffset` except--- for transpositions, which are returned together--- with the offset.-rearrangeWithOffset :: (Eq num, IntegralExp num) =>- IxFun num -> num -> Maybe (num, [(Int,num)])-rearrangeWithOffset (IxFun [] _ _) _ =- error "rearrangeWithOffset: empty index function"-rearrangeWithOffset ixfn@(IxFun [lmad] _ cg) elem_size- | perm <- getPermutation lmad,- mon <- getMonotonicity ixfn,- cg && mon == Inc,- all (\(s,_,_,_,_) -> s /= 0) (getLmadDims lmad),- perm /= [0..length perm - 1],- offset <- getOffset lmad * elem_size =- return (offset, zip perm $ rearrangeShape perm $ shape ixfn)- | otherwise = Nothing-rearrangeWithOffset _ _ = Nothing--isLinear :: (Eq num, IntegralExp num) => IxFun num -> Bool-isLinear =- (==Just 0) . flip linearWithOffset 1------------------------------ Helper functions ------------------------------invertInfo :: DimInfo -> DimInfo-invertInfo Inc = Dec-invertInfo Dec = Inc-invertInfo Unknown = Unknown--getOffset :: Lmad num -> num-getOffset (Lmad tau _) = tau--getPermutation :: Lmad num -> Permutation-getPermutation (Lmad _ srns) = map (\(_,_,_,p,_) -> p) srns--getLmadDims :: Lmad num -> [(num,num,num,Int,DimInfo)]-getLmadDims (Lmad _ srnps) = srnps--setPermutation :: Permutation -> Lmad num -> Lmad num-setPermutation perm (Lmad tau srnps) =- Lmad tau $ zipWith (\(s,r,n,_,i) p -> (s,r,n,p,i)) srnps perm----setOffset :: num -> Lmad num -> Lmad num---setOffset tau (Lmad _ srnps) = Lmad tau srnps---- | Given an input lmad, this function computes a repetition `r`--- and a new lmad `res`, such that `repeat r res` is identical--- to the input lmad`.-shaveoffRepeats :: (Eq num, IntegralExp num) => Lmad num ->- (([Shape num], Shape num), Lmad num)-shaveoffRepeats lmad =- let perm = getPermutation lmad- srnps = getLmadDims lmad- -- compute the Repeat:- resacc= foldl (\acc (s,_,n,_,_) ->- case acc of- rpt:acc0 ->- if s == 0 then (n:rpt) : acc0- else [] : (rpt:acc0)- _ -> error "shaveoffRepeats: empty accum!"- ) [[]] $ L.reverse $ permuteFwd perm srnps- last_shape = last resacc- shapes = take (length resacc - 1) resacc- -- update permutation and lmad:- howManyRepLT k =- foldl (\i (s,_,_,p,_) ->- if s == 0 && p < k then i + 1 else i- ) 0 srnps- srnps' = foldl (\acc (s,r,n,p,info) ->- if s == 0 then acc- else let p' = p - howManyRepLT p- in (s,r,n,p',info):acc- ) [] $ L.reverse srnps- lmad' = Lmad (getOffset lmad) srnps'- in ((shapes,last_shape), lmad')--permuteFwd :: Permutation -> [a] -> [a]-permuteFwd [] _ = []-permuteFwd (p:ps) ds = (ds !! p) : permuteFwd ps ds--permuteInv :: Permutation -> [a] -> [a]-permuteInv ps elems = map snd $ L.sortBy sortGT $ zip ps elems--sortGT :: Ord a => (a, b1) -> (a, b2) -> Ordering-sortGT (a1, _) (a2, _)- | a1 > a2 = GT- | a1 < a2 = LT- | otherwise = GT--flatOneDim :: (Eq num, IntegralExp num) =>- (num, num, num) -> num -> num-flatOneDim (s,r,n) i- | s == 0 = 0- | r == 0 = i*s- | otherwise = ((i+r) `mod` n) * s--makeRotIota :: (IntegralExp num) =>- DimInfo -> num -> [(num,num)] -> Lmad num-makeRotIota info tau support- | info == Inc || info == Dec =- let rk = length support- (rs,ns) = unzip support- ss0= L.reverse $ take rk $ scanl (*) 1 $ L.reverse ns- ss = if info == Inc then ss0- else map (*(-1)) ss0- ps = map fromIntegral [0..rk-1]- fi = replicate rk info- in Lmad tau $ zip5 ss rs ns ps fi- | otherwise = error "makeRotIota requires Inc or Dec!"--getMonotonicityRots :: (Eq num, IntegralExp num) => Bool -> IxFun num -> DimInfo-getMonotonicityRots _ (IxFun [] _ _) =- error "getMonotonicityRots: empty index function"-getMonotonicityRots ignore_rots (IxFun (lmad:lmads) _ _) =- let mon1 = getLmadMonotonicity ignore_rots lmad- in if all (==mon1) $ map (getLmadMonotonicity ignore_rots) lmads- then mon1 else Unknown--getLmadMonotonicity :: (Eq num, IntegralExp num) => Bool -> Lmad num -> DimInfo-getLmadMonotonicity ignore_rots (Lmad _ dims)- | all (isMonDim ignore_rots Inc) dims = Inc- | all (isMonDim ignore_rots Dec) dims = Dec- | otherwise = Unknown--isMonDim :: (Eq num, IntegralExp num) => Bool -> DimInfo ->- (num, num, num, Int, DimInfo) -> Bool-isMonDim ignore_rots mon (s,r,_,_,info) =- s == 0 || ((ignore_rots || r == 0) && mon == info)
src/Futhark/Representation/Kernels/Kernel.hs view
@@ -11,6 +11,7 @@ ( Kernel(..) , kernelType , KernelDebugHints(..)+ , GenReduceOp(..) , KernelBody(..) , KernelSpace(..) , spaceDimensions@@ -35,6 +36,7 @@ ) where +import Control.Arrow (first) import Control.Monad.Writer hiding (mapM_) import Control.Monad.Identity hiding (mapM_) import qualified Data.Set as S@@ -79,6 +81,20 @@ } deriving (Eq, Show, Ord) +data GenReduceOp lore =+ GenReduceOp { genReduceWidth :: SubExp+ , genReduceDest :: [VName]+ , genReduceNeutral :: [SubExp]+ , genReduceShape :: Shape+ -- ^ In case this operator is semantically a+ -- vectorised operator (corresponding to a perfect map+ -- nest in the SOACS representation), these are the+ -- logical "dimensions". This is used to generate+ -- more efficient code.+ , genReduceOp :: LambdaT lore+ }+ deriving (Eq, Ord, Show)+ data Kernel lore = GetSize Name SizeClass -- ^ Produce some runtime-configurable size. | GetSizeMax SizeClass -- ^ The maximum size of some class.@@ -88,6 +104,7 @@ | SegRed KernelSpace Commutativity (Lambda lore) [SubExp] [Type] (Body lore) -- ^ The KernelSpace must always have at least two dimensions, -- implying that the result of a SegRed is always an array.+ | SegGenRed KernelSpace [GenReduceOp lore] [Type] (Body lore) deriving (Eq, Show, Ord) data KernelSpace = KernelSpace { spaceGlobalId :: VName@@ -200,6 +217,18 @@ <*> mapM (mapOnKernelSubExp tv) nes <*> mapM (mapOnType $ mapOnKernelSubExp tv) ts <*> mapOnKernelBody tv lam+mapKernelM tv (SegGenRed space ops ts body) =+ SegGenRed+ <$> mapOnKernelSpace tv space+ <*> mapM onGenRedOp ops+ <*> mapM (mapOnType $ mapOnKernelSubExp tv) ts+ <*> mapOnKernelBody tv body+ where onGenRedOp (GenReduceOp w arrs nes shape op) =+ GenReduceOp <$> mapOnKernelSubExp tv w+ <*> mapM (mapOnKernelVName tv) arrs+ <*> mapM (mapOnKernelSubExp tv) nes+ <*> (Shape <$> mapM (mapOnKernelSubExp tv) (shapeDims shape))+ <*> mapOnKernelLambda tv op mapKernelM tv (Kernel desc space ts kernel_body) = Kernel <$> mapOnKernelDebugHints desc <*> mapOnKernelSpace tv space <*>@@ -421,6 +450,13 @@ dims = map snd $ spaceDimensions space outer_dims = init dims +kernelType (SegGenRed space ops _ _) = do+ op <- ops+ let shape = Shape (segment_dims <> [genReduceWidth op]) <> genReduceShape op+ map (`arrayOfShape` shape) (lambdaReturnType $ genReduceOp op)+ where dims = map snd $ spaceDimensions space+ segment_dims = init dims+ kernelType GetSize{} = [Prim int32] kernelType GetSizeMax{} = [Prim int32] kernelType CmpSizeLe{} = [Prim Bool]@@ -442,6 +478,9 @@ mconcat (map consumedByReturn (kernelBodyResult kbody)) where consumedByReturn (WriteReturn _ a _) = S.singleton a consumedByReturn _ = mempty+ consumedInOp (SegGenRed _ ops _ body) =+ S.fromList (concatMap genReduceDest ops) <>+ consumedInBody body consumedInOp _ = mempty aliasAnalyseKernelBody :: (Attributes lore,@@ -567,6 +606,9 @@ mconcat $ map UT.consumedUsage $ S.toList $ consumedInKernelBody kbody usageInOp (SegRed _ _ _ _ _ body) = mconcat $ map UT.consumedUsage $ S.toList $ consumedInBody body+ usageInOp (SegGenRed _ ops _ body) =+ mconcat $ map UT.consumedUsage $ S.toList (consumedInBody body) <>+ concatMap genReduceDest ops usageInOp GetSize{} = mempty usageInOp GetSizeMax{} = mempty usageInOp CmpSizeLe{} = mempty@@ -598,6 +640,44 @@ TC.checkLambdaBody ts body +typeCheckKernel (SegGenRed space ops ts body) = do+ checkSpace space+ mapM_ TC.checkType ts++ TC.binding (scopeOfKernelSpace space) $ do+ forM_ ops $ \(GenReduceOp dest_w dests nes shape op) -> do+ TC.require [Prim int32] dest_w+ nes' <- mapM TC.checkArg nes+ mapM_ (TC.require [Prim int32]) $ shapeDims shape++ -- Operator type must match the type of neutral elements.+ let stripVecDims = stripArray $ shapeRank shape+ TC.checkLambda op $ map (TC.noArgAliases .first stripVecDims) $ nes' ++ nes'+ let nes_t = map TC.argType nes'+ unless (nes_t == map (`arrayOfShape` shape) (lambdaReturnType op)) $+ TC.bad $ TC.TypeError $ "SegGenRed operator has return type " +++ prettyTuple (lambdaReturnType op) ++ " but neutral element has type " +++ prettyTuple nes_t++ -- Arrays must have proper type.+ let dest_shape = Shape $ segment_dims <> [dest_w]+ forM_ (zip nes_t dests) $ \(t, dest) -> do+ TC.requireI [t `arrayOfShape` dest_shape] dest+ TC.consume =<< TC.lookupAliases dest++ TC.checkLambdaBody ts body++ -- Return type of bucket function must be an index for each+ -- operation followed by the values to write.+ nes_ts <- concat <$> mapM (mapM subExpType . genReduceNeutral) ops+ let bucket_ret_t = replicate (length ops) (Prim int32) ++ nes_ts+ unless (bucket_ret_t == ts) $+ TC.bad $ TC.TypeError $ "SegGenRed body has return type " +++ prettyTuple ts ++ " but should have type " +++ prettyTuple bucket_ret_t++ where segment_dims = init $ map snd $ spaceDimensions space+ typeCheckKernel (Kernel _ space kts kbody) = do checkSpace space mapM_ TC.checkType kts@@ -664,6 +744,9 @@ kernelBodyMetrics = mapM_ bindingMetrics . kernelBodyStms opMetrics (SegRed _ _ red_op _ _ body) = inside "SegRed" $ lambdaMetrics red_op >> bodyMetrics body+ opMetrics (SegGenRed _ ops _ body) =+ inside "SegGenRed" $ do mapM_ (lambdaMetrics . genReduceOp) ops+ bodyMetrics body opMetrics GetSize{} = seen "GetSize" opMetrics GetSizeMax{} = seen "GetSizeMax" opMetrics CmpSizeLe{} = seen "CmpSizeLe"@@ -691,6 +774,18 @@ PP.nestedBlock "{" "}" (ppr body) where name = case comm of Commutative -> "segred_comm" Noncommutative -> "segred"++ ppr (SegGenRed space ops ts body) =+ text "seggenred" <>+ PP.parens (PP.braces (mconcat $ intersperse (PP.comma <> PP.line) $ map ppOp ops)) </>+ PP.align (ppr space) <+> PP.colon <+> ppTuple' ts <+>+ PP.nestedBlock "{" "}" (ppr body)+ where ppOp (GenReduceOp w dests nes shape op) =+ ppr w <> PP.comma </>+ PP.braces (PP.commasep $ map ppr dests) <> PP.comma </>+ PP.braces (PP.commasep $ map ppr nes) <> PP.comma </>+ ppr shape <> PP.comma </>+ ppr op instance Pretty KernelSpace where ppr (KernelSpace f_gtid f_ltid gid num_threads num_groups group_size structure) =
src/Futhark/Representation/Kernels/Simplify.hs view
@@ -92,8 +92,64 @@ Engine.simplifyLambda red_op $ replicate (length nes * 2) Nothing red_op_hoisted' <- mapM processHoistedStm red_op_hoisted + (body', body_hoisted) <- hoistFromBody space' (mk_ops space') env ts body++ return (SegRed space' comm red_op' nes' ts' body',+ red_op_hoisted' <> body_hoisted)++ where scope_vtable = ST.fromScope scope+ scope = scopeOfKernelSpace space++simplifyKernelOp mk_ops env (SegGenRed space ops ts body) = do+ outer_vtable <- Engine.askVtable++ space' <- Engine.simplify space+ ts' <- mapM Engine.simplify ts++ (ops', ops_hoisted) <- fmap unzip $ forM ops $+ \(GenReduceOp w arrs nes dims lam) -> do+ w' <- Engine.simplify w+ arrs' <- Engine.simplify arrs+ nes' <- Engine.simplify nes+ dims' <- Engine.simplify dims+ (lam', op_hoisted) <-+ Engine.subSimpleM (mk_ops space) env outer_vtable $+ Engine.localVtable (<>scope_vtable) $+ Engine.simplifyLambda lam $+ replicate (length nes * 2) Nothing+ return (GenReduceOp w' arrs' nes' dims' lam',+ op_hoisted)++ red_op_hoisted' <- mapM processHoistedStm $ mconcat ops_hoisted++ (body', body_hoisted) <- hoistFromBody space' (mk_ops space') env ts body++ return (SegGenRed space' ops' ts' body',+ red_op_hoisted' <> body_hoisted)++ where scope_vtable = ST.fromScope scope+ scope = scopeOfKernelSpace space++simplifyKernelOp _ _ (GetSize key size_class) = return (GetSize key size_class, mempty)+simplifyKernelOp _ _ (GetSizeMax size_class) = return (GetSizeMax size_class, mempty)+simplifyKernelOp _ _ (CmpSizeLe key size_class x) = do+ x' <- Engine.simplify x+ return (CmpSizeLe key size_class x', mempty)++hoistFromBody :: (Engine.SimplifiableLore lore,+ SameScope lore outerlore,+ BodyAttr outerlore ~ (), BodyAttr lore ~ (),+ ExpAttr lore ~ ExpAttr outerlore,+ RetType lore ~ RetType outerlore,+ BranchType lore ~ BranchType outerlore) =>+ KernelSpace -> Simplify.SimpleOps lore -> Engine.Env lore+ -> [Type] -> Body lore+ -> Engine.SimpleM outerlore (Body (Wise lore), Stms (Wise outerlore))+hoistFromBody kspace ops env ts body = do+ outer_vtable <- Engine.askVtable+ ((body_stms, body_res), body_hoisted) <-- Engine.subSimpleM (mk_ops space) env outer_vtable $ do+ Engine.subSimpleM ops env outer_vtable $ do par_blocker <- Engine.asksEngineEnv $ Engine.blockHoistPar . Engine.envHoistBlockers Engine.localVtable (<>scope_vtable) $ Engine.blockIf (Engine.hasFree bound_here@@ -101,21 +157,15 @@ `Engine.orIf` par_blocker `Engine.orIf` Engine.isConsumed) $ Engine.simplifyBody (replicate (length ts) Observe) body+ body_hoisted' <- mapM processHoistedStm body_hoisted - return (SegRed space' comm red_op' nes' ts' $- mkWiseBody () body_stms body_res,- red_op_hoisted' <> body_hoisted')+ return (mkWiseBody () body_stms body_res,+ body_hoisted') where scope_vtable = ST.fromScope scope- scope = scopeOfKernelSpace space+ scope = scopeOfKernelSpace kspace bound_here = S.fromList $ M.keys scope--simplifyKernelOp _ _ (GetSize key size_class) = return (GetSize key size_class, mempty)-simplifyKernelOp _ _ (GetSizeMax size_class) = return (GetSizeMax size_class, mempty)-simplifyKernelOp _ _ (CmpSizeLe key size_class x) = do- x' <- Engine.simplify x- return (CmpSizeLe key size_class x', mempty) processHoistedStm :: (Monad m, PrettyLore from,
src/Futhark/Test.hs view
@@ -17,6 +17,7 @@ , compileProgram , runProgram , ensureReferenceOutput+ , determineTuning , Mismatch , ProgramTest (..)@@ -184,6 +185,10 @@ lexstr :: T.Text -> Parser () lexstr = void . try . lexeme . string +-- | Like 'lexstr', but does not consume trailing linebreaks.+lexstr' :: T.Text -> Parser ()+lexstr' = void . try . lexeme' . string+ braces :: Parser a -> Parser a braces p = lexstr "{" *> p <* lexstr "}" @@ -210,7 +215,7 @@ tagConstituent c = isAlphaNum c || c == '_' || c == '-' parseAction :: Parser TestAction-parseAction = CompileTimeFailure <$> (lexstr "error:" *> parseExpectedError) <|>+parseAction = CompileTimeFailure <$> (lexstr' "error:" *> parseExpectedError) <|> (RunCases <$> parseInputOutputs <*> many parseExpectedStructure <*> many parseWarning) @@ -232,7 +237,7 @@ return s parseRunCases :: Parser [TestRun]-parseRunCases = parseRunCases' (0::Int)+parseRunCases = parseRunCases' (1::Int) where parseRunCases' i = (:) <$> parseRunCase i <*> parseRunCases' (i+1) <|> pure [] parseRunCase i = do@@ -509,12 +514,12 @@ genValues :: [GenValue] -> IO SBS.ByteString genValues gens = do- (code, stdout, stderr) <- readProcessWithExitCode "futhark-dataset" args mempty+ (code, stdout, stderr) <- readProcessWithExitCode "futhark" ("dataset":args) mempty case code of ExitSuccess -> return stdout ExitFailure e ->- fail $ "futhark-dataset failed with exit code " ++ show e ++ " and stderr:\n" +++ fail $ "'futhark dataset' failed with exit code " ++ show e ++ " and stderr:\n" ++ map (chr . fromIntegral) (SBS.unpack stderr) where args = "-b" : concatMap argForGen gens argForGen g = ["-g", genValueType g]@@ -613,8 +618,10 @@ fail $ "Reference dataset generation failed with exit code " ++ show e ++ " and stderr:\n" ++ map (chr . fromIntegral) (SBS.unpack stderr)- ExitSuccess ->- SBS.writeFile (file (entry, tr)) stdout+ ExitSuccess -> do+ let f = file (entry, tr)+ liftIO $ createDirectoryIfMissing True $ takeDirectory f+ SBS.writeFile f stdout where file (entry, tr) = takeDirectory prog </> testRunReferenceOutput prog entry tr @@ -625,3 +632,15 @@ liftIO . fmap not . doesFileExist . file $ (entry, tr) | otherwise = return False++-- | Determine the --tuning options to pass to the program. The first+-- argument is the extension of the tuning file, or 'Nothing' if none+-- should be used.+determineTuning :: MonadIO m => Maybe FilePath -> FilePath -> m ([String], String)+determineTuning Nothing _ = return ([], mempty)+determineTuning (Just ext) program = do+ exists <- liftIO $ doesFileExist (program <.> ext)+ if exists+ then return (["--tuning", program <.> ext],+ " (using " <> takeFileName (program <.> ext) <> ")")+ else return ([], mempty)
src/Futhark/Transform/FirstOrderTransform.hs view
@@ -25,6 +25,7 @@ import Control.Monad.State import qualified Data.Map.Strict as M import qualified Data.Set as S+import Data.List (zip4) import qualified Futhark.Representation.AST as AST import Futhark.Representation.SOACS@@ -221,22 +222,34 @@ hists_out' = chunks (map (length . lambdaReturnType . genReduceOp) ops) $ map identName hists_out - -- Read values from histograms.- h_vals <- forM (zip inds hists_out') $ \(idx, hist) ->- forM hist $ \arr -> do- arr_t <- lookupType arr- letSubExp "read_hist" $ BasicOp $ Index arr $ fullSlice arr_t [DimFix idx]+ hists_out'' <- forM (zip4 hists_out' ops inds vals) $ \(hist, op, idx, val) -> do+ -- Check whether the indexes are in-bound. If they are not, we+ -- return the histograms unchanged.+ let outside_bounds_branch = insertStmsM $ resultBodyM $ map Var hist+ oob = case hist of [] -> eSubExp $ constant True+ arr:_ -> eOutOfBounds arr [eSubExp idx] - -- Apply operators.- h_vals' <- forM (zip3 (map genReduceOp ops) vals h_vals) $ \(op, ne_val, h_val) ->- bindLambda op $ map (BasicOp . SubExp) $ ne_val ++ h_val+ letTupExp "new_histo" <=<+ eIf oob outside_bounds_branch $ do+ -- Read values from histogram.+ h_val <- forM hist $ \arr -> do+ arr_t <- lookupType arr+ letSubExp "read_hist" $ BasicOp $ Index arr $ fullSlice arr_t [DimFix idx] - -- Write values back to histograms.- ress <- forM (zip3 inds h_vals' hists_out') $ \(idx, val, hist) ->- forM (zip val hist) $ \(v, arr) ->- letExp "write_hist" =<< eWriteArray arr [eSubExp idx] (eSubExp v)+ -- Apply operator.+ h_val' <- bindLambda (genReduceOp op) $+ map (BasicOp . SubExp) $ h_val ++ val - return $ resultBody $ map Var $ concat ress+ -- Write values back to histograms.+ hist' <- forM (zip hist h_val') $ \(arr, v) -> do+ arr_t <- lookupType arr+ letInPlace "hist_out" arr (fullSlice arr_t [DimFix idx]) $+ BasicOp $ SubExp v++ return $ resultBody $ map Var hist'++ return $ resultBody $ map Var $ concat hists_out''+ -- Wrap up the above into a for-loop. letBind_ pat $ DoLoop [] merge (ForLoop iter Int32 len []) loopBody
src/Futhark/Version.hs view
@@ -20,11 +20,16 @@ -- | The version of Futhark that we are using, as a 'String' versionString :: String-versionString = showVersion version ++ "\n" ++ gitversion+versionString = showVersion version +++ if used_hash /= "UNKNOWN"+ then "\n" ++ gitversion+ else "" where+ used_hash = take 7 $(gitHash)+ gitversion = concat ["git: " , branch- , take 7 $(gitHash)+ , used_hash , " (", $(gitCommitDate), ")" , dirty ]
src/Language/Futhark/Attributes.hs view
@@ -456,9 +456,6 @@ typeOf (Index _ _ (Info t) _) = t typeOf (Update e _ _ _) = typeOf e `setAliases` mempty typeOf (RecordUpdate _ _ _ (Info t) _) = removeShapeAnnotations t-typeOf (Zip _ _ _ (Info t) _) = t-typeOf (Unzip _ ts _) =- tupleRecord $ map unInfo ts typeOf (Unsafe e _) = typeOf e typeOf (Assert _ e _ _) = typeOf e typeOf (Map _ _ (Info t) _) = t `setUniqueness` Unique
src/Language/Futhark/Interpreter.hs view
@@ -205,24 +205,30 @@ data Env = Env { envTerm :: M.Map VName TermBinding , envType :: M.Map VName T.TypeBinding+ , envShapes :: M.Map VName Shape+ -- ^ A mapping from type parameters to the shapes of+ -- the value to which they were initially bound. } instance Monoid Env where- mempty = Env mempty mempty+ mempty = Env mempty mempty mempty instance Semigroup Env where- Env vm1 tm1 <> Env vm2 tm2 = Env (vm1 <> vm2) (tm1 <> tm2)+ Env vm1 tm1 sm1 <> Env vm2 tm2 sm2 =+ Env (vm1 <> vm2) (tm1 <> tm2) (sm1 <> sm2) newtype InterpreterError = InterpreterError String valEnv :: M.Map VName (Maybe T.BoundV, Value) -> Env valEnv m = Env { envTerm = M.map (uncurry TermValue) m , envType = mempty+ , envShapes = mempty } modEnv :: M.Map VName Module -> Env modEnv m = Env { envTerm = M.map TermModule m , envType = mempty+ , envShapes = mempty } instance Show InterpreterError where@@ -287,90 +293,92 @@ apply2 loc env f x y = stacking loc env $ do f' <- apply noLoc mempty f x apply noLoc mempty f' y -matchPattern :: Env -> Pattern -> Value- -> EvalM (M.Map VName (Maybe T.BoundV, Value))+matchPattern :: Env -> Pattern -> Value -> EvalM Env matchPattern env p v = do- m <- runMaybeT $ patternMatch env mempty p v+ m <- runMaybeT $ patternMatch env p v case m of Nothing -> error $ "matchPattern: missing case for " ++ pretty p ++ " and " ++ pretty v- Just binds -> return binds+ Just env' -> return env' -patternMatch :: Env -> M.Map VName (Maybe T.BoundV, Value)- -> Pattern -> Value- -> MaybeT EvalM (M.Map VName (Maybe T.BoundV, Value))-patternMatch _ m (Id v (Info t) _) val =- lift $ pure $ M.insert v (Just $ T.BoundV [] $ toStruct t, val) m-patternMatch _ m Wildcard{} _ =- lift $ pure m-patternMatch env m (TuplePattern ps _) (ValueRecord vs)+patternMatch :: Env -> Pattern -> Value -> MaybeT EvalM Env+patternMatch env (Id v (Info t) _) val =+ lift $ pure $ valEnv (M.singleton v (Just $ T.BoundV [] $ toStruct t, val)) <> env+patternMatch env Wildcard{} _ =+ lift $ pure env+patternMatch env (TuplePattern ps _) (ValueRecord vs) | length ps == length vs' =- foldM (\m' (p,v) -> patternMatch env m' p v) m $+ foldM (\env' (p,v) -> patternMatch env' p v) env $ zip ps (map snd $ sortFields vs) where vs' = sortFields vs-patternMatch env m (RecordPattern ps _) (ValueRecord vs)+patternMatch env (RecordPattern ps _) (ValueRecord vs) | length ps == length vs' =- foldM (\m' (p,v) -> patternMatch env m' p v) m $+ foldM (\env' (p,v) -> patternMatch env' p v) env $ zip (map snd $ sortFields $ M.fromList ps) (map snd $ sortFields vs) where vs' = sortFields vs-patternMatch env m (PatternParens p _) v = patternMatch env m p v-patternMatch env m (PatternAscription p td loc) v = do- t <- lift $ evalType env $ unInfo $ expandedType td- case matchValueToType env m t v of+patternMatch env (PatternParens p _) v = patternMatch env p v+patternMatch env (PatternAscription p td loc) v = do+ let t = evalType env $ unInfo $ expandedType td+ case matchValueToType env t v of Left err -> lift $ bad loc env err- Right m' -> patternMatch env m' p v-patternMatch env m (PatternLit e _ _) v = do+ Right env' -> patternMatch env' p v+patternMatch env (PatternLit e _ _) v = do v' <- lift $ eval env e if v == v'- then pure m+ then pure env else mzero -patternMatch _ _ _ _ = mzero+patternMatch _ _ _ = mzero -- | For matching size annotations (the actual type will have been -- verified by the type checker). It is assumed that previously -- unbound names are in binding position here.-matchValueToType :: Env -> M.Map VName (Maybe T.BoundV, Value)+matchValueToType :: Env -> StructType -> Value- -> Either String (M.Map VName (Maybe T.BoundV, Value))+ -> Either String Env -matchValueToType env m t@(Array _ _ _ (ShapeDecl ds@(d:_))) val@(ValueArray arr) =+matchValueToType env t@(TypeVar _ _ tn []) val+ | Just shape <- M.lookup (typeLeaf tn) $ envShapes env,+ shape /= valueShape val =+ Left $ "Value passed for type parameter `" <> prettyName (typeLeaf tn) <>+ "` does not match shape " <> pretty shape <>+ " of previously observed value."+ | Nothing <- M.lookup (typeLeaf tn) $ envShapes env =+ matchValueToType (tnenv <> env) t val+ where tnenv = Env mempty mempty $ M.singleton (typeLeaf tn) (valueShape val)++matchValueToType env t@(Array _ _ _ (ShapeDecl ds@(d:_))) val@(ValueArray arr) = case d of NamedDim v | Just x <- look v -> if x == arr_n- then continue m+ then continue env else emptyOrWrong $ "`" <> pretty v <> "` (" <> pretty x <> ")" | otherwise ->- continue $ M.insert (qualLeaf v)- (Just $ T.BoundV [] $ Prim $ Signed Int32,- ValuePrim $ SignedValue $ Int32Value arr_n)- m- AnyDim -> continue m+ continue $+ valEnv (M.singleton (qualLeaf v)+ (Just $ T.BoundV [] $ Prim $ Signed Int32,+ ValuePrim $ SignedValue $ Int32Value arr_n))+ <> env+ AnyDim -> continue env ConstDim x- | fromIntegral x == arr_n -> continue m+ | fromIntegral x == arr_n -> continue env | otherwise -> emptyOrWrong $ pretty x where arr_n = arrayLength arr look v | Just (TermValue _ (ValuePrim (SignedValue (Int32Value x)))) <- lookupVar v env = Just x- | Just (_, ValuePrim (SignedValue (Int32Value x))) <-- M.lookup (qualLeaf v) m = Just x | otherwise = Nothing - continue m' = case elems arr of- [] ->- -- We have to ensure that remaining unbound shape- -- parameters become zeroes.- return $ m' <> mconcat (map namedAreZero ds)- v:_ ->- matchValueToType env m' (stripArray 1 t) v+ continue env' = case elems arr of+ [] -> return env'+ v:_ -> matchValueToType env' (stripArray 1 t) v -- Empty arrays always match if nothing else does. emptyOrWrong x | any zeroDim ds, emptyShape (valueShape val) =- Right $ m <> mconcat (map namedAreZero ds)+ Right env | otherwise = wrong x wrong x = Left $ "Size annotation " <> x <>@@ -380,20 +388,17 @@ zeroDim AnyDim = True zeroDim (ConstDim x) = x == 0 - namedAreZero (NamedDim v)- | isNothing $ look v =- M.singleton (qualLeaf v) (Just $ T.BoundV [] $ Prim $ Signed Int32,- ValuePrim $ SignedValue $ Int32Value 0)- | otherwise =- mempty- namedAreZero _ = mempty--matchValueToType env m (Record fs) (ValueRecord arr) =- foldM (\m' (t, v) -> matchValueToType env m' t v) m $+matchValueToType env (Record fs) (ValueRecord arr) =+ foldM (\env' (t, v) -> matchValueToType env' t v) env $ M.intersectionWith (,) fs arr -matchValueToType _ m _ _ = return m+matchValueToType env _ _ = return env +bindToZero :: [VName] -> Env+bindToZero = valEnv . M.fromList . map f+ where f v = (v, (Just $ T.BoundV [] $ Prim $ Signed Int32,+ ValuePrim $ SignedValue $ Int32Value 0))+ data Indexing = IndexingFix Int32 | IndexingSlice (Maybe Int32) (Maybe Int32) (Maybe Int32) @@ -442,7 +447,8 @@ -- | 'signum', but with 0 as 1. signum' :: (Eq p, Num p) => p -> p-signum' x = if x == 0 then 1 else signum x+signum' 0 = 1+signum' x = signum x indexArray :: [Indexing] -> Value -> Maybe Value indexArray (IndexingFix i:is) (ValueArray arr)@@ -497,41 +503,41 @@ -- | Expand type based on information that was not available at -- type-checking time (the structure of abstract types).-evalType :: Env -> StructType -> EvalM StructType-evalType _ (Prim pt) = return $ Prim pt-evalType env (Record fs) = Record <$> traverse (evalType env) fs+evalType :: Env -> StructType -> StructType+evalType _ (Prim pt) = Prim pt+evalType env (Record fs) = Record $ fmap (evalType env) fs evalType env (Arrow () p t1 t2) =- Arrow () p <$> evalType env t1 <*> evalType env t2-evalType env t@(Array _ u _ shape) = do+ Arrow () p (evalType env t1) (evalType env t2)+evalType env t@(Array _ u _ shape) = let et = stripArray (shapeRank shape) t- et' <- evalType env et- shape' <- traverse evalDim shape- return $- fromMaybe (error "Cannot construct array after substitution") $- arrayOf et' shape' u+ et' = evalType env et+ shape' = fmap evalDim shape+ in fromMaybe (error "Cannot construct array after substitution") $+ arrayOf et' shape' u where evalDim (NamedDim qn) | Just (TermValue _ (ValuePrim (SignedValue (Int32Value x)))) <- lookupVar qn env =- return $ ConstDim $ fromIntegral x- evalDim d = return d+ ConstDim $ fromIntegral x+ evalDim d = d evalType env t@(TypeVar () _ tn args) = case lookupType (qualNameFromTypeName tn) env of- Just (T.TypeAbbr _ ps t') -> do- (substs, types) <- mconcat <$> zipWithM matchPtoA ps args- let onDim (NamedDim v) = fromMaybe (NamedDim v) $ M.lookup (qualLeaf v) substs+ Just (T.TypeAbbr _ ps t') ->+ let (substs, types) = mconcat $ zipWith matchPtoA ps args+ onDim (NamedDim v) = fromMaybe (NamedDim v) $ M.lookup (qualLeaf v) substs onDim d = d- if null ps then return $ bimap onDim id t'- else evalType (Env mempty types <> env) $ bimap onDim id t'- Nothing -> return t+ in if null ps then bimap onDim id t'+ else evalType (Env mempty types mempty <> env) $ bimap onDim id t'+ Nothing -> t+ where matchPtoA (TypeParamDim p _) (TypeArgDim (NamedDim qv) _) =- return (M.singleton p $ NamedDim qv, mempty)+ (M.singleton p $ NamedDim qv, mempty) matchPtoA (TypeParamDim p _) (TypeArgDim (ConstDim k) _) =- return (M.singleton p $ ConstDim k, mempty)- matchPtoA (TypeParamType l p _) (TypeArgType t' _) = do- t'' <- evalType env t'- return (mempty, M.singleton p $ T.TypeAbbr l [] t'')- matchPtoA _ _ = return mempty-evalType _ (Enum cs) = return $ Enum cs+ (M.singleton p $ ConstDim k, mempty)+ matchPtoA (TypeParamType l p _) (TypeArgType t' _) =+ let t'' = evalType env t'+ in (mempty, M.singleton p $ T.TypeAbbr l [] t'')+ matchPtoA _ _ = mempty+evalType _ (Enum cs) = Enum cs eval :: Env -> Exp -> EvalM Value @@ -580,16 +586,16 @@ eval env (Ascript e td loc) = do v <- eval env e- t <- evalType env $ unInfo $ expandedType td- case matchValueToType env mempty t v of+ let t = evalType env $ unInfo $ expandedType td+ case matchValueToType env t v of Right _ -> return v Left err -> bad loc env $ "Value `" <> pretty v <> "` cannot match shape of type `" <> pretty (declaredType td) <> "` (`" <> pretty t <> "`): " ++ err eval env (LetPat _ p e body _) = do v <- eval env e- p_env <- valEnv <$> matchPattern env p v- eval (p_env <> env) body+ env' <- matchPattern env p v+ eval env' body eval env (LetFun f (tparams, pats, _, Info ret, fbody) body loc) = do v <- eval env $ Lambda tparams pats fbody Nothing (Info (mempty, ret)) loc@@ -684,25 +690,31 @@ -- that takes an empty tuple '()' as argument! Zero-parameter lambdas -- can never occur in a well-formed Futhark program, but they are -- convenient in the interpreter.-eval env (Lambda _ [] body _ (Info (_, t)) loc) = do- v <- eval env body+eval env (Lambda tparams [] body _ (Info (_, t)) loc) = do+ -- All remaining size parameters that have not yet been assigned a+ -- value (because they were inner dimensions of empty arrays) are+ -- now assigned a zero.+ let unbound_dims = bindToZero $ map typeParamName $ filter isDimParam tparams+ v <- eval (env <> unbound_dims) body case (t, v) of (Arrow _ _ _ rt, ValueFun f) -> return $ ValueFun $ \arg -> do r <- f arg- rt' <- evalType env rt- match rt' r+ match (evalType env rt) r _ -> match t v where match vt v =- case matchValueToType env mempty vt v of+ case matchValueToType env vt v of Right _ -> return v Left err -> bad loc env $ "Value `" <> pretty v <> "` cannot match type `" <> pretty vt <> "`: " ++ err + isDimParam TypeParamDim{} = True+ isDimParam _ = False+ eval env (Lambda tparams (p:ps) body mrd (Info (als, ret)) loc) = return $ ValueFun $ \v -> do- p_env <- valEnv <$> matchPattern env p v- eval (p_env <> env) $ Lambda tparams ps body mrd (Info (als, ret)) loc+ env' <- matchPattern env p v+ eval env' $ Lambda tparams ps body mrd (Info (als, ret)) loc eval env (OpSection qv _ _) = evalTermVar env qv @@ -733,7 +745,7 @@ foldM (forInLoop in_pat) init_v in_vs While cond -> whileLoop cond init_v- where withLoopParams v = (<>env) . valEnv <$> matchPattern env pat v+ where withLoopParams = matchPattern env pat inc = (`P.doAdd` Int64Value 1) zero = (`P.doMul` Int64Value 0)@@ -755,8 +767,8 @@ forInLoop in_pat v in_v = do env' <- withLoopParams v- pat_env <- matchPattern env' in_pat in_v- eval (valEnv pat_env <> env') body+ env'' <- matchPattern env' in_pat in_v+ eval env'' body eval env (Project f e _ _) = do v <- eval env e@@ -789,8 +801,8 @@ evalCase :: Value -> Env -> CaseBase Info VName -> EvalM (Maybe Value) evalCase v env (CasePat p cExp _) = runMaybeT $ do- pEnv <- valEnv <$> patternMatch env mempty p v- lift $ eval (pEnv <> env) cExp+ env' <- patternMatch env p v+ lift $ eval env' cExp substituteInModule :: M.Map VName VName -> Module -> Module substituteInModule substs = onModule@@ -801,8 +813,8 @@ replaceM f m = M.fromList $ do (k, v) <- M.toList m return (replace k, f v)- onModule (Module (Env terms types)) =- Module $ Env (replaceM onTerm terms) (replaceM onType types)+ onModule (Module (Env terms types _)) =+ Module $ Env (replaceM onTerm terms) (replaceM onType types) mempty onModule (ModuleFun f) = ModuleFun $ \m -> onModule <$> f (substituteInModule rev_substs m) onTerm (TermValue t v) = TermValue t v@@ -829,10 +841,11 @@ Just m -> return $ Module m evalModExp env (ModDecs ds _) = do- Env terms types <- foldM evalDec env ds+ Env terms types _ <- foldM evalDec env ds -- Remove everything that was present in the original Env. return $ Module $ Env (terms `M.difference` envTerm env) (types `M.difference` envType env)+ mempty evalModExp env (ModVar qv _) = evalModuleVar env qv@@ -857,8 +870,8 @@ evalDec :: Env -> Dec -> EvalM Env evalDec env (ValDec (ValBind _ v _ (Info t) tps ps def _ loc)) = do- t' <- evalType env t- let ftype = T.BoundV tps $ foldr (uncurry (Arrow ()) . patternParam) t' ps+ let t' = evalType env t+ ftype = T.BoundV tps $ foldr (uncurry (Arrow ()) . patternParam) t' ps val <- eval env $ Lambda tps ps def Nothing (Info (mempty, t')) loc return $ valEnv (M.singleton v (Just ftype, val)) <> env @@ -872,8 +885,8 @@ evalDec env (LocalDec d _) = evalDec env d evalDec env SigDec{} = return env evalDec env (TypeDec (TypeBind v ps t _ _)) = do- t' <- evalType env $ unInfo $ expandedType t- let abbr = T.TypeAbbr Lifted ps t'+ let abbr = T.TypeAbbr Lifted ps $+ evalType env $ unInfo $ expandedType t return env { envType = M.insert v abbr $ envType env } evalDec env (ModDec (ModBind v ps ret body _ loc)) = do mod <- evalModExp env $ wrapInLambda ps@@ -892,8 +905,8 @@ initialCtx :: Ctx initialCtx = Ctx (Env (M.insert (VName (nameFromString "intrinsics") 0)- (TermModule (Module $ Env terms types)) terms)- types)+ (TermModule (Module $ Env terms types mempty)) terms)+ types mempty) mempty where terms = M.mapMaybeWithKey (const . def . baseString) intrinsics
src/Language/Futhark/Parser/Parser.y view
@@ -25,6 +25,7 @@ import Control.Arrow import Data.Array import qualified Data.Text as T+import Codec.Binary.UTF8.String (encode) import Data.Char (ord) import Data.Maybe (fromMaybe, fromJust) import Data.Loc hiding (L) -- Lexer has replacements.@@ -598,7 +599,7 @@ | intlit { let L loc (INTLIT x) = $1 in IntLit x NoInfo loc } | floatlit { let L loc (FLOATLIT x) = $1 in FloatLit x NoInfo loc } | stringlit { let L loc (STRINGLIT s) = $1 in- ArrayLit (map (flip Literal loc . SignedValue . Int32Value . fromIntegral . ord) s) NoInfo loc }+ ArrayLit (map (flip Literal loc . UnsignedValue . Int8Value . fromIntegral) $ encode s) NoInfo loc } | '(' Exp ')' FieldAccesses { foldl (\x (y, _) -> Project y x NoInfo (srclocOf x)) (Parens $2 (srcspan $1 $3))@@ -763,7 +764,7 @@ | intlit { let L loc (INTLIT x) = $1 in (IntLit x NoInfo loc, loc) } | floatlit { let L loc (FLOATLIT x) = $1 in (FloatLit x NoInfo loc, loc) } | stringlit { let L loc (STRINGLIT s) = $1 in- (ArrayLit (map (flip Literal loc . SignedValue . Int32Value . fromIntegral . ord) s) NoInfo loc, loc) }+ (ArrayLit (map (flip Literal loc . UnsignedValue . Int8Value . fromIntegral) $ encode s) NoInfo loc, loc) } | VConstr0 { (VConstr0 (fst $1) NoInfo (snd $1), snd $1) } LoopForm :: { LoopFormBase NoInfo Name }@@ -874,7 +875,7 @@ StringValue :: { Value } StringValue : stringlit { let L pos (STRINGLIT s) = $1 in- ArrayValue (arrayFromList $ map (PrimValue . SignedValue . Int32Value . fromIntegral . ord) s) $ Prim $ Signed Int32 }+ ArrayValue (arrayFromList $ map (PrimValue . UnsignedValue . Int8Value . fromIntegral) $ encode s) $ Prim $ Signed Int32 } BoolValue :: { Value } BoolValue : true { PrimValue $ BoolValue True }@@ -898,15 +899,13 @@ : f32lit { let L loc (F32LIT num) = $1 in (Float32Value num, loc) } | f64lit { let L loc (F64LIT num) = $1 in (Float64Value num, loc) } | QualName {% let (qn, loc) = $1 in- if qn == QualName [nameFromString "f32"] (nameFromString "inf")- then return (Float32Value (1/0), loc)- else if qn == QualName [nameFromString "f32"] (nameFromString "nan")- then return (Float32Value (0/0), loc)- else if qn == QualName [nameFromString "f64"] (nameFromString "inf")- then return (Float64Value (1/0), loc)- else if qn == QualName [nameFromString "f64"] (nameFromString "nan")- then return (Float64Value (0/0), loc)- else parseErrorAt (snd $1) Nothing }+ case qn of+ QualName ["f32"] "inf" -> return (Float32Value (1/0), loc)+ QualName ["f32"] "nan" -> return (Float32Value (0/0), loc)+ QualName ["f64"] "inf" -> return (Float64Value (1/0), loc)+ QualName ["f64"] "nan" -> return (Float64Value (0/0), loc)+ _ -> parseErrorAt (snd $1) Nothing+ } | floatlit { let L loc (FLOATLIT num) = $1 in (Float64Value num, loc) } ArrayValue :: { Value }
src/Language/Futhark/Pretty.hs view
@@ -237,7 +237,7 @@ then equals </> indent 2 (ppr e) else equals <+> align (ppr e)) </> (case body of LetPat{} -> ppr body- _ -> text "in" <+> ppr body)+ _ -> text "in" <+> align (ppr body)) where linebreak = case e of Map{} -> True Reduce{} -> True@@ -297,9 +297,6 @@ pprPrec _ (Scan lam e a _) = ppSOAC "scan" [lam] [e, a] pprPrec _ (Filter lam a _) = ppSOAC "filter" [lam] [a] pprPrec _ (Partition k lam a _) = text "partition" <+> ppr k <+> spread (map (pprPrec 10) [lam, a])- pprPrec _ (Zip 0 e es _ _) = text "zip" <+> spread (map (pprPrec 10) (e:es))- pprPrec _ (Zip i e es _ _) = text "zip@" <> ppr i <+> spread (map (pprPrec 10) (e:es))- pprPrec _ (Unzip e _ _) = text "unzip" <+> pprPrec (-1) e pprPrec _ (Unsafe e _) = text "unsafe" <+> pprPrec (-1) e pprPrec _ (Assert e1 e2 _ _) = text "assert" <+> pprPrec 10 e1 <+> pprPrec 10 e2 pprPrec p (Lambda tparams params body ascript _ _) =
src/Language/Futhark/Syntax.hs view
@@ -711,14 +711,6 @@ -- may choose the maximal chunk size that still satisfies the memory -- requirements of the device. - | Zip Int (ExpBase f vn) [ExpBase f vn] (f CompType) SrcLoc- -- ^ Conventional zip taking nonzero arrays as arguments.- -- All arrays must have the exact same length.-- | Unzip (ExpBase f vn) [f CompType] SrcLoc- -- ^ Unzip that can unzip to tuples of arbitrary size.- -- The types are the elements of the tuple.- | Unsafe (ExpBase f vn) SrcLoc -- ^ Explore the Danger Zone and elide safety checks on -- array operations and other assertions during execution@@ -768,8 +760,6 @@ locOf (Map _ _ _ loc) = locOf loc locOf (Reduce _ _ _ _ pos) = locOf pos locOf (GenReduce _ _ _ _ _ pos) = locOf pos- locOf (Zip _ _ _ _ loc) = locOf loc- locOf (Unzip _ _ pos) = locOf pos locOf (Scan _ _ _ pos) = locOf pos locOf (Filter _ _ pos) = locOf pos locOf (Partition _ _ _ loc) = locOf loc
src/Language/Futhark/Traversals.hs view
@@ -125,11 +125,6 @@ astMap tv (GenReduce hist op ne bfun img loc) = GenReduce <$> mapOnExp tv hist <*> mapOnExp tv op <*> mapOnExp tv ne <*> mapOnExp tv bfun <*> mapOnExp tv img <*> pure loc- astMap tv (Zip i e es t loc) =- Zip i <$> mapOnExp tv e <*> mapM (mapOnExp tv) es <*>- traverse (mapOnCompType tv) t <*> pure loc- astMap tv (Unzip e ts loc) =- Unzip <$> mapOnExp tv e <*> mapM (traverse $ mapOnCompType tv) ts <*> pure loc astMap tv (Unsafe e loc) = Unsafe <$> mapOnExp tv e <*> pure loc astMap tv (Assert e1 e2 desc loc) =
src/Language/Futhark/TypeChecker.hs view
@@ -168,6 +168,12 @@ mempty { envTypeTable = M.singleton v $ TypeAbbr l [] $ TypeVar () Nonunique (typeName v) [] } +-- In this function, after the recursion, we add the Env of the+-- current Spec *after* the one that is returned from the recursive+-- call. This implements the behaviour that specs later in a module+-- type can override those earlier (it rarely matters, but it affects+-- the specific structure of substitutions in case some module type is+-- redundantly imported multiple times). checkSpecs :: [SpecBase NoInfo Name] -> TypeM (TySet, Env, [SpecBase Info VName]) checkSpecs [] = return (mempty, mempty, [])@@ -195,7 +201,7 @@ (tenv, tdec') <- checkTypeBind tdec (abstypes, env, specs') <- localEnv tenv $ checkSpecs specs return (abstypes,- tenv <> env,+ env <> tenv, TypeAbbrSpec tdec' : specs') checkSpecs (TypeSpec l name ps doc loc : specs) =@@ -211,7 +217,7 @@ } (abstypes, env, specs') <- localEnv tenv $ checkSpecs specs return (M.insert (qualName name') l abstypes,- tenv <> env,+ env <> tenv, TypeSpec l name' ps' doc loc : specs') checkSpecs (ModSpec name sig doc loc : specs) =@@ -223,7 +229,7 @@ } (abstypes, env, specs') <- localEnv senv $ checkSpecs specs return (M.mapKeys (qualify name') (mtyAbs mty) <> abstypes,- senv <> env,+ env <> senv, ModSpec name' sig' doc loc : specs') checkSpecs (IncludeSpec e loc : specs) = do@@ -232,8 +238,8 @@ mapM_ (warnIfShadowing . fmap baseName) $ M.keys e_abs (abstypes, env, specs') <- localEnv e_env $ checkSpecs specs- return (e_abs <> abstypes,- e_env <> env,+ return (abstypes <> e_abs,+ env <> e_env, IncludeSpec e' loc : specs') where warnIfShadowing qn = (lookupType loc qn >> warnAbout qn)
src/Language/Futhark/TypeChecker/Monad.hs view
@@ -6,6 +6,7 @@ , runTypeM , askEnv , askRootEnv+ , askImportName , localTmpEnv , checkQualNameWithEnv , bindSpaced@@ -154,6 +155,10 @@ askEnv = asks contextEnv askRootEnv = asks contextRootEnv +-- | The name of the current file/import.+askImportName :: TypeM ImportName+askImportName = asks contextImportName+ localTmpEnv :: Env -> TypeM a -> TypeM a localTmpEnv env = local $ \ctx -> ctx { contextEnv = env <> contextEnv ctx }@@ -311,7 +316,7 @@ -- Try to prepend qualifiers to the type names such that they -- represent how to access the type in some scope. qualifyTypeVars :: ASTMappable t => Env -> [VName] -> [VName] -> t -> t-qualifyTypeVars outer_env except qs = runIdentity . astMap mapper+qualifyTypeVars outer_env except ref_qs = runIdentity . astMap mapper where mapper = ASTMapper { mapOnExp = pure , mapOnName = pure , mapOnQualName = pure . qual@@ -321,9 +326,16 @@ , mapOnPatternType = pure } qual (QualName orig_qs name)- | name `elem` except ||- reachable orig_qs name outer_env = QualName orig_qs name- | otherwise = QualName (qs<>orig_qs) name+ | name `elem` except || reachable orig_qs name outer_env =+ QualName orig_qs name+ | otherwise =+ prependAsNecessary [] ref_qs $ QualName orig_qs name++ prependAsNecessary qs rem_qs (QualName orig_qs name)+ | reachable (qs++orig_qs) name outer_env = QualName (qs++orig_qs) name+ | otherwise = case rem_qs of+ q:rem_qs' -> prependAsNecessary (qs++[q]) rem_qs' (QualName orig_qs name)+ [] -> QualName (qs++orig_qs) name reachable [] name env = isJust $ find matches $ M.elems (envTypeTable env)
src/Language/Futhark/TypeChecker/Terms.hs view
@@ -28,6 +28,7 @@ import Prelude hiding (mod) import Language.Futhark+import Language.Futhark.Semantic (includeToString) import Language.Futhark.Traversals import Language.Futhark.TypeChecker.Monad hiding (BoundV, checkQualNameWithEnv) import Language.Futhark.TypeChecker.Types hiding (checkTypeDecl)@@ -347,6 +348,9 @@ checkIntrinsic :: Namespace -> QualName Name -> SrcLoc -> TermTypeM (TermScope, QualName VName) checkIntrinsic space qn@(QualName _ name) loc | Just v <- M.lookup (space, name) intrinsicsNameMap = do+ me <- liftTypeM askImportName+ unless ("/futlib" `isPrefixOf` includeToString me) $+ warn loc "Using intrinsic functions directly can easily crash the compiler or result in wrong code generation." scope <- ask return (scope, v) | otherwise =@@ -420,14 +424,14 @@ returnAliased :: MonadTypeChecker m => Name -> Name -> SrcLoc -> m () returnAliased fname name loc = throwError $ TypeError loc $- "Unique return value of function " ++ nameToString fname ++- " is aliased to " ++ pretty name ++ ", which is not consumed."+ "Unique return value of function " ++ quote (pretty fname) +++ " is aliased to " ++ quote (pretty name) ++ ", which is not consumed." uniqueReturnAliased :: MonadTypeChecker m => Name -> SrcLoc -> m a uniqueReturnAliased fname loc = throwError $ TypeError loc $- "A unique tuple element of return value of function " ++- nameToString fname ++ " is aliased to some other tuple component."+ "A unique tuple element of return value of `" +++ quote (pretty fname) ++ "` is aliased to some other tuple component." --- Basic checking @@ -955,7 +959,7 @@ return $ LetFun name' (tparams', params', maybe_retdecl', Info rettype, e') body' loc -checkExp (LetWith dest src idxes ve body pos) = do+checkExp (LetWith dest src idxes ve body loc) = do (t, _) <- newArrayType (srclocOf src) "src" $ length idxes let elemt = stripArray (length $ filter isFix idxes) t sequentially (checkIdent src) $ \src' _ -> do@@ -965,18 +969,26 @@ void $ unifies t src'' unless (unique $ unInfo $ identType src') $- typeError pos $ "Source " ++ quote (pretty (identName src)) ++- " has type " ++ pretty (unInfo $ identType src') ++ ", which is not unique"+ typeError loc $ "Source " ++ quote (pretty (identName src)) +++ " has type " ++ pretty (unInfo $ identType src') ++ ", which is not unique."+ vtable <- asks scopeVtable+ forM_ (aliases $ unInfo $ identType src') $ \v ->+ case aliasVar v `M.lookup` vtable of+ Just (BoundV Local _ v_t)+ | not $ unique v_t ->+ typeError loc $ "Source " ++ quote (pretty (identName src)) +++ " aliases " ++ quote (prettyName (aliasVar v)) ++ ", which is not consumable."+ _ -> return () idxes' <- mapM checkDimIndex idxes sequentially (unifies elemt =<< checkExp ve) $ \ve' _ -> do ve_t <- expType ve' when (AliasBound (identName src') `S.member` aliases ve_t) $- badLetWithValue pos+ badLetWithValue loc bindingIdent dest (unInfo (identType src') `setAliases` S.empty) $ \dest' -> do body' <- consuming src' $ checkExp body- return $ LetWith dest' src' idxes' ve' body' pos+ return $ LetWith dest' src' idxes' ve' body' loc where isFix DimFix{} = True isFix _ = False @@ -1020,44 +1032,6 @@ where isFix DimFix{} = True isFix _ = False -checkExp (Zip i e es NoInfo loc) = do- let checkInput inp = do (arr_t, _) <- newArrayType (srclocOf e) "e" (1+i)- unifies arr_t =<< checkExp inp- e' <- checkInput e- es' <- mapM checkInput es-- e_ts <- mapM expType $ e':es'- ts <- forM (zip (e':es') e_ts) $ \(arr_e, arr_e_t) ->- case typeToRecordArrayElem =<< peelArray (i+1) arr_e_t of- Just t -> return t- Nothing -> typeError (srclocOf arr_e) $- "Expected array with at least " ++ show (1+i) ++- " dimensions, but got " ++ pretty arr_e_t ++ "."-- let u = mconcat $ map (uniqueness . typeOf) $ e':es'- t = Array (mconcat $ map aliases e_ts) u- (ArrayRecordElem $ M.fromList $ zip tupleFieldNames ts)- (rank (1+i))- return $ Zip i e' es' (Info t) loc--checkExp (Unzip e _ loc) = do- e' <- checkExp e- e_t <- expType e'- case e_t of- Array _ u (ArrayRecordElem fs) shape- | Just ets <- map (componentType shape u) <$> areTupleFields fs ->- return $ Unzip e' (map Info ets) loc- t ->- typeError loc $- "Argument to unzip is not an array of tuples, but " ++- pretty t ++ "."- where componentType shape u et =- case et of- RecordArrayElem et' ->- Array mempty u et' shape- RecordArrayArrayElem et' et_shape ->- Array mempty u et' (shape <> et_shape)- checkExp (Unsafe e loc) = Unsafe <$> checkExp e <*> pure loc @@ -1080,14 +1054,15 @@ maybe_retdecl' <- traverse checkTypeDecl maybe_retdecl (body', closure) <- tapOccurences $ noUnique $ checkFunBody body (unInfo . expandedType <$> maybe_retdecl') loc- (maybe_retdecl'', rettype) <- case maybe_retdecl' of- Just retdecl'@(TypeDecl _ (Info st)) -> return (Just retdecl', st)- Nothing -> do- body_t <- expType body'- return (Nothing, inferReturnUniqueness params' body_t)+ body_t <- expType body'+ let (maybe_retdecl'', rettype) =+ case maybe_retdecl' of+ Just retdecl'@(TypeDecl _ (Info st)) -> (Just retdecl', st)+ Nothing -> (Nothing, inferReturnUniqueness params' body_t) - closure' <- lexicalClosure params' closure+ checkGlobalAliases params' body_t loc + closure' <- lexicalClosure params' closure return $ Lambda tparams' params' body' maybe_retdecl'' (Info (closure', rettype)) loc checkExp (OpSection op _ loc) = do@@ -1253,19 +1228,25 @@ | unique pat_v_t, v:_ <- S.toList $ S.map aliasVar (aliases t) `S.intersection` bound_outside = lift $ typeError loc $ "Loop return value corresponding to merge parameter " ++- prettyName pat_v ++ " aliases " ++ prettyName v ++ "."+ quote (prettyName pat_v) ++ " aliases " ++ prettyName v ++ "." | otherwise = do (cons,obs) <- get unless (S.null $ aliases t `S.intersection` cons) $ lift $ typeError loc $ "Loop return value for merge parameter " ++- prettyName pat_v ++ " aliases other consumed merge parameter."+ quote (prettyName pat_v) ++ " aliases other consumed merge parameter." when (unique pat_v_t && not (S.null (aliases t `S.intersection` (cons<>obs)))) $ lift $ typeError loc $ "Loop return value for consuming merge parameter " ++- prettyName pat_v ++ " aliases previously returned value." ++ show (aliases t, cons, obs)+ quote (prettyName pat_v) ++ " aliases previously returned value." if unique pat_v_t then put (cons<>aliases t, obs) else put (cons, obs<>aliases t)+ checkMergeReturn (PatternParens p _) t =+ checkMergeReturn p t+ checkMergeReturn (PatternAscription p _ _) t =+ checkMergeReturn p t+ checkMergeReturn (RecordPattern pfs _) (Record tfs) =+ sequence_ $ M.elems $ M.intersectionWith checkMergeReturn (M.fromList pfs) tfs checkMergeReturn (TuplePattern pats _) t | Just ts <- isTupleRecord t = zipWithM_ checkMergeReturn pats ts checkMergeReturn _ _ =@@ -1648,21 +1629,8 @@ bindSpaced [(Term, fname)] $ do fname' <- checkName Term fname loc- vtable <- asks scopeVtable- let isLocal v = case v `M.lookup` vtable of- Just (BoundV Local _ _) -> True- _ -> False- let als = filter (not . isLocal) $ S.toList $- boundArrayAliases body_t `S.difference`- S.map identName (mconcat (map patIdentSet params'))- case als of- v:_ | not $ null params ->- typeError loc $- unlines [ "Function result aliases the free variable " <>- quote (prettyName v) <> "."- , "Use " ++ quote "copy" ++ " to break the aliasing."]- _ ->- return (fname', tparams'', params'', maybe_retdecl'', rettype, body')+ checkGlobalAliases params'' body_t loc+ return (fname', tparams'', params'', maybe_retdecl'', rettype, body') where -- | Check that unique return values do not alias a -- non-consumed parameter.@@ -1695,6 +1663,25 @@ concat $ M.elems $ M.intersectionWith returnAliasing ets1 ets2 returnAliasing expected got = [(uniqueness expected, S.map aliasVar $ aliases got)]++checkGlobalAliases :: [Pattern] -> CompType -> SrcLoc -> TermTypeM ()+checkGlobalAliases params body_t loc = do+ vtable <- asks scopeVtable+ let isLocal v = case v `M.lookup` vtable of+ Just (BoundV Local _ _) -> True+ _ -> False+ let als = filter (not . isLocal) $ S.toList $+ boundArrayAliases body_t `S.difference`+ S.map identName (mconcat (map patIdentSet params))+ case als of+ v:_ | not $ null params ->+ typeError loc $+ unlines [ "Function result aliases the free variable " <>+ quote (prettyName v) <> "."+ , "Use " ++ quote "copy" ++ " to break the aliasing."]+ _ ->+ return ()+ inferReturnUniqueness :: [Pattern] -> CompType -> StructType inferReturnUniqueness params t =
src/Language/Futhark/TypeChecker/Types.hs view
@@ -375,18 +375,18 @@ type TypeSubs = M.Map VName TypeSub -substituteTypes :: TypeSubs -> StructType -> StructType+substituteTypes :: Monoid als => TypeSubs -> TypeBase (DimDecl VName) als -> TypeBase (DimDecl VName) als substituteTypes substs ot = case ot of Array als u at shape -> maybe nope (`addAliases` (<>als)) $ arrayOf (substituteTypesInArrayElem at) (substituteInShape shape) u Prim t -> Prim t- TypeVar () u v targs+ TypeVar als u v targs | Just (TypeSub (TypeAbbr _ ps t)) <- M.lookup (qualLeaf (qualNameFromTypeName v)) substs ->- applyType ps t (map substituteInTypeArg targs)- `setUniqueness` u- | otherwise -> TypeVar () u v $ map substituteInTypeArg targs+ applyType ps (t `setAliases` mempty) (map substituteInTypeArg targs)+ `setUniqueness` u `addAliases` (<>als)+ | otherwise -> TypeVar als u v $ map substituteInTypeArg targs Record ts -> Record $ fmap (substituteTypes substs) ts Arrow als v t1 t2 ->@@ -399,9 +399,9 @@ substituteTypesInArrayElem (ArrayPolyElem v targs) | Just (TypeSub (TypeAbbr _ ps t)) <- M.lookup (qualLeaf (qualNameFromTypeName v)) substs =- applyType ps t (map substituteInTypeArg targs)+ applyType ps (t `setAliases` mempty) (map substituteInTypeArg targs) | otherwise =- TypeVar () Nonunique v (map substituteInTypeArg targs)+ TypeVar mempty Nonunique v (map substituteInTypeArg targs) substituteTypesInArrayElem (ArrayRecordElem ts) = Record ts' where ts' = fmap (substituteTypes substs . recordArrayElemToType) ts@@ -424,7 +424,8 @@ substituteTypesInBoundV substs (BoundV tps t) = BoundV tps (substituteTypes substs t) -applyType :: [TypeParam] -> StructType -> [StructTypeArg] -> StructType+applyType :: Monoid als =>+ [TypeParam] -> TypeBase (DimDecl VName) als -> [StructTypeArg] -> TypeBase (DimDecl VName) als applyType ps t args = substituteTypes substs t where substs = M.fromList $ zipWith mkSubst ps args
src/Language/Futhark/TypeChecker/Unify.hs view
@@ -29,11 +29,9 @@ import qualified Data.Map.Strict as M import qualified Data.Set as S -import Prelude hiding (mod)- import Language.Futhark-import Language.Futhark.TypeChecker.Monad hiding (BoundV, checkQualNameWithEnv)-import Language.Futhark.TypeChecker.Types hiding (checkTypeDecl)+import Language.Futhark.TypeChecker.Monad hiding (BoundV)+import Language.Futhark.TypeChecker.Types import Futhark.Util.Pretty (Pretty) -- | Mapping from fresh type variables, instantiated from the type
src/futhark.hs view
@@ -63,6 +63,7 @@ , ("pkg", (Pkg.main, "Manage local packages.")) , ("check", (Misc.mainCheck, "Type check a program."))+ , ("imports", (Misc.mainImports, "Print all non-library imported Futhark files to standard out and exit.")) ] msg :: String
− src/futharki.hs
@@ -1,19 +0,0 @@-module Main (main) where--import System.Environment-import System.Process-import System.IO-import System.Exit--main :: IO ()-main = do- prog <- getProgName- args <- getArgs- let suffix = case args of- [] -> "repl"- _ -> "run"- hPutStrLn stderr $- "'" ++ prog ++ "' is deprecated. Use '" ++- unwords ["futhark", suffix] ++ "' instead."- (_, _, _, h) <- createProcess $ proc "futhark" $ suffix:args- exitWith =<< waitForProcess h
− src/wrapper.hs
@@ -1,29 +0,0 @@--- Wrapper program that translates @futhark-foo@ to @futhark foo@;--- using whichever @futhark@ binary is in the user's search path.--module Main (main) where--import Data.Maybe--import System.Environment-import System.Process-import System.IO-import System.Exit--nameChanges :: [(String, String)]-nameChanges = [ ("py", "python")- , ("cs", "csharp")- ]--main :: IO ()-main = do- prog <- getProgName- args <- getArgs- let suffix = drop 1 $ dropWhile (/='-') prog- suffix' = fromMaybe suffix $ lookup suffix nameChanges-- hPutStrLn stderr $- prog ++ ": this command is deprecated. Use '" ++- unwords ["futhark", suffix'] ++ "' instead."- (_, _, _, h) <- createProcess $ proc "futhark" $ suffix':args- exitWith =<< waitForProcess h
unittests/Futhark/Analysis/ScalExpTests.hs view
@@ -3,7 +3,6 @@ module Futhark.Analysis.ScalExpTests ( tests , parseScalExp- , parseScalExp' ) where @@ -24,11 +23,8 @@ tests :: TestTree tests = testGroup "ScalExpTests" [] -parseScalExp :: String -> ScalExp-parseScalExp = parseScalExp' M.empty--parseScalExp' :: M.Map String (Int, Type) -> String -> ScalExp-parseScalExp' m s = case evalState (runParserT expr ("string: " ++ s) s) (0, m) of+parseScalExp :: M.Map String (Int, Type) -> String -> ScalExp+parseScalExp m s = case evalState (runParserT expr ("string: " ++ s) s) (0, m) of Left err -> error $ show err Right e -> e
unittests/Futhark/Optimise/AlgSimplifyTests.hs view
@@ -9,7 +9,7 @@ import Futhark.Representation.AST import Futhark.Analysis.ScalExp-import Futhark.Analysis.ScalExpTests (parseScalExp')+import Futhark.Analysis.ScalExpTests (parseScalExp) import Futhark.Analysis.AlgSimplify tests :: TestTree@@ -33,7 +33,7 @@ ] where vars = declareVars [("x", int32)] simplify'' e = simplify' vars e []- scalExp = parseScalExp' vars+ scalExp = parseScalExp vars cfoldTest input expected = testCase ("constant-fold " ++ input) $@@ -85,11 +85,11 @@ (lookupVarName name varinfo, (i, fixBound lower, fixBound upper))) fixBound "" = Nothing- fixBound s = Just $ parseScalExp' varinfo s+ fixBound s = Just $ parseScalExp varinfo s simplify' :: VarInfo -> String -> RangesRep' -> ScalExp simplify' varinfo s r = simplify e r'- where e = parseScalExp' varinfo s+ where e = parseScalExp varinfo s r' = instantiateRanges varinfo r mkSuffConds' :: VarInfo -> String -> RangesRep' -> [[ScalExp]]@@ -97,5 +97,5 @@ case mkSuffConds e r' of Left _ -> [[e]] Right sc -> sc- where e = simplify (parseScalExp' varinfo s) r'+ where e = simplify (parseScalExp varinfo s) r' r' = instantiateRanges varinfo r
unittests/Futhark/Representation/AST/AttributesTests.hs view
@@ -7,7 +7,6 @@ import Test.Tasty -import Futhark.Representation.AST.SyntaxTests () import qualified Futhark.Representation.AST.Attributes.ReshapeTests import qualified Futhark.Representation.AST.Attributes.RearrangeTests
+ unittests/Futhark/Representation/ExplicitMemory/IndexFunction/Alg.hs view
@@ -0,0 +1,178 @@+-- | A simple index operation representation. Every operation corresponds to a+-- constructor.+module Futhark.Representation.ExplicitMemory.IndexFunction.Alg+ ( IxFun(..)+ , iota+ , offsetIndex+ , strideIndex+ , permute+ , rotate+ , reshape+ , slice+ , rebase+ , repeat+ , shape+ , rank+ , index+ )+where++import Data.Monoid ((<>))+import Data.List hiding (repeat)++import Prelude hiding (repeat, mod)++import Futhark.Representation.AST.Syntax+ (ShapeChange, DimChange(..), Slice, sliceDims, DimIndex(..), unitSlice)+import Futhark.Representation.AST.Attributes+import Futhark.Representation.AST.Pretty ()+import Futhark.Util.IntegralExp+import Futhark.Util.Pretty++type Shape num = [num]+type Indices num = [num]+type Permutation = [Int]++data IxFun num = Direct (Shape num)+ | Permute (IxFun num) Permutation+ | Rotate (IxFun num) (Indices num)+ | Index (IxFun num) (Slice num)+ | Reshape (IxFun num) (ShapeChange num)+ | Repeat (IxFun num) [Shape num] (Shape num)+ | OffsetIndex (IxFun num) num+ | StrideIndex (IxFun num) num+ | Rebase (IxFun num) (IxFun num)+ deriving (Eq, Show)++instance Pretty num => Pretty (IxFun num) where+ ppr (Direct dims) =+ text "Direct" <> parens (commasep $ map ppr dims)+ ppr (Permute fun perm) = ppr fun <> ppr perm+ ppr (Rotate fun offsets) = ppr fun <> brackets (commasep $ map ((text "+" <>) . ppr) offsets)+ ppr (Index fun is) = ppr fun <> brackets (commasep $ map ppr is)+ ppr (Reshape fun oldshape) =+ ppr fun <> text "->reshape" <>+ parens (commasep (map ppr oldshape))+ ppr (Repeat fun outer_shapes inner_shape) =+ ppr fun <> text "->repeat" <> parens (commasep (map ppr $ outer_shapes++ [inner_shape]))+ ppr (OffsetIndex fun i) =+ ppr fun <> text "->offset_index" <> parens (ppr i)+ ppr (StrideIndex fun s) =+ ppr fun <> text "->stride_index" <> parens (ppr s)+ ppr (Rebase new_base fun) =+ text "rebase(" <> ppr new_base <> text ", " <> ppr fun <> text ")"+++iota :: Shape num -> IxFun num+iota = Direct++offsetIndex :: IxFun num -> num -> IxFun num+offsetIndex = OffsetIndex++strideIndex :: IxFun num -> num -> IxFun num+strideIndex = StrideIndex++permute :: IxFun num -> Permutation -> IxFun num+permute = Permute++rotate :: IxFun num -> Indices num -> IxFun num+rotate = Rotate++repeat :: IxFun num -> [Shape num] -> Shape num -> IxFun num+repeat = Repeat++slice :: IxFun num -> Slice num -> IxFun num+slice = Index++rebase :: IxFun num -> IxFun num -> IxFun num+rebase = Rebase++reshape :: IxFun num -> ShapeChange num -> IxFun num+reshape = Reshape++shape :: IntegralExp num =>+ IxFun num -> Shape num+shape (Direct dims) =+ dims+shape (Permute ixfun perm) =+ rearrangeShape perm $ shape ixfun+shape (Rotate ixfun _) =+ shape ixfun+shape (Index _ how) =+ sliceDims how+shape (Reshape _ dims) =+ map newDim dims+shape (Repeat ixfun outer_shapes inner_shape) =+ concat (zipWith repeated outer_shapes (shape ixfun)) ++ inner_shape+ where repeated outer_ds d = outer_ds ++ [d]+shape (OffsetIndex ixfun _) =+ shape ixfun+shape (StrideIndex ixfun _) =+ shape ixfun+shape (Rebase _ ixfun) =+ shape ixfun++rank :: IntegralExp num =>+ IxFun num -> Int+rank = length . shape+++index :: (IntegralExp num, Eq num) =>+ IxFun num -> Indices num -> num -> num+index (Direct dims) is element_size =+ sum (zipWith (*) is slicesizes) * element_size+ where slicesizes = drop 1 $ sliceSizes dims+index (Permute fun perm) is_new element_size =+ index fun is_old element_size+ where is_old = rearrangeShape (rearrangeInverse perm) is_new+index (Rotate fun offsets) is element_size =+ index fun (zipWith mod (zipWith (+) is offsets) dims) element_size+ where dims = shape fun+index (Index fun js) is element_size =+ index fun (adjust js is) element_size+ where adjust (DimFix j:js') is' = j : adjust js' is'+ adjust (DimSlice j _ s:js') (i:is') = j + i * s : adjust js' is'+ adjust _ _ = []+index (Reshape fun newshape) is element_size =+ let new_indices = reshapeIndex (shape fun) (newDims newshape) is+ in index fun new_indices element_size+index (Repeat fun outer_shapes _) is element_size =+ -- Discard those indices that are just repeats. It is intentional+ -- that we cut off those indices that correspond to the innermost+ -- repeated dimensions.+ index fun is' element_size+ where flags dims = replicate (length dims) True ++ [False]+ is' = map snd $ filter (not . fst) $ zip (concatMap flags outer_shapes) is+index (OffsetIndex fun i) is element_size =+ case shape fun of+ d : ds ->+ index (Index fun (DimSlice i (d-i) 1 : map (unitSlice 0) ds)) is element_size+ [] -> error "index: OffsetIndex: underlying index function has rank zero"+index (StrideIndex fun s) is element_size =+ case shape fun of+ d : ds ->+ index (Index fun (DimSlice 0 d s : map (unitSlice 0) ds)) is element_size+ [] -> error "index: StrideIndex: underlying index function has rank zero"+index (Rebase new_base fun) is element_size =+ let fun' = case fun of+ Direct old_shape ->+ if old_shape == shape new_base+ then new_base+ else reshape new_base $ map DimCoercion old_shape+ Permute ixfun perm ->+ permute (rebase new_base ixfun) perm+ Rotate ixfun offsets ->+ rotate (rebase new_base ixfun) offsets+ Index ixfun iis ->+ slice (rebase new_base ixfun) iis+ Reshape ixfun new_shape ->+ reshape (rebase new_base ixfun) new_shape+ Repeat ixfun outer_shapes inner_shape ->+ repeat (rebase new_base ixfun) outer_shapes inner_shape+ StrideIndex ixfun i ->+ strideIndex (rebase new_base ixfun) i+ OffsetIndex ixfun s ->+ offsetIndex (rebase new_base ixfun) s+ r@Rebase{} ->+ r+ in index fun' is element_size
+ unittests/Futhark/Representation/ExplicitMemory/IndexFunctionTests.hs view
@@ -0,0 +1,371 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+module Futhark.Representation.ExplicitMemory.IndexFunctionTests+ ( tests+ )+where++import Prelude hiding (span, repeat)+import qualified Prelude as P+import qualified Data.List as DL++import Test.Tasty+import Test.Tasty.HUnit++import Futhark.Representation.AST.Syntax+import Futhark.Representation.AST.Syntax.Core()+import qualified Futhark.Util.Pretty as PR+import qualified Futhark.Util.IntegralExp as IE+import qualified Futhark.Representation.ExplicitMemory.IndexFunction as IxFunLMAD+import qualified Futhark.Representation.ExplicitMemory.IndexFunction.Alg as IxFunAlg++import qualified Futhark.Representation.ExplicitMemory.IndexFunctionWrapper as IxFunWrap+import Futhark.Representation.ExplicitMemory.IndexFunctionWrapper+++instance IE.IntegralExp Int where+ quot = P.quot+ rem = P.rem+ div = P.div+ mod = P.mod+ sgn = Just . P.signum++ fromInt8 = fromInteger . toInteger+ fromInt16 = fromInteger . toInteger+ fromInt32 = fromInteger . toInteger+ fromInt64 = fromInteger . toInteger++allPoints :: [Int] -> [[Int]]+allPoints dims =+ let total = product dims+ strides = drop 1 $ DL.reverse $ scanl (*) 1 $ DL.reverse dims+ in map (unflatInd strides) [0..total-1]+ where unflatInd :: [Int] -> Int -> [Int]+ unflatInd strides x = fst $+ foldl (\(res, acc) span ->+ (res ++ [acc `P.div` span], acc `P.mod` span))+ ([], x) strides++compareIxFuns :: IxFunLMAD.IxFun Int -> IxFunAlg.IxFun Int -> Assertion+compareIxFuns ixfunLMAD ixfunAlg =+ let lmadShape = IxFunLMAD.shape ixfunLMAD+ algShape = IxFunAlg.shape ixfunAlg+ points = allPoints lmadShape+ resLMAD = map (\is -> IxFunLMAD.index ixfunLMAD is 4) points+ resAlg = map (\is -> IxFunAlg.index ixfunAlg is 4) points+ errorMessage = "lmad ixfun: " ++ PR.pretty ixfunLMAD ++ "\n" +++ "alg ixfun: " ++ PR.pretty ixfunAlg ++ "\n" +++ "lmad shape: " ++ show lmadShape ++ "\n" +++ "alg shape: " ++ show algShape ++ "\n" +++ "lmad points length: " ++ show (length resLMAD) ++ "\n" +++ "alg points length: " ++ show (length resAlg) ++ "\n" +++ "lmad points: " ++ show resLMAD ++ "\n" +++ "alg points: " ++ show resAlg+ in (lmadShape == algShape && resLMAD == resAlg) @? errorMessage++compareOps :: IxFunWrap.IxFun Int -> Assertion+compareOps (ixfunLMAD, ixfunAlg) = compareIxFuns ixfunLMAD ixfunAlg++-- XXX: Clean this up.+n :: Int+n = 19+slice3 :: [DimIndex Int]+slice3 = [ DimSlice 2 (n `P.div` 3) 3+ , DimFix (n `P.div` 2)+ , DimSlice 1 (n `P.div` 2) 2+ ]+++-- Actual tests.+tests :: TestTree+tests = testGroup "IndexFunctionTests"+ $ concat+ [ test_iota+ , test_slice_iota+ , test_reshape_slice_iota1+ , test_permute_slice_iota+ , test_repeat_slice_iota+ , test_rotate_rotate_permute_slice_iota+ , test_slice_rotate_permute_slice_iota1+ , test_slice_rotate_permute_slice_iota2+ , test_slice_rotate_permute_slice_iota3+ , test_permute_rotate_slice_permute_slice_iota+ , test_reshape_rotate_iota+ , test_reshape_permute_iota+ , test_reshape_slice_iota2+ , test_reshape_slice_iota3+ , test_complex1+ , test_complex2+ , test_complex3+ , test_rebase1+ , test_rebase2+ , test_rebase3+ , test_rebase4_5+ , test_rebase6+ ]++singleton :: TestTree -> [TestTree]+singleton = (: [])++test_iota :: [TestTree]+test_iota = singleton $ testCase "iota" $ compareOps $+ iota [n]++test_slice_iota :: [TestTree]+test_slice_iota = singleton $ testCase "slice . iota" $ compareOps $+ slice (iota [n, n, n]) slice3++test_reshape_slice_iota1 :: [TestTree]+test_reshape_slice_iota1 = singleton $ testCase "reshape . slice . iota 1" $ compareOps $+ reshape (slice (iota [n, n, n]) slice3)+ [DimNew (n `P.div` 2), DimNew (n `P.div` 3)]++test_permute_slice_iota :: [TestTree]+test_permute_slice_iota = singleton $ testCase "permute . slice . iota" $ compareOps $+ permute (slice (iota [n, n, n]) slice3) [1, 0]++test_repeat_slice_iota :: [TestTree]+test_repeat_slice_iota = singleton $ testCase "repeat . slice . iota" $ compareOps $+ repeat (slice (iota [n, n, n]) slice3) [[2, 3], [3, 2]] [4, 4]++test_rotate_rotate_permute_slice_iota :: [TestTree]+test_rotate_rotate_permute_slice_iota =+ singleton $ testCase "rotate . rotate . permute . slice . iota" $ compareOps $+ let ixfun = permute (slice (iota [n, n, n]) slice3) [1, 0]+ in rotate (rotate ixfun [2, 1]) [1, 2]++test_slice_rotate_permute_slice_iota1 :: [TestTree]+test_slice_rotate_permute_slice_iota1 =+ singleton $ testCase "slice . rotate . permute . slice . iota 1" $ compareOps $+ let slice2 = [ DimSlice 0 n 1+ , DimSlice 1 (n `P.div` 2) 2+ , DimSlice 0 n 1+ ]+ slice13 = [ DimSlice 2 (n `P.div` 3) 3+ , DimSlice 0 (n `P.div` 2) 1+ , DimSlice 1 (n `P.div` 2) 2+ ]+ ixfun = permute (slice (iota [n, n, n]) slice2) [2, 1, 0]+ ixfun' = slice (rotate ixfun [3, 1, 2]) slice13+ in ixfun'++test_slice_rotate_permute_slice_iota2 :: [TestTree]+test_slice_rotate_permute_slice_iota2 =+ singleton $ testCase "slice . rotate . permute . slice . iota 2" $ compareOps $+ let slice2 = [ DimSlice 0 (n `P.div` 2) 1+ , DimFix (n `P.div` 2)+ , DimSlice 0 (n `P.div` 3) 1+ ]+ slice13 = [ DimSlice 2 (n `P.div` 3) 3+ , DimSlice 0 n 1+ , DimSlice 1 (n `P.div` 2) 2+ ]+ ixfun = permute (slice (iota [n, n, n]) slice13) [2, 1, 0]+ ixfun' = slice (rotate ixfun [3, 1, 2]) slice2+ in ixfun'++test_slice_rotate_permute_slice_iota3 :: [TestTree]+test_slice_rotate_permute_slice_iota3 =+ singleton $ testCase "slice . rotate . permute . slice . iota 3" $ compareOps $+ -- full-slice of (-1) stride+ let ixfun = permute (slice (iota [n, n, n]) slice3) [1, 0]+ ixfun' = rotate ixfun [2, 1]++ (n1, m1) = case IxFunLMAD.shape (fst ixfun') of+ [a, b] -> (a, b)+ _ -> error "expecting 2 dimensions at this point!"+ negslice = [DimSlice 0 n1 1, DimSlice (m1 - 1) m1 (-1)]+ ixfun'' = rotate (slice ixfun' negslice) [1,2]+ in ixfun''++test_permute_rotate_slice_permute_slice_iota :: [TestTree]+test_permute_rotate_slice_permute_slice_iota =+ singleton $ testCase "permute . rotate . slice . permute . slice . iota" $ compareOps $+ -- contiguousness+ let slice33 = [ DimFix (n `P.div` 2)+ , DimSlice (n - 1) (n `P.div` 3) (-1)+ , DimSlice 0 n 1+ ]+ ixfun = permute (slice (iota [n, n, n]) slice33) [1, 0]+ m = n `P.div` 3+ slice1 = [DimSlice (n - 1) n (-1), DimSlice 2 (m - 2) 1]+ ixfun' = permute (rotate (slice ixfun slice1) [1, 2]) [1, 0]+ in ixfun'++test_reshape_rotate_iota :: [TestTree]+test_reshape_rotate_iota =+ -- negative reshape test+ singleton $ testCase "reshape . rotate . iota" $ compareOps $+ let newdims = [DimNew (n * n), DimCoercion n]+ in reshape (rotate (iota [n, n, n]) [1, 0, 0]) newdims++test_reshape_permute_iota :: [TestTree]+test_reshape_permute_iota =+ -- negative reshape test+ singleton $ testCase "reshape . permute . iota" $ compareOps $+ let newdims = [DimNew (n * n), DimCoercion n]+ in reshape (permute (iota [n, n, n]) [1, 2, 0]) newdims++test_reshape_slice_iota2 :: [TestTree]+test_reshape_slice_iota2 =+ -- negative reshape test+ singleton $ testCase "reshape . slice . iota 2" $ compareOps $+ let newdims = [DimNew (n*n), DimCoercion n]+ slc = [ DimFix (n `P.div` 2)+ , DimSlice (n-1) n (-1)+ , DimSlice 0 n 1+ , DimSlice (n-1) n (-1)+ ]+ in reshape (slice (iota [n, n, n, n]) slc) newdims++test_reshape_slice_iota3 :: [TestTree]+test_reshape_slice_iota3 =+ -- negative reshape test+ singleton $ testCase "reshape . slice . iota 3" $ compareOps $+ let newdims = [DimNew (n*n), DimCoercion n]+ slc = [ DimFix (n `P.div` 2)+ , DimSlice 0 n 1+ , DimSlice 0 (n `P.div` 2) 1+ , DimSlice 0 n 1+ ]+ in reshape (slice (iota [n, n, n, n]) slc) newdims++test_complex1 :: [TestTree]+test_complex1 =+ singleton $ testCase "reshape . permute . rotate . slice . permute . slice . iota 1" $ compareOps $+ let newdims = [ DimCoercion n+ , DimCoercion n+ , DimNew n+ , DimCoercion ((n `P.div` 3) - 2)+ ]+ slice33 = [ DimSlice (n-1) (n `P.div` 3) (-1)+ , DimSlice (n-1) n (-1)+ , DimSlice (n-1) n (-1)+ , DimSlice 0 n 1+ ]+ ixfun = permute (slice (iota [n, n, n, n, n]) slice33) [3, 1, 2, 0]+ m = n `P.div` 3+ slice1 = [DimSlice 0 n 1, DimSlice (n-1) n (-1), DimSlice (n-1) n (-1), DimSlice 1 (m-2) (-1)]+ ixfun' = reshape (rotate (slice ixfun slice1) [1, 2, 3, 4]) newdims+ in ixfun'++test_complex2 :: [TestTree]+test_complex2 =+ singleton $ testCase "reshape . permute . rotate . slice . permute . slice . iota 2" $ compareOps $+ let newdims = [ DimCoercion n+ , DimNew (n*n)+ , DimCoercion ((n `P.div` 3) - 2)]+ slc2 = [ DimFix (n `P.div` 2)+ , DimSlice (n-1) (n `P.div` 3) (-1)+ , DimSlice (n-1) n (-1)+ , DimSlice (n-1) n (-1)+ , DimSlice 0 n 1+ ]+ ixfun = permute (slice (iota [n, n, n, n, n]) slc2) [3, 1, 2, 0]+ m = n `P.div` 3+ slice1 = [DimSlice 0 n 1, DimSlice (n-1) n (-1), DimSlice (n-1) n (-1), DimSlice 1 (m-2) (-1)]+ ixfun' = reshape (rotate (slice ixfun slice1) [1, 0, 0, 2]) newdims+ in ixfun'++test_complex3 :: [TestTree]+test_complex3 =+ singleton $ testCase "reshape . permute . rotate . slice . permute . slice . iota 3" $ compareOps $+ let newdims = [ DimCoercion 1+ , DimCoercion n+ , DimNew (n*n)+ , DimCoercion 2+ , DimCoercion ((n `P.div` 3) - 2)+ ]+ slc3 = [ DimFix (n `P.div` 2)+ , DimSlice (n-1) (n `P.div` 3) (-1)+ , DimSlice (n-1) n (-1)+ , DimSlice (n-1) n (-1)+ , DimSlice 0 n 1+ ]+ ixfun = permute (slice (iota [n, n, n, n, n]) slc3) [3, 1, 2, 0]+ m = n `P.div` 3+ slice1 = [DimSlice 0 n 1, DimSlice (n-1) n (-1), DimSlice (n-1) n (-1), DimSlice 1 (m-2) (-1)]+ repeats = [[1],[],[],[2]]+ ixfun' = reshape (repeat (rotate (slice ixfun slice1) [1, 0, 0, 2]) repeats []) newdims+ in ixfun'++test_rebase1 :: [TestTree]+test_rebase1 =+ singleton $ testCase "rebase 1" $ compareOps $+ let slice_base = [ DimFix (n `P.div` 2)+ , DimSlice 2 (n-2) 1+ , DimSlice 3 (n-3) 1+ ]+ ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]+ ixfn_orig = rotate (permute (iota [n-3, n-2]) [1, 0]) [1, 2]+ ixfn_rebase = rebase ixfn_base ixfn_orig+ in ixfn_rebase++test_rebase2 :: [TestTree]+test_rebase2 =+ singleton $ testCase "rebase 2" $ compareOps $+ let slice_base = [ DimFix (n `P.div` 2)+ , DimSlice (n-1) (n-2) (-1)+ , DimSlice (n-1) (n-3) (-1)+ ]+ slice_orig = [ DimSlice (n-4) (n-3) (-1)+ , DimSlice (n-3) (n-2) (-1)+ ]+ ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]+ ixfn_orig = rotate (permute (slice (iota [n-3, n-2]) slice_orig) [1, 0]) [1, 2]+ ixfn_rebase = rebase ixfn_base ixfn_orig+ in ixfn_rebase++test_rebase3 :: [TestTree]+test_rebase3 =+ singleton $ testCase "rebase full orig but not monotonic" $ compareOps $+ let n2 = (n-2) `P.div` 3+ n3 = (n-3) `P.div` 2+ slice_base = [ DimFix (n `P.div` 2)+ , DimSlice (n-1) n2 (-3)+ , DimSlice (n-1) n3 (-2)+ ]+ slice_orig = [ DimSlice (n3-1) n3 (-1)+ , DimSlice (n2-1) n2 (-1)+ ]+ ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]+ ixfn_orig = rotate (permute (slice (iota [n3, n2]) slice_orig) [1, 0]) [1, 2]+ ixfn_rebase = rebase ixfn_base ixfn_orig+ in ixfn_rebase++test_rebase4_5 :: [TestTree]+test_rebase4_5 =+ let n2 = (n-2) `P.div` 3+ n3 = (n-3) `P.div` 2+ slice_base = [ DimFix (n `P.div` 2)+ , DimSlice (n-1) n2 (-3)+ , DimSlice 3 n3 2+ ]+ slice_orig = [ DimSlice (n3-1) n3 (-1)+ , DimSlice 0 n2 1+ ]+ ixfn_base = rotate (permute (slice (iota [n, n, n]) slice_base) [1, 0]) [2, 1]+ ixfn_orig = rotate (permute (slice (iota [n3, n2]) slice_orig) [1, 0]) [1, 2]+ in [ testCase "rebase mixed monotonicities" $ compareOps $+ rebase ixfn_base ixfn_orig++ , testCase "rebase repetitions and mixed monotonicities 1" $ compareOps $+ let ixfn_orig' = repeat ixfn_orig [[2, 2], [3, 3]] [2, 3]+ in rebase ixfn_base ixfn_orig'+ ]++test_rebase6 :: [TestTree]+test_rebase6 =+ singleton $ testCase "rebase repetitions and mixed monotonicities 2" $ compareOps $+ let n2 = (n-2) `P.div` 3+ n3 = (n-3) `P.div` 2+ slice_base = [ DimFix (n `P.div` 2)+ , DimSlice (n-1) n2 (-3)+ ]+ slice_orig = [ DimSlice (n3-1) n3 (-1)+ , DimSlice 0 n2 1+ ]+ ixfn_base = permute (repeat (rotate (slice (iota [n, n]) slice_base) [1]) [[], []] [n3]) [1, 0]+ ixfn_orig = rotate (permute (slice (iota [n3, n2]) slice_orig) [1, 0]) [1, 2]+ ixfn_orig' = repeat ixfn_orig [[2, 2],[3, 3]] [2, 3]+ ixfn_rebase = rebase ixfn_base ixfn_orig'+ in ixfn_rebase
+ unittests/Futhark/Representation/ExplicitMemory/IndexFunctionWrapper.hs view
@@ -0,0 +1,65 @@+-- | Perform index function operations in both algebraic and LMAD+-- representations.+module Futhark.Representation.ExplicitMemory.IndexFunctionWrapper+ ( IxFun+ , iota+ , offsetIndex+ , strideIndex+ , permute+ , rotate+ , reshape+ , slice+ , rebase+ , repeat+ )+where++import Prelude hiding (repeat)++import Futhark.Util.IntegralExp+import Futhark.Representation.AST.Syntax (ShapeChange, Slice)+import qualified Futhark.Representation.ExplicitMemory.IndexFunction as I+import qualified Futhark.Representation.ExplicitMemory.IndexFunction.Alg as IA+++type Shape num = [num]+type Indices num = [num]+type Permutation = [Int]++type IxFun num = (I.IxFun num, IA.IxFun num)++iota :: IntegralExp num =>+ Shape num -> IxFun num+iota x = (I.iota x, IA.iota x)++offsetIndex :: (Eq num, IntegralExp num) =>+ IxFun num -> num -> IxFun num+offsetIndex (l, a) x = (I.offsetIndex l x, IA.offsetIndex a x)++strideIndex :: (Eq num, IntegralExp num) =>+ IxFun num -> num -> IxFun num+strideIndex (l, a) x = (I.strideIndex l x, IA.strideIndex a x)++permute :: IntegralExp num =>+ IxFun num -> Permutation -> IxFun num+permute (l, a) x = (I.permute l x, IA.permute a x)++rotate :: (Eq num, IntegralExp num) =>+ IxFun num -> Indices num -> IxFun num+rotate (l, a) x = (I.rotate l x, IA.rotate a x)++repeat :: (Eq num, IntegralExp num) =>+ IxFun num -> [Shape num] -> Shape num -> IxFun num+repeat (l, a) x y = (I.repeat l x y, IA.repeat a x y)++reshape :: (Eq num, IntegralExp num) =>+ IxFun num -> ShapeChange num -> IxFun num+reshape (l, a) x = (I.reshape l x, IA.reshape a x)++slice :: (Eq num, IntegralExp num) =>+ IxFun num -> Slice num -> IxFun num+slice (l, a) x = (I.slice l x, IA.slice a x)++rebase :: (Eq num, IntegralExp num) =>+ IxFun num -> IxFun num -> IxFun num+rebase (l, a) (l1, a1) = (I.rebase l l1, IA.rebase a a1)
unittests/futhark_tests.hs view
@@ -3,8 +3,11 @@ import qualified Language.Futhark.SyntaxTests import qualified Futhark.Representation.AST.Syntax.CoreTests import qualified Futhark.Representation.AST.AttributesTests+import qualified Futhark.Representation.ExplicitMemory.IndexFunctionTests import qualified Futhark.Optimise.AlgSimplifyTests import qualified Futhark.Pkg.SolveTests+import qualified Futhark.Representation.PrimitiveTests+import qualified Futhark.Analysis.ScalExpTests import Test.Tasty @@ -16,6 +19,9 @@ , Futhark.Optimise.AlgSimplifyTests.tests , Futhark.Representation.AST.Syntax.CoreTests.tests , Futhark.Pkg.SolveTests.tests+ , Futhark.Representation.ExplicitMemory.IndexFunctionTests.tests+ , Futhark.Representation.PrimitiveTests.tests+ , Futhark.Analysis.ScalExpTests.tests ] main :: IO ()